[PATCH 0/4] cifs: convert oplock breaks to use slow_work facility

View: New views
8 Messages — Rating Filter:   Alert me  

[PATCH 0/4] cifs: convert oplock breaks to use slow_work facility

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

This patchset converts the oplock breaks to use the new slow_work
facility that was added a couple of releases ago. In addition to fixing
some races in the current scheme, this has the advantage that oplock
breaks callbacks can potentially run in parallel.

This patchset is identical to the one I sent the other day plus the
patch to convert that to slow_work. The only difference is that I merged
4/4 and 5/4. I didn't see any reason to have a patch that converts the
code to workqueues only to re-convert it to slow_work.

This probably needs to go in ASAP since we have people hitting panics in
the current code.

Jeff Layton (4):
  cifs: remove cifsInodeInfo.oplockPending flag
  cifs: take read lock on GlobalSMBSes_lock in is_valid_oplock_break
  cifs: have cifsFileInfo hold an extra inode reference
  cifs: convert oplock breaks to use slow_work facility

 fs/cifs/Kconfig     |    1 +
 fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
 fs/cifs/cifsglob.h  |   17 ++++-----
 fs/cifs/cifsproto.h |    4 --
 fs/cifs/cifssmb.c   |    1 +
 fs/cifs/connect.c   |    1 -
 fs/cifs/dir.c       |    4 ++-
 fs/cifs/file.c      |   73 +++++++++++++++++++++++++++++++++++++--
 fs/cifs/misc.c      |   34 ++++++++++++------
 fs/cifs/transport.c |   50 ---------------------------
 10 files changed, 110 insertions(+), 170 deletions(-)

_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

[PATCH 1/4] cifs: remove cifsInodeInfo.oplockPending flag

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

It's set on oplock break but nothing ever looks at it.

Signed-off-by: Jeff Layton <jlayton@...>
---
 fs/cifs/cifsglob.h |    1 -
 fs/cifs/misc.c     |    1 -
 2 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6cfc81a..b8d673c 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -382,7 +382,6 @@ struct cifsInodeInfo {
  unsigned long time; /* jiffies of last update/check of inode */
  bool clientCanCacheRead:1; /* read oplock */
  bool clientCanCacheAll:1; /* read and writebehind oplock */
- bool oplockPending:1;
  bool delete_pending:1; /* DELETE_ON_CLOSE is set */
  u64  server_eof; /* current file size on server */
  u64  uniqueid; /* server inode number */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index e079a91..f2d508d 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -576,7 +576,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  pCifsInode->clientCanCacheAll = false;
  if (pSMB->OplockLevel == 0)
  pCifsInode->clientCanCacheRead = false;
- pCifsInode->oplockPending = true;
  AllocOplockQEntry(netfile->pInode,
   netfile->netfid, tcon);
  cFYI(1, ("about to wake up oplock thread"));
--
1.6.0.6

_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

[PATCH 2/4] cifs: take read lock on GlobalSMBSes_lock in is_valid_oplock_break

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

...rather than a write lock. It doesn't change the list so a read lock
should be sufficient.

Signed-off-by: Jeff Layton <jlayton@...>
---
 fs/cifs/misc.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index f2d508d..191e622 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -562,14 +562,14 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  continue;
 
  cifs_stats_inc(&tcon->num_oplock_brks);
- write_lock(&GlobalSMBSeslock);
+ read_lock(&GlobalSMBSeslock);
  list_for_each(tmp2, &tcon->openFileList) {
  netfile = list_entry(tmp2, struct cifsFileInfo,
      tlist);
  if (pSMB->Fid != netfile->netfid)
  continue;
 
- write_unlock(&GlobalSMBSeslock);
+ read_unlock(&GlobalSMBSeslock);
  read_unlock(&cifs_tcp_ses_lock);
  cFYI(1, ("file id match, oplock break"));
  pCifsInode = CIFS_I(netfile->pInode);
@@ -584,7 +584,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 
  return true;
  }
- write_unlock(&GlobalSMBSeslock);
+ read_unlock(&GlobalSMBSeslock);
  read_unlock(&cifs_tcp_ses_lock);
  cFYI(1, ("No matching file for oplock break"));
  return true;
--
1.6.0.6

_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

[PATCH 3/4] cifs: have cifsFileInfo hold an extra inode reference

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

It's possible that this struct will outlive the filp to which it is
attached. If it does and it needs to do some work on the inode, then
it'll need a reference.

Signed-off-by: Jeff Layton <jlayton@...>
---
 fs/cifs/cifsglob.h |    4 +++-
 fs/cifs/dir.c      |    2 +-
 fs/cifs/file.c     |    2 +-
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index b8d673c..908b2de 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -365,8 +365,10 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
 /* Release a reference on the file private data */
 static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
 {
- if (atomic_dec_and_test(&cifs_file->count))
+ if (atomic_dec_and_test(&cifs_file->count)) {
+ iput(cifs_file->pInode);
  kfree(cifs_file);
+ }
 }
 
 /*
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index a6424cf..6b45feb 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -147,7 +147,7 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
 
  pCifsFile->netfid = fileHandle;
  pCifsFile->pid = current->tgid;
- pCifsFile->pInode = newinode;
+ pCifsFile->pInode = igrab(newinode);
  pCifsFile->invalidHandle = false;
  pCifsFile->closePend = false;
  mutex_init(&pCifsFile->fh_mutex);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index fa7beac..9498d2c 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -50,7 +50,7 @@ static inline struct cifsFileInfo *cifs_init_private(
  mutex_init(&private_data->lock_mutex);
  INIT_LIST_HEAD(&private_data->llist);
  private_data->pfile = file; /* needed for writepage */
- private_data->pInode = inode;
+ private_data->pInode = igrab(inode);
  private_data->invalidHandle = false;
  private_data->closePend = false;
  /* Initialize reference count to one.  The private data is
--
1.6.0.6

_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

[PATCH 4/4] cifs: convert oplock breaks to use slow_work facility

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Currently, when an oplock break comes in there's a chance that the
oplock break job won't occur if the allocation of the oplock_q_entry
fails. There are also some rather nasty races in the allocation and
handling these structs.

Rather than allocating oplock queue entries when an oplock break comes
in, add a few extra fields to the cifsFileInfo struct. Get rid of the
dedicated cifs_oplock_thread as well and queue the oplock break job
to the slow_work thread pool.

This approach also has the advantage that the oplock break jobs can
potentially run in parallel rather than be serialized like they are
today.

Signed-off-by: Jeff Layton <jlayton@...>
---
 fs/cifs/Kconfig     |    1 +
 fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
 fs/cifs/cifsglob.h  |   12 +++----
 fs/cifs/cifsproto.h |    4 --
 fs/cifs/cifssmb.c   |    1 +
 fs/cifs/connect.c   |    1 -
 fs/cifs/dir.c       |    2 +
 fs/cifs/file.c      |   71 +++++++++++++++++++++++++++++++++++++-
 fs/cifs/misc.c      |   29 +++++++++++-----
 fs/cifs/transport.c |   50 ---------------------------
 10 files changed, 103 insertions(+), 163 deletions(-)

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 6994a0f..80f3525 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -2,6 +2,7 @@ config CIFS
  tristate "CIFS support (advanced network filesystem, SMBFS successor)"
  depends on INET
  select NLS
+ select SLOW_WORK
  help
   This is the client VFS module for the Common Internet File System
   (CIFS) protocol which is the successor to the Server Message Block
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 3610e99..89142b3 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -64,9 +64,6 @@ unsigned int multiuser_mount = 0;
 unsigned int extended_security = CIFSSEC_DEF;
 /* unsigned int ntlmv2_support = 0; */
 unsigned int sign_CIFS_PDUs = 1;
-extern struct task_struct *oplockThread; /* remove sparse warning */
-struct task_struct *oplockThread = NULL;
-/* extern struct task_struct * dnotifyThread; remove sparse warning */
 static const struct super_operations cifs_super_ops;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
@@ -973,89 +970,12 @@ cifs_destroy_mids(void)
  kmem_cache_destroy(cifs_oplock_cachep);
 }
 
