[CIFS] Defer close of file handle slightly if there are pending writes that
need to get in ahead of it that depend on that file handle. Fixes
occassional bad file handle errors on write with heavy use multiple process
cases.
Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index f554a70..5bab24f 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,9 @@
+Version 1.39
+------------
+Defer close of a file handle slightly if pending writes depend on that file handle
+(this reduces the EBADF bad file handle errors that can be logged under heavy
+stress on writes).
+
Version 1.38
------------
Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket)
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 4cdb29f..1223fa8 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -97,5 +97,5 @@
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern int cifs_ioctl (struct inode * inode, struct file * filep,
unsigned int command, unsigned long arg);
-#define CIFS_VERSION "1.38"
+#define CIFS_VERSION "1.39"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 839a556..1ba08f8 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -299,6 +299,7 @@
struct inode * pInode; /* needed for oplock break */
unsigned closePend:1; /* file is marked to close */
unsigned invalidHandle:1; /* file closed via session abend */
+ atomic_t wrtPending; /* handle in use - defer close */
struct semaphore fh_sem; /* prevents reopen race after dead ses*/
char * search_resume_name; /* BB removeme BB */
unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 23af20d..da4f5e1 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -29,6 +29,7 @@
#include <linux/pagevec.h>
#include <linux/smp_lock.h>
#include <linux/writeback.h>
+#include <linux/delay.h>
#include <asm/div64.h>
#include "cifsfs.h"
#include "cifspdu.h"
@@ -50,6 +51,11 @@
private_data->pInode = inode;
private_data->invalidHandle = FALSE;
private_data->closePend = FALSE;
+ /* we have to track num writers to the inode, since writepages
+ does not tell us which handle the write is for so there can
+ be a close (overlapping with write) of the filehandle that
+ cifs_writepages chose to use */
+ atomic_set(&private_data->wrtPending,0);
return private_data;
}
@@ -473,6 +479,20 @@
/* no sense reconnecting to close a file that is
already closed */
if (pTcon->tidStatus != CifsNeedReconnect) {
+ int timeout = 2;
+ while((atomic_read(&pSMBFile->wrtPending) != 0)
+ && (timeout < 1000) ) {
+ /* Give write a better chance to get to
+ server ahead of the close. We do not
+ want to add a wait_q here as it would
+ increase the memory utilization as
+ the struct would be in each open file,
+ but this should give enough time to
+ clear the socket */
+ cERROR(1,("close with pending writes"));
+ msleep(timeout);
+ timeout *= 4;
+ }
write_unlock(&file->f_owner.lock);
rc = CIFSSMBClose(xid, pTcon,
pSMBFile->netfid);
@@ -919,9 +939,10 @@
if (open_file->pfile &&
((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) {
+ atomic_inc(&open_file->wrtPending);
read_unlock(&GlobalSMBSeslock);
if((open_file->invalidHandle) &&
- (!open_file->closePend)) {
+ (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) {
rc = cifs_reopen_file(&cifs_inode->vfs_inode,
open_file->pfile, FALSE);
/* if it fails, try another handle - might be */
@@ -929,6 +950,10 @@
if(rc) {
cFYI(1,("failed on reopen file in wp"));
read_lock(&GlobalSMBSeslock);
+ /* can not use this handle, no write
+ pending on this one after all */
+ atomic_dec
+ (&open_file->wrtPending);
continue;
}
}
@@ -981,6 +1006,7 @@
if (open_file) {
bytes_written = cifs_write(open_file->pfile, write_data,
to-from, &offset);
+ atomic_dec(&open_file->wrtPending);
/* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
if ((bytes_written > 0) && (offset)) {
@@ -1016,7 +1042,7 @@
pgoff_t next;
int nr_pages;
__u64 offset = 0;
- struct cifsFileInfo *open_file = NULL;
+ struct cifsFileInfo *open_file;
struct page *page;
struct pagevec pvec;
int rc = 0;
@@ -1071,15 +1097,6 @@
int first;
unsigned int i;
- if (!open_file) {
- open_file = find_writable_file(CIFS_I(mapping->host));
- if (!open_file) {
- pagevec_release(&pvec);
- cERROR(1, ("No writable handles for inode"));
- return -EIO;
- }
- }
-
first = -1;
next = 0;
n_iov = 0;
@@ -1155,18 +1172,32 @@
break;
}
if (n_iov) {
- rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
- open_file->netfid, bytes_to_write,
- offset, &bytes_written, iov, n_iov,
- 1);
- if (rc || bytes_written < bytes_to_write) {
- cERROR(1,("CIFSSMBWrite2 returned %d, written = %x",
- rc, bytes_written));
- set_bit(AS_EIO, &mapping->flags);
- SetPageError(page);
+ /* Search for a writable handle every time we call
+ * CIFSSMBWrite2. We can't rely on the last handle
+ * we used to still be valid
+ */
+ open_file = find_writable_file(CIFS_I(mapping->host));
+ if (!open_file) {
+ cERROR(1, ("No writable handles for inode"));
+ rc = -EBADF;
} else {
- cifs_stats_bytes_written(cifs_sb->tcon,
- bytes_written);
+ rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
+ open_file->netfid,
+ bytes_to_write, offset,
+ &bytes_written, iov, n_iov,
+ 1);
+ atomic_dec(&open_file->wrtPending);
+ if (rc || bytes_written < bytes_to_write) {
+ cERROR(1,("Write2 ret %d, written = %d",
+ rc, bytes_written));
+ /* BB what if continued retry is
+ requested via mount flags? */
+ set_bit(AS_EIO, &mapping->flags);
+ SetPageError(page);
+ } else {
+ cifs_stats_bytes_written(cifs_sb->tcon,
+ bytes_written);
+ }
}
for (i = 0; i < n_iov; i++) {
page = pvec.pages[first + i];
@@ -1788,9 +1819,18 @@
page caching in the current Linux kernel design */
int is_size_safe_to_change(struct cifsInodeInfo *cifsInode)
{
- if (cifsInode && find_writable_file(cifsInode))
+ struct cifsFileInfo *open_file = NULL;
+
+ if (cifsInode)
+ open_file = find_writable_file(cifsInode);
+
+ if(open_file) {
+ /* there is not actually a write pending so let
+ this handle go free and allow it to
+ be closable if needed */
+ atomic_dec(&open_file->wrtPending);
return 0;
- else
+ } else
return 1;
}
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index ff4d1cc7..912d4016 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1006,6 +1006,7 @@
__u32 npid = open_file->pid;
rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size,
nfid, npid, FALSE);
+ atomic_dec(&open_file->wrtPending);
cFYI(1,("SetFSize for attrs rc = %d", rc));
if(rc == -EINVAL) {
int bytes_written;