Browse Source

cifs: use workqueue instead of slow-work

Workqueue can now handle high concurrency.  Use system_nrt_wq
instead of slow-work.

* Updated is_valid_oplock_break() to not call cifs_oplock_break_put()
  as advised by Steve French.  It might cause deadlock.  Instead,
  reference is increased after queueing succeeded and
  cifs_oplock_break() briefly grabs GlobalSMBSeslock before putting
  the cfile to make sure it doesn't put before the matching get is
  finished.

* Anton Blanchard reported that cifs conversion was using now gone
  system_single_wq.  Use system_nrt_wq which provides non-reentrance
  guarantee which is enough and much better.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Steve French <sfrench@samba.org>
Cc: Anton Blanchard <anton@samba.org>
Tejun Heo 15 years ago
parent
commit
9b64697246
6 changed files with 31 additions and 35 deletions
  1. 0 1
      fs/cifs/Kconfig
  2. 0 5
      fs/cifs/cifsfs.c
  3. 5 3
      fs/cifs/cifsglob.h
  4. 1 1
      fs/cifs/dir.c
  5. 13 17
      fs/cifs/file.c
  6. 12 8
      fs/cifs/misc.c

+ 0 - 1
fs/cifs/Kconfig

@@ -2,7 +2,6 @@ config CIFS
 	tristate "CIFS support (advanced network filesystem, SMBFS successor)"
 	tristate "CIFS support (advanced network filesystem, SMBFS successor)"
 	depends on INET
 	depends on INET
 	select NLS
 	select NLS
-	select SLOW_WORK
 	help
 	help
 	  This is the client VFS module for the Common Internet File System
 	  This is the client VFS module for the Common Internet File System
 	  (CIFS) protocol which is the successor to the Server Message Block
 	  (CIFS) protocol which is the successor to the Server Message Block

+ 0 - 5
fs/cifs/cifsfs.c

@@ -917,15 +917,10 @@ init_cifs(void)
 	if (rc)
 	if (rc)
 		goto out_unregister_key_type;
 		goto out_unregister_key_type;
 #endif
 #endif
-	rc = slow_work_register_user(THIS_MODULE);
-	if (rc)
-		goto out_unregister_resolver_key;
 
 
 	return 0;
 	return 0;
 
 
- out_unregister_resolver_key:
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #ifdef CONFIG_CIFS_DFS_UPCALL
-	unregister_key_type(&key_type_dns_resolver);
  out_unregister_key_type:
  out_unregister_key_type:
 #endif
 #endif
 #ifdef CONFIG_CIFS_UPCALL
 #ifdef CONFIG_CIFS_UPCALL

+ 5 - 3
fs/cifs/cifsglob.h

@@ -19,7 +19,7 @@
 #include <linux/in.h>
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <linux/in6.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/slow-work.h>
+#include <linux/workqueue.h>
 #include "cifs_fs_sb.h"
 #include "cifs_fs_sb.h"
 #include "cifsacl.h"
 #include "cifsacl.h"
 /*
 /*
@@ -363,7 +363,7 @@ struct cifsFileInfo {
 	atomic_t count;		/* reference count */
 	atomic_t count;		/* reference count */
 	struct mutex fh_mutex; /* prevents reopen race after dead ses*/
 	struct mutex fh_mutex; /* prevents reopen race after dead ses*/
 	struct cifs_search_info srch_inf;
 	struct cifs_search_info srch_inf;
-	struct slow_work oplock_break; /* slow_work job for oplock breaks */
+	struct work_struct oplock_break; /* work for oplock breaks */
 };
 };
 
 
 /* Take a reference on the file private data */
 /* Take a reference on the file private data */
@@ -732,4 +732,6 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv;    /* min size of big ntwrk buf pool */
 GLOBAL_EXTERN unsigned int cifs_min_small;  /* min size of small buf pool */
 GLOBAL_EXTERN unsigned int cifs_min_small;  /* min size of small buf pool */
 GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
 GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
 
 
-extern const struct slow_work_ops cifs_oplock_break_ops;
+void cifs_oplock_break(struct work_struct *work);
+void cifs_oplock_break_get(struct cifsFileInfo *cfile);
+void cifs_oplock_break_put(struct cifsFileInfo *cfile);

+ 1 - 1
fs/cifs/dir.c

@@ -162,7 +162,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
 	mutex_init(&pCifsFile->lock_mutex);
 	mutex_init(&pCifsFile->lock_mutex);
 	INIT_LIST_HEAD(&pCifsFile->llist);
 	INIT_LIST_HEAD(&pCifsFile->llist);
 	atomic_set(&pCifsFile->count, 1);
 	atomic_set(&pCifsFile->count, 1);