-static int cifs_oplock_thread(void *dummyarg)
-{
- struct oplock_q_entry *oplock_item;
- struct cifsTconInfo *pTcon;
- struct inode *inode;
- __u16  netfid;
- int rc, waitrc = 0;
-
- set_freezable();
- do {
- if (try_to_freeze())
- continue;
-
- spin_lock(&cifs_oplock_lock);
- if (list_empty(&cifs_oplock_list)) {
- spin_unlock(&cifs_oplock_lock);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(39*HZ);
- } else {
- oplock_item = list_entry(cifs_oplock_list.next,
- struct oplock_q_entry, qhead);
- cFYI(1, ("found oplock item to write out"));
- pTcon = oplock_item->tcon;
- inode = oplock_item->pinode;
- netfid = oplock_item->netfid;
- spin_unlock(&cifs_oplock_lock);
- DeleteOplockQEntry(oplock_item);
- /* can not grab inode sem here since it would
- deadlock when oplock received on delete
- since vfs_unlink holds the i_mutex across
- the call */
- /* mutex_lock(&inode->i_mutex);*/
- if (S_ISREG(inode->i_mode)) {
-#ifdef CONFIG_CIFS_EXPERIMENTAL
- if (CIFS_I(inode)->clientCanCacheAll == 0)
- break_lease(inode, FMODE_READ);
- else if (CIFS_I(inode)->clientCanCacheRead == 0)
- break_lease(inode, FMODE_WRITE);
-#endif
- rc = filemap_fdatawrite(inode->i_mapping);
- if (CIFS_I(inode)->clientCanCacheRead == 0) {
- waitrc = filemap_fdatawait(
-      inode->i_mapping);
- invalidate_remote_inode(inode);
- }
- if (rc == 0)
- rc = waitrc;
- } else
- rc = 0;
- /* mutex_unlock(&inode->i_mutex);*/
- if (rc)
- CIFS_I(inode)->write_behind_rc = rc;
- cFYI(1, ("Oplock flush inode %p rc %d",
- inode, rc));
-
- /* releasing stale oplock after recent reconnect
- of smb session using a now incorrect file
- handle is not a data integrity issue but do
- not bother sending an oplock release if session
- to server still is disconnected since oplock
- already released by the server in that case */
- if (!pTcon->need_reconnect) {
- rc = CIFSSMBLock(0, pTcon, netfid,
- 0 /* len */ , 0 /* offset */, 0,
- 0, LOCKING_ANDX_OPLOCK_RELEASE,
- false /* wait flag */);
- cFYI(1, ("Oplock release rc = %d", rc));
- }
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);  /* yield in case q were corrupt */
- }
- } while (!kthread_should_stop());
-
- return 0;
-}
-
 static int __init
 init_cifs(void)
 {
  int rc = 0;
  cifs_proc_init();
  INIT_LIST_HEAD(&cifs_tcp_ses_list);
- INIT_LIST_HEAD(&cifs_oplock_list);
 #ifdef CONFIG_CIFS_EXPERIMENTAL
  INIT_LIST_HEAD(&GlobalDnotifyReqList);
  INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
@@ -1084,7 +1004,6 @@ init_cifs(void)
  rwlock_init(&GlobalSMBSeslock);
  rwlock_init(&cifs_tcp_ses_lock);
  spin_lock_init(&GlobalMid_Lock);
- spin_lock_init(&cifs_oplock_lock);
 
  if (cifs_max_pending < 2) {
  cifs_max_pending = 2;
@@ -1119,18 +1038,15 @@ init_cifs(void)
  if (rc)
  goto out_unregister_key_type;
 #endif
- oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd");
- if (IS_ERR(oplockThread)) {
- rc = PTR_ERR(oplockThread);
- cERROR(1, ("error %d create oplock thread", rc));
- goto out_unregister_dfs_key_type;
- }
+ rc = slow_work_register_user();
+ if (rc)
+ goto out_unregister_resolver_key;
 
  return 0;
 
- out_unregister_dfs_key_type:
-#ifdef CONFIG_CIFS_DFS_UPCALL
+ out_unregister_resolver_key:
  unregister_key_type(&key_type_dns_resolver);
+#ifdef CONFIG_CIFS_DFS_UPCALL
  out_unregister_key_type:
 #endif
 #ifdef CONFIG_CIFS_UPCALL
@@ -1165,7 +1081,6 @@ exit_cifs(void)
  cifs_destroy_inodecache();
  cifs_destroy_mids();
  cifs_destroy_request_bufs();
- kthread_stop(oplockThread);
 }
 
 MODULE_AUTHOR("Steve French <sfrench@...>");
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 908b2de..c3280fa 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -18,6 +18,7 @@
  */
 #include <linux/in.h>
 #include <linux/in6.h>
+#include <linux/slow-work.h>
 #include "cifs_fs_sb.h"
 #include "cifsacl.h"
 /*
@@ -346,14 +347,16 @@ struct cifsFileInfo {
  /* lock scope id (0 if none) */
  struct file *pfile; /* needed for writepage */
  struct inode *pInode; /* needed for oplock break */
+ struct cifsTconInfo *tcon;
  struct mutex lock_mutex;
  struct list_head llist; /* list of byte range locks we have. */
  bool closePend:1; /* file is marked to close */
  bool invalidHandle:1; /* file closed via session abend */
- bool messageMode:1; /* for pipes: message vs byte mode */
+ bool oplock_break_cancelled:1;
  atomic_t count; /* reference count */
  struct mutex fh_mutex; /* prevents reopen race after dead ses*/
  struct cifs_search_info srch_inf;
+ struct slow_work oplock_break; /* slow_work job for oplock breaks */
 };
 
 /* Take a reference on the file private data */
@@ -670,12 +673,6 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
  */
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;
 
-/* Global list of oplocks */
-GLOBAL_EXTERN struct list_head cifs_oplock_list;
-
-/* Protects the cifs_oplock_list */
-GLOBAL_EXTERN spinlock_t cifs_oplock_lock;
-
 /* Outstanding dir notify requests */
 GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
 /* DirNotify response queue */
@@ -726,3 +723,4 @@ 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_max_pending; /* MAX requests at once to server*/
 
+extern const struct slow_work_ops cifs_oplock_break_ops;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index da8fbf5..f2a6241 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -86,10 +86,6 @@ extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
      const int stage,
      const struct nls_table *nls_cp);
 extern __u16 GetNextMid(struct TCP_Server_Info *server);
-extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16,
- struct cifsTconInfo *);
-extern void DeleteOplockQEntry(struct oplock_q_entry *);
-extern void DeleteTconOplockQEntries(struct cifsTconInfo *);
 extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
 extern u64 cifs_UnixTimeToNT(struct timespec);
 extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 301e307..941441d 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -94,6 +94,7 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
  list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
  open_file = list_entry(tmp, struct cifsFileInfo, tlist);
  open_file->invalidHandle = true;
+ open_file->oplock_break_cancelled = true;
  }
  write_unlock(&GlobalSMBSeslock);
  /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 9c91c8b..e8b72b8 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1664,7 +1664,6 @@ cifs_put_tcon(struct cifsTconInfo *tcon)
  CIFSSMBTDis(xid, tcon);
  _FreeXid(xid);
 
- DeleteTconOplockQEntries(tcon);
  tconInfoFree(tcon);
  cifs_put_smb_ses(ses);
 }
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 6b45feb..95c2931 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -148,12 +148,14 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
  pCifsFile->netfid = fileHandle;
  pCifsFile->pid = current->tgid;
  pCifsFile->pInode = igrab(newinode);
