diff options
author | Steve French | 2005-10-05 14:50:29 -0700 |
---|---|---|
committer | Steve French | 2005-10-05 14:50:29 -0700 |
commit | 37c0eb4677f733a773df6287b0f73f00274402e3 (patch) | |
tree | 29d6da2f609bdc22316b24aca866ad2ee2093959 /fs | |
parent | 6148a742b2bd76abfe0c1fc50dd747cb9f28cd6b (diff) |
CIFS: implement cifs_writepages to perform multi-page I/O
Signed-off-by: Dave Kleikamp <shaggy@austin.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/file.c | 191 | ||||
-rw-r--r-- | fs/cifs/transport.c | 10 |
2 files changed, 190 insertions, 11 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 94875455d7fa..0473b221f643 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -21,11 +21,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/fs.h> +#include <linux/backing-dev.h> #include <linux/stat.h> #include <linux/fcntl.h> +#include <linux/mpage.h> #include <linux/pagemap.h> #include <linux/pagevec.h> #include <linux/smp_lock.h> +#include <linux/writeback.h> #include <asm/div64.h> #include "cifsfs.h" #include "cifspdu.h" @@ -916,6 +919,16 @@ static struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { read_unlock(&GlobalSMBSeslock); + if(open_file->invalidHandle) { + rc = cifs_reopen_file(cifs_inode->vfs_inode, + open_file->pfile, FALSE); + /* if it fails, try another handle - might be */ + /* dangerous to hold up writepages with retry */ + if(rc) { + read_lock(&GlobalSMBSeslock); + continue; + } + } return open_file; } } @@ -982,20 +995,181 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) return rc; } -#if 0 +#ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_writepages(struct address_space *mapping, - struct writeback_control *wbc) + struct writeback_control *wbc) { - int rc = -EFAULT; + struct backing_dev_info *bdi = mapping->backing_dev_info; + unsigned int bytes_to_write; + unsigned int bytes_written; + struct cifs_sb_info *cifs_sb; + int done = 0; + pgoff_t end = -1; + pgoff_t index; + int is_range = 0; + struct kvec iov[32]; + int n_iov = 0; + pgoff_t next; + int nr_pages; + __u64 offset = 0; + struct cifsFileInfo *open_file = NULL; + struct page *page; + struct pagevec pvec; + int rc = 0; + int scanned = 0; int xid; + cifs_sb = CIFS_SB(mapping->host->i_sb); + + /* + * If wsize is smaller that the page cache size, default to writing + * one page at a time via cifs_writepage + */ + if (cifs_sb->wsize < PAGE_CACHE_SIZE) + return generic_writepages(mapping, wbc); + + /* + * BB: Is this meaningful for a non-block-device file system? + * If it is, we should test it again after we do I/O + */ + if (wbc->nonblocking && bdi_write_congested(bdi)) { + wbc->encountered_congestion = 1; + return 0; + } + xid = GetXid(); - /* Find contiguous pages then iterate through repeating - call 16K write then Setpageuptodate or if LARGE_WRITE_X - support then send larger writes via kevec so as to eliminate - a memcpy */ + pagevec_init(&pvec, 0); + if (wbc->sync_mode == WB_SYNC_NONE) + index = mapping->writeback_index; /* Start from prev offset */ + else { + index = 0; + scanned = 1; + } + if (wbc->start || wbc->end) { + index = wbc->start >> PAGE_CACHE_SHIFT; + end = wbc->end >> PAGE_CACHE_SHIFT; + is_range = 1; + scanned = 1; + } +retry: + while (!done && (index <= end) && + (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, + PAGECACHE_TAG_DIRTY, + min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) { + 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; + bytes_to_write = 0; + + for (i = 0; i < nr_pages; i++) { + page = pvec.pages[i]; + /* + * At this point we hold neither mapping->tree_lock nor + * lock on the page itself: the page may be truncated or + * invalidated (changing page->mapping to NULL), or even + * swizzled back from swapper_space to tmpfs file + * mapping + */ + + if (first < 0) + lock_page(page); + else if (TestSetPageLocked(page)) + break; + + if (unlikely(page->mapping != mapping)) { + unlock_page(page); + break; + } + + if (unlikely(is_range) && (page->index > end)) { + done = 1; + unlock_page(page); + break; + } + + if (next && (page->index != next)) { + /* Not next consecutive page */ + unlock_page(page); + break; + } + + if (wbc->sync_mode != WB_SYNC_NONE) + wait_on_page_writeback(page); + + if (PageWriteback(page) || + !test_clear_page_dirty(page)) { + unlock_page(page); + break; + } + /* + * BB can we get rid of this? pages are held by pvec + */ + page_cache_get(page); + + /* reserve iov[0] for the smb header */ + n_iov++; + iov[n_iov].iov_base = kmap(page); + iov[n_iov].iov_len = PAGE_CACHE_SIZE; + bytes_to_write += PAGE_CACHE_SIZE; + + if (first < 0) { + first = i; + offset = page_offset(page); + } + next = page->index + 1; + if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize) + 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); + } + for (i = 0; i < n_iov; i++) { + page = pvec.pages[first + i]; + kunmap(page); + unlock_page(page); + page_cache_release(page); + } + if ((wbc->nr_to_write -= n_iov) <= 0) + done = 1; + index = next; + } + pagevec_release(&pvec); + } + if (!scanned && !done) { + /* + * We hit the last page and there is more work to be done: wrap + * back to the start of the file + */ + scanned = 1; + index = 0; + goto retry; + } + if (!is_range) + mapping->writeback_index = index; + FreeXid(xid); + return rc; } #endif @@ -1635,6 +1809,9 @@ struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, .readpages = cifs_readpages, .writepage = cifs_writepage, +#ifdef CONFIG_CIFS_EXPERIMENTAL + .writepages = cifs_writepages, +#endif .prepare_write = cifs_prepare_write, .commit_write = cifs_commit_write, .set_page_dirty = __set_page_dirty_nobuffers, diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 64c712629f27..e104c1ad2da3 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -395,7 +395,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if (long_op == -1) goto cifs_no_response_exit2; else if (long_op == 2) /* writes past end of file can take loong time */ - timeout = 300 * HZ; + timeout = 180 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ @@ -431,7 +431,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response buffer")); + cERROR(1,("No response to cmd %d mid %d", + midQ->command, midQ->mid)); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; @@ -646,7 +647,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if (long_op == -1) goto cifs_no_response_exit; else if (long_op == 2) /* writes past end of file can take loong time */ - timeout = 300 * HZ; + timeout = 180 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ @@ -682,7 +683,8 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response buffer")); + cERROR(1,("No response for cmd %d mid %d", + midQ->command, midQ->mid)); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; |