diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index f554a70c9cf3..5bab24f59053 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 4cdb29fdc8c2..1223fa81dbd2 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -97,5 +97,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); 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 839a55667c3c..1ba08f8c5bc4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -299,6 +299,7 @@ struct cifsFileInfo { 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 23af20d5af7c..da4f5e10b3cc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "cifsfs.h" #include "cifspdu.h" @@ -50,6 +51,11 @@ static inline struct cifsFileInfo *cifs_init_private( 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 @@ int cifs_close(struct inode *inode, struct file *file) /* 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 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) 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 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) 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 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) 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 @@ static int cifs_writepages(struct address_space *mapping, 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 @@ retry: 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 @@ retry: 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 @@ static int cifs_readpage(struct file *file, struct page *page) 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 ff4d1cc7c248..912d401600f6 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1006,6 +1006,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) __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;