+ pCifsFile->tcon = tcon;
  pCifsFile->invalidHandle = false;
  pCifsFile->closePend = false;
  mutex_init(&pCifsFile->fh_mutex);
  mutex_init(&pCifsFile->lock_mutex);
  INIT_LIST_HEAD(&pCifsFile->llist);
  atomic_set(&pCifsFile->count, 1);
+ slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
 
  /* set the following in open now
  pCifsFile->pfile = file; */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 9498d2c..04aab66 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -41,7 +41,7 @@
 
 static inline struct cifsFileInfo *cifs_init_private(
  struct cifsFileInfo *private_data, struct inode *inode,
- struct file *file, __u16 netfid)
+ struct cifsTconInfo *tcon, struct file *file, __u16 netfid)
 {
  memset(private_data, 0, sizeof(struct cifsFileInfo));
  private_data->netfid = netfid;
@@ -51,11 +51,13 @@ static inline struct cifsFileInfo *cifs_init_private(
  INIT_LIST_HEAD(&private_data->llist);
  private_data->pfile = file; /* needed for writepage */
  private_data->pInode = igrab(inode);
+ private_data->tcon = tcon;
  private_data->invalidHandle = false;
  private_data->closePend = false;
  /* Initialize reference count to one.  The private data is
  freed on the release of the last reference */
  atomic_set(&private_data->count, 1);
+ slow_work_init(&private_data->oplock_break, &cifs_oplock_break_ops);
 
  return private_data;
 }
@@ -420,7 +422,8 @@ int cifs_open(struct inode *inode, struct file *file)
  rc = -ENOMEM;
  goto out;
  }
- pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
+ pCifsFile = cifs_init_private(file->private_data, inode, tcon, file,
+ netfid);
  write_lock(&GlobalSMBSeslock);
  list_add(&pCifsFile->tlist, &tcon->openFileList);
 
@@ -2308,6 +2311,70 @@ out:
  return rc;
 }
 
+static void
+cifs_oplock_break(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ struct inode *inode = cfile->pInode;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ int rc, waitrc = 0;
+
+ if (inode && S_ISREG(inode->i_mode)) {
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cinode->clientCanCacheAll == 0)
+ break_lease(inode, FMODE_READ);
+ else if (cinode->clientCanCacheRead == 0)
+ break_lease(inode, FMODE_WRITE);
+#endif
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (cinode->clientCanCacheRead == 0) {
+ waitrc = filemap_fdatawait(inode->i_mapping);
+ invalidate_remote_inode(inode);
+ }
+ if (!rc)
+ rc = waitrc;
+ if (rc)
+ cinode->write_behind_rc = rc;
+ cFYI(1, ("Oplock flush inode %p rc %d", inode, rc));
+ }
+
+ /*
+ * releasing stale oplock after recent reconnect of smb session using
+ * a now incorrect file handle is not a data integrity issue but do
+ * not bother sending an oplock release if session to server still is
+ * disconnected since oplock already released by the server
+ */
+ if (!cfile->closePend && !cfile->oplock_break_cancelled) {
+ rc = CIFSSMBLock(0, cfile->tcon, cfile->netfid, 0, 0, 0, 0,
+ LOCKING_ANDX_OPLOCK_RELEASE, false);
+ cFYI(1, ("Oplock release rc = %d", rc));
+ }
+}
+
+static int
+cifs_oplock_break_get(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ cifsFileInfo_get(cfile);
+ return 0;
+}
+
+static void
+cifs_oplock_break_put(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ 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 = {
  .readpage = cifs_readpage,
  .readpages = cifs_readpages,
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 191e622..0241b25 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -32,7 +32,6 @@
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
-extern struct task_struct *oplockThread;
 
 /* The xid serves as a useful identifier for each incoming vfs request,
    in a similar way to the mid which is useful to track each sent smb,
@@ -500,6 +499,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  struct cifsTconInfo *tcon;
  struct cifsInodeInfo *pCifsInode;
  struct cifsFileInfo *netfile;
+ int rc;
 
  cFYI(1, ("Checking for oplock break or dnotify response"));
  if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
@@ -569,19 +569,30 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  if (pSMB->Fid != netfile->netfid)
  continue;
 
- read_unlock(&GlobalSMBSeslock);
- read_unlock(&cifs_tcp_ses_lock);
+ /*
+ * don't do anything if file is about to be
+ * closed anyway.
+ */
+ if (netfile->closePend) {
+ read_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+
  cFYI(1, ("file id match, oplock break"));
  pCifsInode = CIFS_I(netfile->pInode);
  pCifsInode->clientCanCacheAll = false;
  if (pSMB->OplockLevel == 0)
  pCifsInode->clientCanCacheRead = false;
- AllocOplockQEntry(netfile->pInode,
-  netfile->netfid, tcon);
- cFYI(1, ("about to wake up oplock thread"));
- if (oplockThread)
- wake_up_process(oplockThread);
-
+ 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;
+ }
+ read_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
  return true;
  }
  read_unlock(&GlobalSMBSeslock);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 1da4ab2..07b8e71 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -103,56 +103,6 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
  mempool_free(midEntry, cifs_mid_poolp);
 }
 
-struct oplock_q_entry *
-AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon)
-{
- struct oplock_q_entry *temp;
- if ((pinode == NULL) || (tcon == NULL)) {
- cERROR(1, ("Null parms passed to AllocOplockQEntry"));
- return NULL;
- }
- temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
-       GFP_KERNEL);
- if (temp == NULL)
- return temp;
- else {
- temp->pinode = pinode;
- temp->tcon = tcon;
- temp->netfid = fid;
- spin_lock(&cifs_oplock_lock);
- list_add_tail(&temp->qhead, &cifs_oplock_list);
- spin_unlock(&cifs_oplock_lock);
- }
- return temp;
-}
-
-void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry)
-{
- spin_lock(&cifs_oplock_lock);
-    /* should we check if list empty first? */
- list_del(&oplockEntry->qhead);
- spin_unlock(&cifs_oplock_lock);
- kmem_cache_free(cifs_oplock_cachep, oplockEntry);
-}
-
-
-void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
-{
- struct oplock_q_entry *temp;
-
- if (tcon == NULL)
- return;
-
- spin_lock(&cifs_oplock_lock);
- list_for_each_entry(temp, &cifs_oplock_list, qhead) {
- if ((temp->tcon) && (temp->tcon == tcon)) {
- list_del(&temp->qhead);
- kmem_cache_free(cifs_oplock_cachep, temp);
- }
- }
- spin_unlock(&cifs_oplock_lock);
-}
-
 static int
 smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
 {
--
1.6.0.6

_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

Re: [PATCH 0/4] cifs: convert oplock breaks to use slow_work facility

by Steve French-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

 No objections to this.   Let me recheck the obvious today and merge.

On Fri, Sep 11, 2009 at 7:14 AM, Jeff Layton <jlayton@...> wrote:

> This patchset converts the oplock breaks to use the new slow_work
> facility that was added a couple of releases ago. In addition to fixing
> some races in the current scheme, this has the advantage that oplock
> breaks callbacks can potentially run in parallel.
>
> This patchset is identical to the one I sent the other day plus the
> patch to convert that to slow_work. The only difference is that I merged
> 4/4 and 5/4. I didn't see any reason to have a patch that converts the
> code to workqueues only to re-convert it to slow_work.
>
> This probably needs to go in ASAP since we have people hitting panics in
> the current code.
>
> Jeff Layton (4):
>  cifs: remove cifsInodeInfo.oplockPending flag
>  cifs: take read lock on GlobalSMBSes_lock in is_valid_oplock_break
>  cifs: have cifsFileInfo hold an extra inode reference
>  cifs: convert oplock breaks to use slow_work facility
>
>  fs/cifs/Kconfig     |    1 +
>  fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
>  fs/cifs/cifsglob.h  |   17 ++++-----
>  fs/cifs/cifsproto.h |    4 --
>  fs/cifs/cifssmb.c   |    1 +
>  fs/cifs/connect.c   |    1 -
>  fs/cifs/dir.c       |    4 ++-
>  fs/cifs/file.c      |   73 +++++++++++++++++++++++++++++++++++++--
>  fs/cifs/misc.c      |   34 ++++++++++++------
>  fs/cifs/transport.c |   50 ---------------------------
>  10 files changed, 110 insertions(+), 170 deletions(-)
>
>



--
Thanks,

Steve
_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

Re: [PATCH 4/4] cifs: convert oplock breaks to use slow_work facility

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Fri, 11 Sep 2009 08:14:08 -0400
Jeff Layton <jlayton@...> wrote:

> Currently, when an oplock break comes in there's a chance that the
> oplock break job won't occur if the allocation of the oplock_q_entry
> fails. There are also some rather nasty races in the allocation and
> handling these structs.
>
> Rather than allocating oplock queue entries when an oplock break comes
> in, add a few extra fields to the cifsFileInfo struct. Get rid of the
> dedicated cifs_oplock_thread as well and queue the oplock break job
> to the slow_work thread pool.
>
> This approach also has the advantage that the oplock break jobs can
> potentially run in parallel rather than be serialized like they are
> today.
>
> Signed-off-by: Jeff Layton <jlayton@...>
> ---
>  fs/cifs/Kconfig     |    1 +
>  fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
>  fs/cifs/cifsglob.h  |   12 +++----
>  fs/cifs/cifsproto.h |    4 --
>  fs/cifs/cifssmb.c   |    1 +
>  fs/cifs/connect.c   |    1 -
>  fs/cifs/dir.c       |    2 +
>  fs/cifs/file.c      |   71 +++++++++++++++++++++++++++++++++++++-
>  fs/cifs/misc.c      |   29 +++++++++++-----
>  fs/cifs/transport.c |   50 ---------------------------
>  10 files changed, 103 insertions(+), 163 deletions(-)
>
Steve,

Attached is a new version of this patch. It's still very close to
the old version. The only difference is that it has the oplock break
job take and hold a reference to the tcon until it completes. With
the other patch, there was a small possibility that the tcon could
vanish while the oplock break job is running. This should fix by
ensuring that the tcon can't go away until the oplock break job
completes.

--
Jeff Layton <jlayton@...>


From 6cfc94a57260ae0ae13224301aa8303aa89a29d7 Mon Sep 17 00:00:00 2001
From: Jeff Layton <jlayton@...>
Date: Sat, 12 Sep 2009 07:51:53 -0400
Subject: [PATCH] cifs: convert oplock breaks to use slow_work facility

Currently, when an oplock break comes in there's a chance that the
oplock break job won't occur if the allocation of the oplock_q_entry
fails. There are also some rather nasty races in the allocation and
handling these structs.

Rather than allocating oplock queue entries when an oplock break comes
in, add a few extra fields to the cifsFileInfo struct. Get rid of the
dedicated cifs_oplock_thread as well and queue the oplock break job
to the slow_work thread pool.

This approach also has the advantage that the oplock break jobs can
potentially run in parallel rather than be serialized like they are
today.

Signed-off-by: Jeff Layton <jlayton@...>
---
 fs/cifs/Kconfig     |    1 +
 fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
 fs/cifs/cifsglob.h  |   12 +++----
 fs/cifs/cifsproto.h |    5 +--
 fs/cifs/cifssmb.c   |    1 +
 fs/cifs/connect.c   |    3 +-
 fs/cifs/dir.c       |    2 +
 fs/cifs/file.c      |   73 ++++++++++++++++++++++++++++++++++++++-
 fs/cifs/misc.c      |   29 +++++++++++-----
 fs/cifs/transport.c |   50 ---------------------------
 10 files changed, 107 insertions(+), 164 deletions(-)

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 6994a0f..80f3525 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -2,6 +2,7 @@ config CIFS
  tristate "CIFS support (advanced network filesystem, SMBFS successor)"
  depends on INET
  select NLS
+ select SLOW_WORK
  help
   This is the client VFS module for the Common Internet File System
   (CIFS) protocol which is the successor to the Server Message Block
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 3610e99..89142b3 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -64,9 +64,6 @@ unsigned int multiuser_mount = 0;
 unsigned int extended_security = CIFSSEC_DEF;
 /* unsigned int ntlmv2_support = 0; */
 unsigned int sign_CIFS_PDUs = 1;
-extern struct task_struct *oplockThread; /* remove sparse warning */
-struct task_struct *oplockThread = NULL;
-/* extern struct task_struct * dnotifyThread; remove sparse warning */
 static const struct super_operations cifs_super_ops;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
@@ -973,89 +970,12 @@ cifs_destroy_mids(void)
  kmem_cache_destroy(cifs_oplock_cachep);
 }
 
-static int cifs_oplock_thread(void *dummyarg)
-{
- struct oplock_q_entry *oplock_item;
- struct cifsTconInfo *pTcon;
- struct inode *inode;
- __u16  netfid;
- int rc, waitrc = 0;
-
- set_freezable();
- do {
- if (try_to_freeze())
- continue;
-
- spin_lock(&cifs_oplock_lock);
- if (list_empty(&cifs_oplock_list)) {
- spin_unlock(&cifs_oplock_lock);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(39*HZ);
- } else {
- oplock_item = list_entry(cifs_oplock_list.next,
- struct oplock_q_entry, qhead);
- cFYI(1, ("found oplock item to write out"));
- pTcon = oplock_item->tcon;
- inode = oplock_item->pinode;
- netfid = oplock_item->netfid;
- spin_unlock(&cifs_oplock_lock);
- DeleteOplockQEntry(oplock_item);
- /* can not grab inode sem here since it would
- deadlock when oplock received on delete
- since vfs_unlink holds the i_mutex across
- the call */
- /* mutex_lock(&inode->i_mutex);*/
- if (S_ISREG(inode->i_mode)) {
-#ifdef CONFIG_CIFS_EXPERIMENTAL
- if (CIFS_I(inode)->clientCanCacheAll == 0)
- break_lease(inode, FMODE_READ);
- else if (CIFS_I(inode)->clientCanCacheRead == 0)
- break_lease(inode, FMODE_WRITE);
-#endif
- rc = filemap_fdatawrite(inode->i_mapping);
- if (CIFS_I(inode)->clientCanCacheRead == 0) {
- waitrc = filemap_fdatawait(
-      inode->i_mapping);
- invalidate_remote_inode(inode);
- }
- if (rc == 0)
- rc = waitrc;
- } else
- rc = 0;
- /* mutex_unlock(&inode->i_mutex);*/
- if (rc)
- CIFS_I(inode)->write_behind_rc = rc;
- cFYI(1, ("Oplock flush inode %p rc %d",
- inode, rc));
-
- /* releasing stale oplock after recent reconnect
- of smb session using a now incorrect file
- handle is not a data integrity issue but do
- not bother sending an oplock release if session
- to server still is disconnected since oplock
- already released by the server in that case */
- if (!pTcon->need_reconnect) {
- rc = CIFSSMBLock(0, pTcon, netfid,
- 0 /* len */ , 0 /* offset */, 0,
- 0, LOCKING_ANDX_OPLOCK_RELEASE,
- false /* wait flag */);
- cFYI(1, ("Oplock release rc = %d", rc));
- }
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);  /* yield in case q were corrupt */
- }
- } while (!kthread_should_stop());
-
- return 0;
-}
-
 static int __init
 init_cifs(void)
 {
  int rc = 0;
  cifs_proc_init();
  INIT_LIST_HEAD(&cifs_tcp_ses_list);
- INIT_LIST_HEAD(&cifs_oplock_list);
 #ifdef CONFIG_CIFS_EXPERIMENTAL
  INIT_LIST_HEAD(&GlobalDnotifyReqList);
  INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
@@ -1084,7 +1004,6 @@ init_cifs(void)
  rwlock_init(&GlobalSMBSeslock);
  rwlock_init(&cifs_tcp_ses_lock);
  spin_lock_init(&GlobalMid_Lock);
- spin_lock_init(&cifs_oplock_lock);
 
  if (cifs_max_pending < 2) {
  cifs_max_pending = 2;
@@ -1119,18 +1038,15 @@ init_cifs(void)
  if (rc)
  goto out_unregister_key_type;
 #endif
- oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd");
- if (IS_ERR(oplockThread)) {
- rc = PTR_ERR(oplockThread);
- cERROR(1, ("error %d create oplock thread", rc));
- goto out_unregister_dfs_key_type;
- }
+ rc = slow_work_register_user();
+ if (rc)
+ goto out_unregister_resolver_key;
 
  return 0;
 
- out_unregister_dfs_key_type:
-#ifdef CONFIG_CIFS_DFS_UPCALL
+ out_unregister_resolver_key:
  unregister_key_type(&key_type_dns_resolver);
+#ifdef CONFIG_CIFS_DFS_UPCALL
  out_unregister_key_type:
 #endif
 #ifdef CONFIG_CIFS_UPCALL
@@ -1165,7 +1081,6 @@ exit_cifs(void)
  cifs_destroy_inodecache();
  cifs_destroy_mids();
  cifs_destroy_request_bufs();
- kthread_stop(oplockThread);
 }
 
 MODULE_AUTHOR("Steve French <sfrench@...>");
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 908b2de..c3280fa 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -18,6 +18,7 @@
  */
 #include <linux/in.h>
 #include <linux/in6.h>
+#include <linux/slow-work.h>
 #include "cifs_fs_sb.h"
 #include "cifsacl.h"
 /*
@@ -346,14 +347,16 @@ struct cifsFileInfo {
  /* lock scope id (0 if none) */
  struct file *pfile; /* needed for writepage */
  struct inode *pInode; /* needed for oplock break */
+ struct cifsTconInfo *tcon;
  struct mutex lock_mutex;
  struct list_head llist; /* list of byte range locks we have. */
  bool closePend:1; /* file is marked to close */
  bool invalidHandle:1; /* file closed via session abend */
- bool messageMode:1; /* for pipes: message vs byte mode */
+ bool oplock_break_cancelled:1;
  atomic_t count; /* reference count */
  struct mutex fh_mutex; /* prevents reopen race after dead ses*/
  struct cifs_search_info srch_inf;
+ struct slow_work oplock_break; /* slow_work job for oplock breaks */
 };
 
 /* Take a reference on the file private data */