-	slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
+	INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
 
 
 	write_lock(&GlobalSMBSeslock);
 	write_lock(&GlobalSMBSeslock);
 	list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
 	list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);

+ 13 - 17
fs/cifs/file.c

@@ -2295,8 +2295,7 @@ out:
 	return rc;
 	return rc;
 }
 }
 
 
-static void
-cifs_oplock_break(struct slow_work *work)
+void cifs_oplock_break(struct work_struct *work)
 {
 {
 	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
 	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
 						  oplock_break);
 						  oplock_break);
@@ -2333,33 +2332,30 @@ cifs_oplock_break(struct slow_work *work)
 				 LOCKING_ANDX_OPLOCK_RELEASE, false);
 				 LOCKING_ANDX_OPLOCK_RELEASE, false);
 		cFYI(1, "Oplock release rc = %d", rc);
 		cFYI(1, "Oplock release rc = %d", rc);
 	}
 	}
+
+	/*
+	 * We might have kicked in before is_valid_oplock_break()
+	 * finished grabbing reference for us.  Make sure it's done by
+	 * waiting for GlobalSMSSeslock.
+	 */
+	write_lock(&GlobalSMBSeslock);
+	write_unlock(&GlobalSMBSeslock);
+
+	cifs_oplock_break_put(cfile);
 }
 }
 
 
-static int
-cifs_oplock_break_get(struct slow_work *work)
+void cifs_oplock_break_get(struct cifsFileInfo *cfile)
 {
 {
-	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
-						  oplock_break);
 	mntget(cfile->mnt);
 	mntget(cfile->mnt);
 	cifsFileInfo_get(cfile);
 	cifsFileInfo_get(cfile);
-	return 0;
 }
 }
 
 
-static void
-cifs_oplock_break_put(struct slow_work *work)
+void cifs_oplock_break_put(struct cifsFileInfo *cfile)
 {
 {
-	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
-						  oplock_break);
 	mntput(cfile->mnt);
 	mntput(cfile->mnt);
 	cifsFileInfo_put(cfile);
 	cifsFileInfo_put(cfile);
 }
 }
 
 
-const struct slow_work_ops cifs_oplock_break_ops = {
-	.get_ref	= cifs_oplock_break_get,
-	.put_ref	= cifs_oplock_break_put,
-	.execute	= cifs_oplock_break,
-};
-
 const struct address_space_operations cifs_addr_ops = {
 const struct address_space_operations cifs_addr_ops = {
 	.readpage = cifs_readpage,
 	.readpage = cifs_readpage,
 	.readpages = cifs_readpages,
 	.readpages = cifs_readpages,

+ 12 - 8
fs/cifs/misc.c

@@ -498,7 +498,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 	struct cifsTconInfo *tcon;
 	struct cifsTconInfo *tcon;
 	struct cifsInodeInfo *pCifsInode;
 	struct cifsInodeInfo *pCifsInode;
 	struct cifsFileInfo *netfile;
 	struct cifsFileInfo *netfile;
-	int rc;
 
 
 	cFYI(1, "Checking for oplock break or dnotify response");
 	cFYI(1, "Checking for oplock break or dnotify response");
 	if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
 	if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
@@ -583,13 +582,18 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 				pCifsInode->clientCanCacheAll = false;
 				pCifsInode->clientCanCacheAll = false;
 				if (pSMB->OplockLevel == 0)
 				if (pSMB->OplockLevel == 0)
 					pCifsInode->clientCanCacheRead = false;
 					pCifsInode->clientCanCacheRead = false;
-				rc = slow_work_enqueue(&netfile->oplock_break);
-				if (rc) {
-					cERROR(1, "failed to enqueue oplock "
-						   "break: %d\n", rc);
-				} else {
-					netfile->oplock_break_cancelled = false;
-				}
+
+				/*
+				 * cifs_oplock_break_put() can't be called
+				 * from here.  Get reference after queueing
+				 * succeeded.  cifs_oplock_break() will
+				 * synchronize using GlobalSMSSeslock.
+				 */
+				if (queue_work(system_nrt_wq,
+					       &netfile->oplock_break))
+					cifs_oplock_break_get(netfile);
+				netfile->oplock_break_cancelled = false;
+
 				read_unlock(&GlobalSMBSeslock);
 				read_unlock(&GlobalSMBSeslock);
 				read_unlock(&cifs_tcp_ses_lock);
 				read_unlock(&cifs_tcp_ses_lock);
 				return true;
 				return true;