[PATCH 0/2] cifs: fix "Busy inodes after umount" issues (try #2)

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 0/2] cifs: fix "Busy inodes after umount" issues (try #2)

Jeff Layton-2
This is the second attempt at a patchset to fix a recent outbreak of
"Busy inodes after umount" issues with cifs. The approach is basically
the same.

The first patch in the original set has also been dropped since Al
pointed out that the existing behavior is more clear. The second and
third patches have also been merged since they touch the same areas of
code. Finally a few bugs in the original set have been fixed as well.

With this set, I've not been able to reproduce any "busy inodes after
umount" issues, but it would be nice to have some extra eyeballs and
testing on this set before it goes in.

Jeff Layton (2):
  cifs: move cifs_new_fileinfo call out of cifs_posix_open
  cifs: pass instantiated filp back after open call

 fs/cifs/cifsproto.h |    1 -
 fs/cifs/dir.c       |   76 +++++++++++++++++++++++++++++---------------------
 fs/cifs/file.c      |   59 +++++++++-------------------------------
 3 files changed, 57 insertions(+), 79 deletions(-)

_______________________________________________
linux-cifs-client mailing list
[hidden email]
https://lists.samba.org/mailman/listinfo/linux-cifs-client
Reply | Threaded
Open this post in threaded view
|

[PATCH 1/2] cifs: move cifs_new_fileinfo call out of cifs_posix_open

Jeff Layton-2
Having cifs_posix_open call cifs_new_fileinfo is problematic and
inconsistent with how "regular" opens work. It's also buggy as
cifs_reopen_file calls this function on a reconnect, which creates a new
struct cifsFileInfo that just gets leaked.

Push it out into the callers. This also allows us to get rid of the
"mnt" arg to cifs_posix_open.

Finally, in the event that a cifsFileInfo isn't or can't be created, we
always want to close the filehandle out on the server as the client
won't have a record of the filehandle and can't actually use it. Make
sure that CIFSSMBClose is called in those cases.

Signed-off-by: Jeff Layton <[hidden email]>
---
 fs/cifs/cifsproto.h |    1 -
 fs/cifs/dir.c       |   43 ++++++++++++++++++-------------------------
 fs/cifs/file.c      |   17 ++++++++++++-----
 3 files changed, 30 insertions(+), 31 deletions(-)

diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index fb1657e..fb6318b 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -106,7 +106,6 @@ extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode,
  __u16 fileHandle, struct file *file,
  struct vfsmount *mnt, unsigned int oflags);
 extern int cifs_posix_open(char *full_path, struct inode **pinode,
- struct vfsmount *mnt,
  struct super_block *sb,
  int mode, int oflags,
  __u32 *poplock, __u16 *pnetfid, int xid);
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 391816b..f49afb9 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -188,8 +188,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
 }
 
 int cifs_posix_open(char *full_path, struct inode **pinode,
- struct vfsmount *mnt, struct super_block *sb,
- int mode, int oflags,
+ struct super_block *sb, int mode, int oflags,
  __u32 *poplock, __u16 *pnetfid, int xid)
 {
  int rc;
@@ -258,19 +257,6 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
  cifs_fattr_to_inode(*pinode, &fattr);
  }
 
- /*
- * cifs_fill_filedata() takes care of setting cifsFileInfo pointer to
- * file->private_data.
- */
- if (mnt) {
- struct cifsFileInfo *pfile_info;
-
- pfile_info = cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt,
-       oflags);
- if (pfile_info == NULL)
- rc = -ENOMEM;
- }
-
 posix_open_ret:
  kfree(presp_data);
  return rc;
@@ -298,7 +284,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
  int create_options = CREATE_NOT_DIR;
  __u32 oplock = 0;
  int oflags;
- bool posix_create = false;
  /*
  * BB below access is probably too much for mknod to request
  *    but we have to do query and setpathinfo so requesting
@@ -339,7 +324,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
     (CIFS_UNIX_POSIX_PATH_OPS_CAP &
  le64_to_cpu(tcon->fsUnixInfo.Capability))) {
  rc = cifs_posix_open(full_path, &newinode,
- nd ? nd->path.mnt : NULL,
  inode->i_sb, mode, oflags, &oplock, &fileHandle, xid);
  /* EIO could indicate that (posix open) operation is not
    supported, despite what server claimed in capability
@@ -347,7 +331,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
    handled in posix open */
 
  if (rc == 0) {
- posix_create = true;
  if (newinode == NULL) /* query inode info */
  goto cifs_create_get_file_info;
  else /* success, no need to query */
@@ -478,11 +461,7 @@ cifs_create_set_dentry:
  else
  cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
 
- /* nfsd case - nfs srv does not set nd */
- if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) {
- /* mknod case - do not leave file open */
- CIFSSMBClose(xid, tcon, fileHandle);
- } else if (!(posix_create) && (newinode)) {
+ if (newinode && nd && (nd->flags & LOOKUP_OPEN)) {
  struct cifsFileInfo *pfile_info;
  /*
  * cifs_fill_filedata() takes care of setting cifsFileInfo
@@ -492,7 +471,10 @@ cifs_create_set_dentry:
        nd->path.mnt, oflags);
  if (pfile_info == NULL)
  rc = -ENOMEM;
+ } else {
+ CIFSSMBClose(xid, tcon, fileHandle);
  }
+
 cifs_create_out:
  kfree(buf);
  kfree(full_path);
@@ -636,6 +618,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
  bool posix_open = false;
  struct cifs_sb_info *cifs_sb;
  struct cifsTconInfo *pTcon;
+ struct cifsFileInfo *cfile;
  struct inode *newInode = NULL;
  char *full_path = NULL;
  struct file *filp;
@@ -703,7 +686,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
  if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
      (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
      (nd->intent.open.flags & O_CREAT)) {
- rc = cifs_posix_open(full_path, &newInode, nd->path.mnt,
+ rc = cifs_posix_open(full_path, &newInode,
  parent_dir_inode->i_sb,
  nd->intent.open.create_mode,
  nd->intent.open.flags, &oplock,
@@ -733,8 +716,17 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
  else
  direntry->d_op = &cifs_dentry_ops;
  d_add(direntry, newInode);
- if (posix_open)
+ if (posix_open) {
+ cfile = cifs_new_fileinfo(newInode, fileHandle, NULL,
+  nd->path.mnt,
+  nd->intent.open.flags);
+ if (cfile == NULL) {
+ CIFSSMBClose(xid, pTcon, fileHandle);
+ rc = -ENOMEM;
+ goto lookup_out;
+ }
  filp = lookup_instantiate_filp(nd, direntry, NULL);
+ }
  /* since paths are not looked up by component - the parent
    directories are presumed to be good here */
  renew_parental_timestamps(direntry);
@@ -755,6 +747,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
  is a common return code */
  }
 
+lookup_out:
  kfree(full_path);
  FreeXid(xid);
  return ERR_PTR(rc);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index a83541e..001e916 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -299,8 +299,7 @@ int cifs_open(struct inode *inode, struct file *file)
  int oflags = (int) cifs_posix_convert_flags(file->f_flags);
  oflags |= SMB_O_CREAT;
  /* can not refresh inode info since size could be stale */
- rc = cifs_posix_open(full_path, &inode, file->f_path.mnt,
- inode->i_sb,
+ rc = cifs_posix_open(full_path, &inode, inode->i_sb,
  cifs_sb->mnt_file_mode /* ignored */,
  oflags, &oplock, &netfid, xid);
  if (rc == 0) {
@@ -308,7 +307,16 @@ int cifs_open(struct inode *inode, struct file *file)
  /* no need for special case handling of setting mode
    on read only files needed here */
 
- pCifsFile = cifs_fill_filedata(file);
+ pCifsFile = cifs_new_fileinfo(inode, netfid, file,
+ file->f_path.mnt,
+ oflags);
+ if (pCifsFile == NULL) {
+ CIFSSMBClose(xid, tcon, netfid);
+ rc = -ENOMEM;
+ goto out;
+ }
+ file->private_data = pCifsFile;
+
  cifs_posix_open_inode_helper(inode, file, pCifsInode,
      oplock, netfid);
  goto out;
@@ -513,8 +521,7 @@ reopen_error_exit:
  le64_to_cpu(tcon->fsUnixInfo.Capability))) {
  int oflags = (int) cifs_posix_convert_flags(file->f_flags);
  /* can not refresh inode info since size could be stale */
- rc = cifs_posix_open(full_path, NULL, file->f_path.mnt,
- inode->i_sb,
+ rc = cifs_posix_open(full_path, NULL, inode->i_sb,
  cifs_sb->mnt_file_mode /* ignored */,
  oflags, &oplock, &netfid, xid);
  if (rc == 0) {
--
1.6.6.1

_______________________________________________
linux-cifs-client mailing list
[hidden email]
https://lists.samba.org/mailman/listinfo/linux-cifs-client
Reply | Threaded
Open this post in threaded view
|

[PATCH 2/2] cifs: pass instantiated filp back after open call

Jeff Layton-2
In reply to this post by Jeff Layton-2
The current scheme of sticking open files on a list and assuming that
cifs_open will scoop them off of it is broken and leads to "Busy
inodes after umount..." errors at unmount time.

The problem is that there is no guarantee that cifs_open will always
be called after a ->lookup or ->create operation. If there are
permissions or other problems, then it's quite likely that it *won't*
be called.

Fix this by fully instantiating the filp whenever the file is created
and pass that filp back to the VFS. If there is a problem, the VFS
can clean up the references.

Signed-off-by: Jeff Layton <[hidden email]>
---
 fs/cifs/dir.c  |   35 +++++++++++++++++++++++++++--------
 fs/cifs/file.c |   44 ++------------------------------------------
 2 files changed, 29 insertions(+), 50 deletions(-)

diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index f49afb9..e7ae78b 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/file.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
@@ -184,6 +185,8 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
  }
  write_unlock(&GlobalSMBSeslock);
 
+ file->private_data = pCifsFile;
+
  return pCifsFile;
 }
 
@@ -463,14 +466,22 @@ cifs_create_set_dentry:
 
  if (newinode && nd && (nd->flags & LOOKUP_OPEN)) {
  struct cifsFileInfo *pfile_info;
- /*
- * cifs_fill_filedata() takes care of setting cifsFileInfo
- * pointer to file->private_data.
- */
- pfile_info = cifs_new_fileinfo(newinode, fileHandle, NULL,
+ struct file *filp;
+
+ filp = lookup_instantiate_filp(nd, direntry, generic_file_open);
+ if (IS_ERR(filp)) {
+ rc = PTR_ERR(filp);
+ CIFSSMBClose(xid, tcon, fileHandle);
+ goto cifs_create_out;
+ }
+
+ pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp,
        nd->path.mnt, oflags);
- if (pfile_info == NULL)
+ if (pfile_info == NULL) {
+ fput(filp);
+ CIFSSMBClose(xid, tcon, fileHandle);
  rc = -ENOMEM;
+ }
  } else {
  CIFSSMBClose(xid, tcon, fileHandle);
  }
@@ -717,15 +728,23 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
  direntry->d_op = &cifs_dentry_ops;
  d_add(direntry, newInode);
  if (posix_open) {
- cfile = cifs_new_fileinfo(newInode, fileHandle, NULL,
+ filp = lookup_instantiate_filp(nd, direntry,
+       generic_file_open);
+ if (IS_ERR(filp)) {
+ rc = PTR_ERR(filp);
+ CIFSSMBClose(xid, pTcon, fileHandle);
+ goto lookup_out;
+ }
+
+ cfile = cifs_new_fileinfo(newInode, fileHandle, filp,
   nd->path.mnt,
   nd->intent.open.flags);
  if (cfile == NULL) {
+ fput(filp);
  CIFSSMBClose(xid, pTcon, fileHandle);
  rc = -ENOMEM;
  goto lookup_out;
  }
- filp = lookup_instantiate_filp(nd, direntry, NULL);
  }
  /* since paths are not looked up by component - the parent
    directories are presumed to be good here */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 001e916..310533d 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -162,38 +162,6 @@ psx_client_can_cache:
  return 0;
 }
 
-static struct cifsFileInfo *
-cifs_fill_filedata(struct file *file)
-{
- struct list_head *tmp;
- struct cifsFileInfo *pCifsFile = NULL;
- struct cifsInodeInfo *pCifsInode = NULL;
-
- /* search inode for this file and fill in file->private_data */
- pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
- read_lock(&GlobalSMBSeslock);
- list_for_each(tmp, &pCifsInode->openFileList) {
- pCifsFile = list_entry(tmp, struct cifsFileInfo, flist);
- if ((pCifsFile->pfile == NULL) &&
-    (pCifsFile->pid == current->tgid)) {
- /* mode set in cifs_create */
-
- /* needed for writepage */
- pCifsFile->pfile = file;
- file->private_data = pCifsFile;
- break;
- }
- }
- read_unlock(&GlobalSMBSeslock);
-
- if (file->private_data != NULL) {
- return pCifsFile;
- } else if ((file->f_flags & O_CREAT) && (file->f_flags & O_EXCL))
- cERROR(1, "could not find file instance for "
-   "new file %p", file);
- return NULL;
-}
-
 /* all arguments to this function must be checked for validity in caller */
 static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
  struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile,
@@ -256,7 +224,7 @@ int cifs_open(struct inode *inode, struct file *file)
  __u32 oplock;
  struct cifs_sb_info *cifs_sb;
  struct cifsTconInfo *tcon;
- struct cifsFileInfo *pCifsFile;
+ struct cifsFileInfo *pCifsFile = NULL;
  struct cifsInodeInfo *pCifsInode;
  char *full_path = NULL;
  int desiredAccess;
@@ -270,12 +238,6 @@ int cifs_open(struct inode *inode, struct file *file)
  tcon = cifs_sb->tcon;
 
  pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
- pCifsFile = cifs_fill_filedata(file);
- if (pCifsFile) {
- rc = 0;
- FreeXid(xid);
- return rc;
- }
 
  full_path = build_path_from_dentry(file->f_path.dentry);
  if (full_path == NULL) {
@@ -315,7 +277,6 @@ int cifs_open(struct inode *inode, struct file *file)
  rc = -ENOMEM;
  goto out;
  }
- file->private_data = pCifsFile;
 
  cifs_posix_open_inode_helper(inode, file, pCifsInode,
      oplock, netfid);
@@ -401,8 +362,7 @@ int cifs_open(struct inode *inode, struct file *file)
 
  pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt,
  file->f_flags);
- file->private_data = pCifsFile;
- if (file->private_data == NULL) {
+ if (pCifsFile == NULL) {
  rc = -ENOMEM;
  goto out;
  }
--
1.6.6.1

_______________________________________________
linux-cifs-client mailing list
[hidden email]
https://lists.samba.org/mailman/listinfo/linux-cifs-client