@@ -670,12 +673,6 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
  */
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;
 
-/* Global list of oplocks */
-GLOBAL_EXTERN struct list_head cifs_oplock_list;
-
-/* Protects the cifs_oplock_list */
-GLOBAL_EXTERN spinlock_t cifs_oplock_lock;
-
 /* Outstanding dir notify requests */
 GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
 /* DirNotify response queue */
@@ -726,3 +723,4 @@ 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_max_pending; /* MAX requests at once to server*/
 
+extern const struct slow_work_ops cifs_oplock_break_ops;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index bf3ae88..9b682e3 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -86,10 +86,6 @@ extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
      const int stage,
      const struct nls_table *nls_cp);
 extern __u16 GetNextMid(struct TCP_Server_Info *server);
-extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16,
- struct cifsTconInfo *);
-extern void DeleteOplockQEntry(struct oplock_q_entry *);
-extern void DeleteTconOplockQEntries(struct cifsTconInfo *);
 extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
 extern u64 cifs_UnixTimeToNT(struct timespec);
 extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
@@ -117,6 +113,7 @@ extern void cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
       const char *path, const __u16 *pfid);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
+void cifs_put_tcon(struct cifsTconInfo *tcon);
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
  const char *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 301e307..941441d 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -94,6 +94,7 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
  list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
  open_file = list_entry(tmp, struct cifsFileInfo, tlist);
  open_file->invalidHandle = true;
+ open_file->oplock_break_cancelled = true;
  }
  write_unlock(&GlobalSMBSeslock);
  /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 9c91c8b..056af6f 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1645,7 +1645,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
  return NULL;
 }
 
-static void
+void
 cifs_put_tcon(struct cifsTconInfo *tcon)
 {
  int xid;
@@ -1664,7 +1664,6 @@ cifs_put_tcon(struct cifsTconInfo *tcon)
  CIFSSMBTDis(xid, tcon);
  _FreeXid(xid);
 
- DeleteTconOplockQEntries(tcon);
  tconInfoFree(tcon);
  cifs_put_smb_ses(ses);
 }
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 3643550..cbe20f2 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -148,12 +148,14 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
  pCifsFile->netfid = fileHandle;
  pCifsFile->pid = current->tgid;
  pCifsFile->pInode = igrab(newinode);
+ pCifsFile->tcon = tcon;
  pCifsFile->invalidHandle = false;
  pCifsFile->closePend = false;
  mutex_init(&pCifsFile->fh_mutex);
  mutex_init(&pCifsFile->lock_mutex);
  INIT_LIST_HEAD(&pCifsFile->llist);
  atomic_set(&pCifsFile->count, 1);
+ slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
 
  /* set the following in open now
  pCifsFile->pfile = file; */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index b976cea..5af63dd 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -41,7 +41,7 @@
 
 static inline struct cifsFileInfo *cifs_init_private(
  struct cifsFileInfo *private_data, struct inode *inode,
- struct file *file, __u16 netfid)
+ struct cifsTconInfo *tcon, struct file *file, __u16 netfid)
 {
  memset(private_data, 0, sizeof(struct cifsFileInfo));
  private_data->netfid = netfid;
@@ -51,11 +51,13 @@ static inline struct cifsFileInfo *cifs_init_private(
  INIT_LIST_HEAD(&private_data->llist);
  private_data->pfile = file; /* needed for writepage */
  private_data->pInode = igrab(inode);
+ private_data->tcon = tcon;
  private_data->invalidHandle = false;
  private_data->closePend = false;
  /* Initialize reference count to one.  The private data is
  freed on the release of the last reference */
  atomic_set(&private_data->count, 1);
+ slow_work_init(&private_data->oplock_break, &cifs_oplock_break_ops);
 
  return private_data;
 }
@@ -423,7 +425,8 @@ int cifs_open(struct inode *inode, struct file *file)
  rc = -ENOMEM;
  goto out;
  }
- pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
+ pCifsFile = cifs_init_private(file->private_data, inode, tcon, file,
+ netfid);
  write_lock(&GlobalSMBSeslock);
  list_add(&pCifsFile->tlist, &tcon->openFileList);
 
@@ -2312,6 +2315,72 @@ out:
  return rc;
 }
 
