Commit 9b646972 authored by Tejun Heo's avatar Tejun Heo

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: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarSteve French <sfrench@samba.org>
Cc: Anton Blanchard <anton@samba.org>
parent d098adfb
...@@ -2,7 +2,6 @@ config CIFS ...@@ -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
......
...@@ -917,15 +917,10 @@ init_cifs(void) ...@@ -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
......
...@@ -19,7 +19,7 @@ ...@@ -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 { ...@@ -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 */ ...@@ -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);
...@@ -162,7 +162,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, ...@@ -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);
......
...@@ -2295,8 +2295,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping, ...@@ -2295,8 +2295,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
return rc; return rc;
} }
static void void cifs_oplock_break(struct work_struct *work)
cifs_oplock_break(struct slow_work *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) ...@@ -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 void cifs_oplock_break_get(struct cifsFileInfo *cfile)
cifs_oplock_break_get(struct slow_work *work)
{ {
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 void cifs_oplock_break_put(struct cifsFileInfo *cfile)
cifs_oplock_break_put(struct slow_work *work)
{ {
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,
......
...@@ -498,7 +498,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -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) ...@@ -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 " * cifs_oplock_break_put() can't be called
"break: %d\n", rc); * from here. Get reference after queueing
} else { * 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; 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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment