Home Home > GIT Browse > SLE12-SP3-AZURE
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAurelien Aptel <aaptel@suse.com>2019-05-14 16:37:55 +0200
committerAurelien Aptel <aaptel@suse.com>2019-05-14 16:37:55 +0200
commit7eeab982e09eb92aaedcbc22db1d0e969bcafea0 (patch)
tree4109f0eaf272aeb9334a648375102a2e3d5b92bd
parent62fcebb54e543597c4a3e1373e0bfc21aba4361e (diff)
CIFS: keep FileInfo handle live during oplock break
(bsc#1106284, bsc#1131565).
-rw-r--r--patches.fixes/CIFS-keep-FileInfo-handle-live-during-oplock-break.patch176
-rw-r--r--series.conf1
2 files changed, 177 insertions, 0 deletions
diff --git a/patches.fixes/CIFS-keep-FileInfo-handle-live-during-oplock-break.patch b/patches.fixes/CIFS-keep-FileInfo-handle-live-during-oplock-break.patch
new file mode 100644
index 0000000000..627a16cce1
--- /dev/null
+++ b/patches.fixes/CIFS-keep-FileInfo-handle-live-during-oplock-break.patch
@@ -0,0 +1,176 @@
+From: Aurelien Aptel <aaptel@suse.com>
+Date: Fri, 29 Mar 2019 10:49:12 +0100
+Subject: [PATCH] CIFS: keep FileInfo handle live during oplock break
+Git-commit: b98749cac4a695f084a5ff076f4510b23e353ecd
+References: bsc#1106284, bsc#1131565
+Patch-mainline: v5.1-rc6
+
+
+In the oplock break handler, writing pending changes from pages puts
+the FileInfo handle. If the refcount reaches zero it closes the handle
+and waits for any oplock break handler to return, thus causing a deadlock.
+
+To prevent this situation:
+
+* We add a wait flag to cifsFileInfo_put() to decide whether we should
+ wait for running/pending oplock break handlers
+
+* We keep an additionnal reference of the SMB FileInfo handle so that
+ for the rest of the handler putting the handle won't close it.
+ - The ref is bumped everytime we queue the handler via the
+ cifs_queue_oplock_break() helper.
+ - The ref is decremented at the end of the handler
+
+This bug was triggered by xfstest 464.
+
+Also important fix to address the various reports of
+oops in smb2_push_mandatory_locks
+
+Signed-off-by: Aurelien Aptel <aaptel@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
+Cc: Stable <stable@vger.kernel.org>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 2 ++
+ fs/cifs/file.c | 30 +++++++++++++++++++++++++-----
+ fs/cifs/misc.c | 25 +++++++++++++++++++++++--
+ fs/cifs/smb2misc.c | 4 ++--
+ 4 files changed, 52 insertions(+), 9 deletions(-)
+
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -1149,6 +1149,7 @@ cifsFileInfo_get_locked(struct cifsFileI
+ }
+
+ struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
++void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr);
+ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
+
+ #define CIFS_CACHE_READ_FLG 1
+@@ -1654,6 +1655,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
+ #endif /* CONFIG_CIFS_ACL */
+
+ void cifs_oplock_break(struct work_struct *work);
++void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
+
+ extern const struct slow_work_ops cifs_oplock_break_ops;
+ extern struct workqueue_struct *cifsiod_wq;
+--- a/fs/cifs/file.c
++++ b/fs/cifs/file.c
+@@ -358,13 +358,31 @@ cifsFileInfo_get(struct cifsFileInfo *ci
+ return cifs_file;
+ }
+
+-/*
+- * Release a reference on the file private data. This may involve closing
+- * the filehandle out on the server. Must be called without holding
+- * tcon->open_file_lock and cifs_file->file_info_lock.
++/**
++ * cifsFileInfo_put - release a reference of file priv data
++ *
++ * Always potentially wait for oplock handler. See _cifsFileInfo_put().
+ */
+ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
+ {
++ _cifsFileInfo_put(cifs_file, true);
++}
++
++/**
++ * _cifsFileInfo_put - release a reference of file priv data
++ *
++ * This may involve closing the filehandle @cifs_file out on the
++ * server. Must be called without holding tcon->open_file_lock and
++ * cifs_file->file_info_lock.
++ *
++ * If @wait_for_oplock_handler is true and we are releasing the last
++ * reference, wait for any running oplock break handler of the file
++ * and cancel any pending one. If calling this function from the
++ * oplock break handler, you need to pass false.
++ *
++ */
++void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
++{
+ struct inode *inode = d_inode(cifs_file->dentry);
+ struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+@@ -411,7 +429,8 @@ void cifsFileInfo_put(struct cifsFileInf
+
+ spin_unlock(&tcon->open_file_lock);
+
+- oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
++ oplock_break_cancelled = wait_oplock_handler ?
++ cancel_work_sync(&cifs_file->oplock_break) : false;
+
+ if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
+ struct TCP_Server_Info *server = tcon->ses->server;
+@@ -3898,6 +3917,7 @@ void cifs_oplock_break(struct work_struc
+ cinode);
+ cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
+ }
++ _cifsFileInfo_put(cfile, false /* do not wait for ourself */);
+ cifs_done_oplock_break(cinode);
+ }
+
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -494,8 +494,7 @@ is_valid_oplock_break(char *buffer, stru
+ CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
+ &pCifsInode->flags);
+
+- queue_work(cifsiod_wq,
+- &netfile->oplock_break);
++ cifs_queue_oplock_break(netfile);
+ netfile->oplock_break_cancelled = false;
+
+ spin_unlock(&tcon->open_file_lock);
+@@ -592,6 +591,28 @@ void cifs_put_writer(struct cifsInodeInf
+ spin_unlock(&cinode->writers_lock);
+ }
+
++/**
++ * cifs_queue_oplock_break - queue the oplock break handler for cfile
++ *
++ * This function is called from the demultiplex thread when it
++ * receives an oplock break for @cfile.
++ *
++ * Assumes the tcon->open_file_lock is held.
++ * Assumes cfile->file_info_lock is NOT held.
++ */
++void cifs_queue_oplock_break(struct cifsFileInfo *cfile)
++{
++ /*
++ * Bump the handle refcount now while we hold the
++ * open_file_lock to enforce the validity of it for the oplock
++ * break handler. The matching put is done at the end of the
++ * handler.
++ */
++ cifsFileInfo_get(cfile);
++
++ queue_work(cifsiod_wq, &cfile->oplock_break);
++}
++
+ void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
+ {
+ clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -506,7 +506,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tc
+ else
+ cfile->oplock_break_cancelled = true;
+
+- queue_work(cifsiod_wq, &cfile->oplock_break);
++ cifs_queue_oplock_break(cfile);
+ kfree(lw);
+ return true;
+ }
+@@ -650,7 +650,7 @@ smb2_is_valid_oplock_break(char *buffer,
+ CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
+ &cinode->flags);
+ spin_unlock(&cfile->file_info_lock);
+- queue_work(cifsiod_wq, &cfile->oplock_break);
++ cifs_queue_oplock_break(cfile);
+
+ spin_unlock(&tcon->open_file_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
diff --git a/series.conf b/series.conf
index 09f457622f..681a4ad068 100644
--- a/series.conf
+++ b/series.conf
@@ -6388,6 +6388,7 @@
patches.arch/powerpc-pseries-energy-Use-OF-accessor-functions-to-.patch
patches.drivers/vfio-type1-limit-dma-mappings-per-container
patches.arch/powerpc-vdso32-fix-CLOCK_MONOTONIC-on-PPC64.patch
+ patches.fixes/CIFS-keep-FileInfo-handle-live-during-oplock-break.patch
# out-of-tree patches
patches.suse/0001-USB-check-usb_get_extra_descriptor-for-proper-size-F.patch