+static void
+cifs_oplock_break(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ struct inode *inode = cfile->pInode;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ int rc, waitrc = 0;
+
+ if (inode && S_ISREG(inode->i_mode)) {
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cinode->clientCanCacheAll == 0)
+ break_lease(inode, FMODE_READ);
+ else if (cinode->clientCanCacheRead == 0)
+ break_lease(inode, FMODE_WRITE);
+#endif
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (cinode->clientCanCacheRead == 0) {
+ waitrc = filemap_fdatawait(inode->i_mapping);
+ invalidate_remote_inode(inode);
+ }
+ if (!rc)
+ rc = waitrc;
+ if (rc)
+ cinode->write_behind_rc = rc;
+ cFYI(1, ("Oplock flush inode %p rc %d", inode, rc));
+ }
+
+ /*
+ * releasing stale oplock after recent reconnect of smb session using
+ * a now incorrect file handle is not a data integrity issue but do
+ * not bother sending an oplock release if session to server still is
+ * disconnected since oplock already released by the server
+ */
+ if (!cfile->closePend && !cfile->oplock_break_cancelled) {
+ rc = CIFSSMBLock(0, cfile->tcon, cfile->netfid, 0, 0, 0, 0,
+ LOCKING_ANDX_OPLOCK_RELEASE, false);
+ cFYI(1, ("Oplock release rc = %d", rc));
+ }
+}
+
+static int
+cifs_oplock_break_get(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ cifsFileInfo_get(cfile);
+ ++cfile->tcon->tc_count;
+ return 0;
+}
+
+static void
+cifs_oplock_break_put(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ cifsFileInfo_put(cfile);
+ cifs_put_tcon(cfile->tcon);
+}
+
+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 = {
  .readpage = cifs_readpage,
  .readpages = cifs_readpages,
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 191e622..0241b25 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -32,7 +32,6 @@
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
-extern struct task_struct *oplockThread;
 
 /* The xid serves as a useful identifier for each incoming vfs request,
    in a similar way to the mid which is useful to track each sent smb,
@@ -500,6 +499,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  struct cifsTconInfo *tcon;
  struct cifsInodeInfo *pCifsInode;
  struct cifsFileInfo *netfile;
+ int rc;
 
  cFYI(1, ("Checking for oplock break or dnotify response"));
  if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
@@ -569,19 +569,30 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  if (pSMB->Fid != netfile->netfid)
  continue;
 
- read_unlock(&GlobalSMBSeslock);
- read_unlock(&cifs_tcp_ses_lock);
+ /*
+ * don't do anything if file is about to be
+ * closed anyway.
+ */
+ if (netfile->closePend) {
+ read_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+
  cFYI(1, ("file id match, oplock break"));
  pCifsInode = CIFS_I(netfile->pInode);
  pCifsInode->clientCanCacheAll = false;
  if (pSMB->OplockLevel == 0)
  pCifsInode->clientCanCacheRead = false;
- AllocOplockQEntry(netfile->pInode,
-  netfile->netfid, tcon);
- cFYI(1, ("about to wake up oplock thread"));
- if (oplockThread)
- wake_up_process(oplockThread);
-
+ 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;
+ }
+ read_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
  return true;
  }
  read_unlock(&GlobalSMBSeslock);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 1da4ab2..07b8e71 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -103,56 +103,6 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
  mempool_free(midEntry, cifs_mid_poolp);
 }
 
-struct oplock_q_entry *
-AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon)
-{
- struct oplock_q_entry *temp;
- if ((pinode == NULL) || (tcon == NULL)) {
- cERROR(1, ("Null parms passed to AllocOplockQEntry"));
- return NULL;
- }
- temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
-       GFP_KERNEL);
- if (temp == NULL)
- return temp;
- else {
- temp->pinode = pinode;
- temp->tcon = tcon;
- temp->netfid = fid;
- spin_lock(&cifs_oplock_lock);
- list_add_tail(&temp->qhead, &cifs_oplock_list);
- spin_unlock(&cifs_oplock_lock);
- }
- return temp;
-}
-
-void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry)
-{
- spin_lock(&cifs_oplock_lock);
-    /* should we check if list empty first? */
- list_del(&oplockEntry->qhead);
- spin_unlock(&cifs_oplock_lock);
- kmem_cache_free(cifs_oplock_cachep, oplockEntry);
-}
-
-
-void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
-{
- struct oplock_q_entry *temp;
-
- if (tcon == NULL)
- return;
-
- spin_lock(&cifs_oplock_lock);
- list_for_each_entry(temp, &cifs_oplock_list, qhead) {
- if ((temp->tcon) && (temp->tcon == tcon)) {
- list_del(&temp->qhead);
- kmem_cache_free(cifs_oplock_cachep, temp);
- }
- }
- spin_unlock(&cifs_oplock_lock);
-}
-
 static int
 smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
 {
--
1.6.0.6



_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client

Re: [PATCH 4/4] cifs: convert oplock breaks to use slow_work facility

by Jeff Layton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Sat, 12 Sep 2009 07:59:53 -0400
Jeff Layton <jlayton@...> wrote:

> On Fri, 11 Sep 2009 08:14:08 -0400
> Jeff Layton <jlayton@...> wrote:
>
> > Currently, when an oplock break comes in there's a chance that the
> > oplock break job won't occur if the allocation of the oplock_q_entry
> > fails. There are also some rather nasty races in the allocation and
> > handling these structs.
> >
> > Rather than allocating oplock queue entries when an oplock break comes
> > in, add a few extra fields to the cifsFileInfo struct. Get rid of the
> > dedicated cifs_oplock_thread as well and queue the oplock break job
> > to the slow_work thread pool.
> >
> > This approach also has the advantage that the oplock break jobs can
> > potentially run in parallel rather than be serialized like they are
> > today.
> >
> > Signed-off-by: Jeff Layton <jlayton@...>
> > ---
> >  fs/cifs/Kconfig     |    1 +
> >  fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
> >  fs/cifs/cifsglob.h  |   12 +++----
> >  fs/cifs/cifsproto.h |    4 --
> >  fs/cifs/cifssmb.c   |    1 +
> >  fs/cifs/connect.c   |    1 -
> >  fs/cifs/dir.c       |    2 +
> >  fs/cifs/file.c      |   71 +++++++++++++++++++++++++++++++++++++-
> >  fs/cifs/misc.c      |   29 +++++++++++-----
> >  fs/cifs/transport.c |   50 ---------------------------
> >  10 files changed, 103 insertions(+), 163 deletions(-)
> >
>
> Steve,
>
> Attached is a new version of this patch. It's still very close to
> the old version. The only difference is that it has the oplock break
> job take and hold a reference to the tcon until it completes. With
> the other patch, there was a small possibility that the tcon could
> vanish while the oplock break job is running. This should fix by
> ensuring that the tcon can't go away until the oplock break job
> completes.
>
Yet another version...bonehead error this time.

This one reverses the order of operations in cifs_oplock_break_put. We
do not want to reference "cfile" after possibly freeing it!

I'll have this in my git repo momentarily...

--
Jeff Layton <jlayton@...>


From ff02f0d1d7554ff861bc233c0d11cc2151e68a38 Mon Sep 17 00:00:00 2001
From: Jeff Layton <jlayton@...>
Date: Mon, 14 Sep 2009 07:45:23 -0400
Subject: [PATCH] cifs: convert oplock breaks to use slow_work facility

Currently, when an oplock break comes in there's a chance that the
oplock break job won't occur if the allocation of the oplock_q_entry
fails. There are also some rather nasty races in the allocation and
handling these structs.

Rather than allocating oplock queue entries when an oplock break comes
in, add a few extra fields to the cifsFileInfo struct. Get rid of the
dedicated cifs_oplock_thread as well and queue the oplock break job
to the slow_work thread pool.

This approach also has the advantage that the oplock break jobs can
potentially run in parallel rather than be serialized like they are
today.

Signed-off-by: Jeff Layton <jlayton@...>
---
 fs/cifs/Kconfig     |    1 +
 fs/cifs/cifsfs.c    |   95 +++------------------------------------------------
 fs/cifs/cifsglob.h  |   12 +++----
 fs/cifs/cifsproto.h |    5 +--
 fs/cifs/cifssmb.c   |    1 +
 fs/cifs/connect.c   |    3 +-
 fs/cifs/dir.c       |    2 +
 fs/cifs/file.c      |   73 ++++++++++++++++++++++++++++++++++++++-
 fs/cifs/misc.c      |   29 +++++++++++-----
 fs/cifs/transport.c |   50 ---------------------------
 10 files changed, 107 insertions(+), 164 deletions(-)

diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 6994a0f..80f3525 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -2,6 +2,7 @@ config CIFS
  tristate "CIFS support (advanced network filesystem, SMBFS successor)"
  depends on INET
  select NLS
+ select SLOW_WORK
  help
   This is the client VFS module for the Common Internet File System
   (CIFS) protocol which is the successor to the Server Message Block
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 3610e99..89142b3 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -64,9 +64,6 @@ unsigned int multiuser_mount = 0;
 unsigned int extended_security = CIFSSEC_DEF;
 /* unsigned int ntlmv2_support = 0; */
 unsigned int sign_CIFS_PDUs = 1;
-extern struct task_struct *oplockThread; /* remove sparse warning */
-struct task_struct *oplockThread = NULL;
-/* extern struct task_struct * dnotifyThread; remove sparse warning */
 static const struct super_operations cifs_super_ops;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
@@ -973,89 +970,12 @@ cifs_destroy_mids(void)
  kmem_cache_destroy(cifs_oplock_cachep);
 }
 
-static int cifs_oplock_thread(void *dummyarg)
-{
- struct oplock_q_entry *oplock_item;
- struct cifsTconInfo *pTcon;
- struct inode *inode;
- __u16  netfid;
- int rc, waitrc = 0;
-
- set_freezable();
- do {
- if (try_to_freeze())
- continue;
-
- spin_lock(&cifs_oplock_lock);
- if (list_empty(&cifs_oplock_list)) {
- spin_unlock(&cifs_oplock_lock);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(39*HZ);
- } else {
- oplock_item = list_entry(cifs_oplock_list.next,
- struct oplock_q_entry, qhead);
- cFYI(1, ("found oplock item to write out"));
- pTcon = oplock_item->tcon;
- inode = oplock_item->pinode;
- netfid = oplock_item->netfid;
- spin_unlock(&cifs_oplock_lock);
- DeleteOplockQEntry(oplock_item);
- /* can not grab inode sem here since it would
- deadlock when oplock received on delete
- since vfs_unlink holds the i_mutex across
- the call */
- /* mutex_lock(&inode->i_mutex);*/
- if (S_ISREG(inode->i_mode)) {
-#ifdef CONFIG_CIFS_EXPERIMENTAL
- if (CIFS_I(inode)->clientCanCacheAll == 0)
- break_lease(inode, FMODE_READ);
- else if (CIFS_I(inode)->clientCanCacheRead == 0)
- break_lease(inode, FMODE_WRITE);
-#endif
- rc = filemap_fdatawrite(inode->i_mapping);
- if (CIFS_I(inode)->clientCanCacheRead == 0) {
- waitrc = filemap_fdatawait(
-      inode->i_mapping);
- invalidate_remote_inode(inode);
- }
- if (rc == 0)
- rc = waitrc;
- } else
- rc = 0;
- /* mutex_unlock(&inode->i_mutex);*/
- if (rc)
- CIFS_I(inode)->write_behind_rc = rc;
- cFYI(1, ("Oplock flush inode %p rc %d",
- inode, rc));
-
- /* releasing stale oplock after recent reconnect
- of smb session using a now incorrect file
- handle is not a data integrity issue but do
- not bother sending an oplock release if session
- to server still is disconnected since oplock
- already released by the server in that case */
- if (!pTcon->need_reconnect) {
- rc = CIFSSMBLock(0, pTcon, netfid,
- 0 /* len */ , 0 /* offset */, 0,
- 0, LOCKING_ANDX_OPLOCK_RELEASE,
- false /* wait flag */);
- cFYI(1, ("Oplock release rc = %d", rc));
- }
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);  /* yield in case q were corrupt */
- }
- } while (!kthread_should_stop());
-
- return 0;
-}
-
 static int __init
 init_cifs(void)
 {
  int rc = 0;
  cifs_proc_init();
  INIT_LIST_HEAD(&cifs_tcp_ses_list);
- INIT_LIST_HEAD(&cifs_oplock_list);
 #ifdef CONFIG_CIFS_EXPERIMENTAL
  INIT_LIST_HEAD(&GlobalDnotifyReqList);
  INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
@@ -1084,7 +1004,6 @@ init_cifs(void)
  rwlock_init(&GlobalSMBSeslock);
  rwlock_init(&cifs_tcp_ses_lock);
  spin_lock_init(&GlobalMid_Lock);
- spin_lock_init(&cifs_oplock_lock);
 
  if (cifs_max_pending < 2) {
  cifs_max_pending = 2;
@@ -1119,18 +1038,15 @@ init_cifs(void)
  if (rc)
  goto out_unregister_key_type;
 #endif
- oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd");
- if (IS_ERR(oplockThread)) {
- rc = PTR_ERR(oplockThread);
- cERROR(1, ("error %d create oplock thread", rc));
- goto out_unregister_dfs_key_type;
- }
+ rc = slow_work_register_user();
+ if (rc)
+ goto out_unregister_resolver_key;
 
  return 0;
 
- out_unregister_dfs_key_type:
-#ifdef CONFIG_CIFS_DFS_UPCALL
+ out_unregister_resolver_key:
  unregister_key_type(&key_type_dns_resolver);
+#ifdef CONFIG_CIFS_DFS_UPCALL
  out_unregister_key_type:
 #endif
 #ifdef CONFIG_CIFS_UPCALL
@@ -1165,7 +1081,6 @@ exit_cifs(void)
  cifs_destroy_inodecache();
  cifs_destroy_mids();
  cifs_destroy_request_bufs();
- kthread_stop(oplockThread);
 }
 
 MODULE_AUTHOR("Steve French <sfrench@...>");
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 908b2de..c3280fa 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -18,6 +18,7 @@
  */
 #include <linux/in.h>
 #include <linux/in6.h>
+#include <linux/slow-work.h>
 #include "cifs_fs_sb.h"
 #include "cifsacl.h"
 /*
@@ -346,14 +347,16 @@ struct cifsFileInfo {
  /* lock scope id (0 if none) */
  struct file *pfile; /* needed for writepage */
  struct inode *pInode; /* needed for oplock break */
+ struct cifsTconInfo *tcon;
  struct mutex lock_mutex;
  struct list_head llist; /* list of byte range locks we have. */
  bool closePend:1; /* file is marked to close */
  bool invalidHandle:1; /* file closed via session abend */
- bool messageMode:1; /* for pipes: message vs byte mode */
+ bool oplock_break_cancelled:1;
  atomic_t count; /* reference count */
  struct mutex fh_mutex; /* prevents reopen race after dead ses*/
  struct cifs_search_info srch_inf;
+ struct slow_work oplock_break; /* slow_work job for oplock breaks */
 };
 
 /* Take a reference on the file private data */
@@ -670,12 +673,6 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
  */
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;
 
-/* Global list of oplocks */
-GLOBAL_EXTERN struct list_head cifs_oplock_list;
-
-/* Protects the cifs_oplock_list */
-GLOBAL_EXTERN spinlock_t cifs_oplock_lock;
-
 /* Outstanding dir notify requests */
 GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
 /* DirNotify response queue */
@@ -726,3 +723,4 @@ 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_max_pending; /* MAX requests at once to server*/
 
+extern const struct slow_work_ops cifs_oplock_break_ops;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index bf3ae88..b7d165c 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -86,10 +86,6 @@ extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
      const int stage,
      const struct nls_table *nls_cp);
 extern __u16 GetNextMid(struct TCP_Server_Info *server);
-extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16,
- struct cifsTconInfo *);
-extern void DeleteOplockQEntry(struct oplock_q_entry *);
-extern void DeleteTconOplockQEntries(struct cifsTconInfo *);
 extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
 extern u64 cifs_UnixTimeToNT(struct timespec);
 extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
@@ -117,6 +113,7 @@ extern void cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
       const char *path, const __u16 *pfid);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
+extern void cifs_put_tcon(struct cifsTconInfo *tcon);
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
  const char *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 301e307..941441d 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -94,6 +94,7 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
  list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
  open_file = list_entry(tmp, struct cifsFileInfo, tlist);
  open_file->invalidHandle = true;
+ open_file->oplock_break_cancelled = true;
  }
  write_unlock(&GlobalSMBSeslock);
  /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 9c91c8b..056af6f 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1645,7 +1645,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
  return NULL;
 }
 
-static void
+void
 cifs_put_tcon(struct cifsTconInfo *tcon)
 {
  int xid;
@@ -1664,7 +1664,6 @@ cifs_put_tcon(struct cifsTconInfo *tcon)
  CIFSSMBTDis(xid, tcon);
  _FreeXid(xid);
 
- DeleteTconOplockQEntries(tcon);
  tconInfoFree(tcon);
  cifs_put_smb_ses(ses);
 }
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 3643550..cbe20f2 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -148,12 +148,14 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
  pCifsFile->netfid = fileHandle;
  pCifsFile->pid = current->tgid;
  pCifsFile->pInode = igrab(newinode);
+ pCifsFile->tcon = tcon;
  pCifsFile->invalidHandle = false;
  pCifsFile->closePend = false;
  mutex_init(&pCifsFile->fh_mutex);
  mutex_init(&pCifsFile->lock_mutex);
  INIT_LIST_HEAD(&pCifsFile->llist);
  atomic_set(&pCifsFile->count, 1);
+ slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
 
  /* set the following in open now
  pCifsFile->pfile = file; */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index b976cea..9b59222 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -41,7 +41,7 @@
 
 static inline struct cifsFileInfo *cifs_init_private(
  struct cifsFileInfo *private_data, struct inode *inode,
- struct file *file, __u16 netfid)
+ struct cifsTconInfo *tcon, struct file *file, __u16 netfid)
 {
  memset(private_data, 0, sizeof(struct cifsFileInfo));
  private_data->netfid = netfid;
@@ -51,11 +51,13 @@ static inline struct cifsFileInfo *cifs_init_private(
  INIT_LIST_HEAD(&private_data->llist);
  private_data->pfile = file; /* needed for writepage */
  private_data->pInode = igrab(inode);
+ private_data->tcon = tcon;
  private_data->invalidHandle = false;
  private_data->closePend = false;
  /* Initialize reference count to one.  The private data is
  freed on the release of the last reference */
  atomic_set(&private_data->count, 1);
+ slow_work_init(&private_data->oplock_break, &cifs_oplock_break_ops);
 
  return private_data;
 }
@@ -423,7 +425,8 @@ int cifs_open(struct inode *inode, struct file *file)
  rc = -ENOMEM;
  goto out;
  }
- pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
+ pCifsFile = cifs_init_private(file->private_data, inode, tcon, file,
+ netfid);
  write_lock(&GlobalSMBSeslock);
  list_add(&pCifsFile->tlist, &tcon->openFileList);
 
@@ -2312,6 +2315,72 @@ out:
  return rc;
 }
 
+static void
+cifs_oplock_break(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ struct inode *inode = cfile->pInode;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ int rc, waitrc = 0;
+
+ if (inode && S_ISREG(inode->i_mode)) {
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cinode->clientCanCacheAll == 0)
+ break_lease(inode, FMODE_READ);
+ else if (cinode->clientCanCacheRead == 0)
+ break_lease(inode, FMODE_WRITE);
+#endif
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (cinode->clientCanCacheRead == 0) {
+ waitrc = filemap_fdatawait(inode->i_mapping);
+ invalidate_remote_inode(inode);
+ }
+ if (!rc)
+ rc = waitrc;
+ if (rc)
+ cinode->write_behind_rc = rc;
+ cFYI(1, ("Oplock flush inode %p rc %d", inode, rc));
+ }
+
+ /*
+ * releasing stale oplock after recent reconnect of smb session using
+ * a now incorrect file handle is not a data integrity issue but do
+ * not bother sending an oplock release if session to server still is
+ * disconnected since oplock already released by the server
+ */
+ if (!cfile->closePend && !cfile->oplock_break_cancelled) {
+ rc = CIFSSMBLock(0, cfile->tcon, cfile->netfid, 0, 0, 0, 0,
+ LOCKING_ANDX_OPLOCK_RELEASE, false);
+ cFYI(1, ("Oplock release rc = %d", rc));
+ }
+}
+
+static int
+cifs_oplock_break_get(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ ++cfile->tcon->tc_count;
+ cifsFileInfo_get(cfile);
+ return 0;
+}
+
+static void
+cifs_oplock_break_put(struct slow_work *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+  oplock_break);
+ cifs_put_tcon(cfile->tcon);
+ 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 = {
  .readpage = cifs_readpage,
  .readpages = cifs_readpages,
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 191e622..0241b25 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -32,7 +32,6 @@
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
-extern struct task_struct *oplockThread;
 
 /* The xid serves as a useful identifier for each incoming vfs request,
    in a similar way to the mid which is useful to track each sent smb,
@@ -500,6 +499,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  struct cifsTconInfo *tcon;
  struct cifsInodeInfo *pCifsInode;
  struct cifsFileInfo *netfile;
+ int rc;
 
  cFYI(1, ("Checking for oplock break or dnotify response"));
  if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
@@ -569,19 +569,30 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
  if (pSMB->Fid != netfile->netfid)
  continue;
 
- read_unlock(&GlobalSMBSeslock);
- read_unlock(&cifs_tcp_ses_lock);
+ /*
+ * don't do anything if file is about to be
+ * closed anyway.
+ */
+ if (netfile->closePend) {
+ read_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+
  cFYI(1, ("file id match, oplock break"));
  pCifsInode = CIFS_I(netfile->pInode);
  pCifsInode->clientCanCacheAll = false;
  if (pSMB->OplockLevel == 0)
  pCifsInode->clientCanCacheRead = false;
- AllocOplockQEntry(netfile->pInode,
-  netfile->netfid, tcon);
- cFYI(1, ("about to wake up oplock thread"));
- if (oplockThread)
- wake_up_process(oplockThread);
-
+ 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;
+ }
+ read_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
  return true;
  }
  read_unlock(&GlobalSMBSeslock);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 1da4ab2..07b8e71 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -103,56 +103,6 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
  mempool_free(midEntry, cifs_mid_poolp);
 }
 
-struct oplock_q_entry *
-AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon)
-{
- struct oplock_q_entry *temp;
- if ((pinode == NULL) || (tcon == NULL)) {
- cERROR(1, ("Null parms passed to AllocOplockQEntry"));
- return NULL;
- }
- temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
-       GFP_KERNEL);
- if (temp == NULL)
- return temp;
- else {
- temp->pinode = pinode;
- temp->tcon = tcon;
- temp->netfid = fid;
- spin_lock(&cifs_oplock_lock);
- list_add_tail(&temp->qhead, &cifs_oplock_list);
- spin_unlock(&cifs_oplock_lock);
- }
- return temp;
-}
-
-void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry)
-{
- spin_lock(&cifs_oplock_lock);
-    /* should we check if list empty first? */
- list_del(&oplockEntry->qhead);
- spin_unlock(&cifs_oplock_lock);
- kmem_cache_free(cifs_oplock_cachep, oplockEntry);
-}
-
-
-void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
-{
- struct oplock_q_entry *temp;
-
- if (tcon == NULL)
- return;
-
- spin_lock(&cifs_oplock_lock);
- list_for_each_entry(temp, &cifs_oplock_list, qhead) {
- if ((temp->tcon) && (temp->tcon == tcon)) {
- list_del(&temp->qhead);
- kmem_cache_free(cifs_oplock_cachep, temp);
- }
- }
- spin_unlock(&cifs_oplock_lock);
-}
-
 static int
 smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
 {
--
1.6.0.6



_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@...
https://lists.samba.org/mailman/listinfo/linux-cifs-client