diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-04-28 01:09:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-04-28 01:09:25 +0000 |
commit | b9ba7aeb165cffecdffb60aec8c3fa8d590d9ca9 (patch) | |
tree | 42d07b0c7246ae2536a702e7c5de9e2732341116 /fs/nfs | |
parent | 7406b0a326f2d70ade2671c37d1beef62249db97 (diff) |
Merge with 2.3.99-pre6.
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 2 | ||||
-rw-r--r-- | fs/nfs/file.c | 33 | ||||
-rw-r--r-- | fs/nfs/flushd.c | 4 | ||||
-rw-r--r-- | fs/nfs/inode.c | 28 | ||||
-rw-r--r-- | fs/nfs/mount_clnt.c | 16 | ||||
-rw-r--r-- | fs/nfs/nfsroot.c | 30 | ||||
-rw-r--r-- | fs/nfs/read.c | 485 | ||||
-rw-r--r-- | fs/nfs/write.c | 288 |
8 files changed, 575 insertions, 311 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 7d80e6468..2d2ee4a02 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -891,7 +891,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); dfprintk(VFS, "trying to rename %s to %s\n", dentry->d_name.name, silly); - sdentry = lookup_one(silly, dget(dentry->d_parent)); + sdentry = lookup_one(silly, dentry->d_parent); /* * N.B. Better to return EBUSY here ... it could be * dangerous to delete the file while it's in use. diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d5c3d0944..44e71719c 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -74,6 +74,11 @@ nfs_file_flush(struct file *file) dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino); + /* Make sure all async reads have been sent off. We don't bother + * waiting on them though... */ + if (file->f_mode & FMODE_READ) + nfs_pagein_inode(inode, 0, 0); + status = nfs_wb_file(inode, file); if (!status) { status = file->f_error; @@ -166,8 +171,36 @@ static int nfs_commit_write(struct file *file, struct page *page, unsigned offse return status; } +/* + * The following is used by wait_on_page(), generic_file_readahead() + * to initiate the completion of any page readahead operations. + */ +static int nfs_sync_page(struct page *page) +{ + struct inode *inode = (struct inode *)page->mapping->host; + unsigned long index = page_index(page); + unsigned int rpages, wpages; + int result; + + if (!inode) + return 0; + + rpages = NFS_SERVER(inode)->rpages; + result = nfs_pagein_inode(inode, index, rpages); + if (result < 0) + goto out_bad; + wpages = NFS_SERVER(inode)->wpages; + result = nfs_sync_file(inode, NULL, index, wpages, FLUSH_STABLE); + if (result < 0) + goto out_bad; + return 0; + out_bad: + return result; +} + struct address_space_operations nfs_file_aops = { readpage: nfs_readpage, + sync_page: nfs_sync_page, writepage: nfs_writepage, prepare_write: nfs_prepare_write, commit_write: nfs_commit_write diff --git a/fs/nfs/flushd.c b/fs/nfs/flushd.c index 800a42171..460d4eba9 100644 --- a/fs/nfs/flushd.c +++ b/fs/nfs/flushd.c @@ -250,16 +250,18 @@ nfs_flushd(struct rpc_task *task) NFS_FLAGS(inode) &= ~NFS_INO_FLUSH; if (flush) { + nfs_pagein_inode(inode, 0, 0); nfs_sync_file(inode, NULL, 0, 0, FLUSH_AGING); } else if (time_after(jiffies, NFS_NEXTSCAN(inode))) { NFS_NEXTSCAN(inode) = jiffies + NFS_WRITEBACK_LOCKDELAY; + nfs_pagein_timeout(inode); nfs_flush_timeout(inode, FLUSH_AGING); #ifdef CONFIG_NFS_V3 nfs_commit_timeout(inode, FLUSH_AGING); #endif } - if (nfs_have_writebacks(inode)) { + if (nfs_have_writebacks(inode) || nfs_have_read(inode)) { inode_append_flushd(inode); if (time_after(delay, NFS_NEXTSCAN(inode))) delay = NFS_NEXTSCAN(inode); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 56271c14b..2d71aa7b5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -101,9 +101,11 @@ nfs_read_inode(struct inode * inode) inode->i_rdev = 0; NFS_FILEID(inode) = 0; NFS_FSID(inode) = 0; + INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); + inode->u.nfs_i.nread = 0; inode->u.nfs_i.ndirty = 0; inode->u.nfs_i.ncommit = 0; inode->u.nfs_i.npages = 0; @@ -131,7 +133,7 @@ nfs_delete_inode(struct inode * inode) /* * The following can never actually happen... */ - if (nfs_have_writebacks(inode)) { + if (nfs_have_writebacks(inode) || nfs_have_read(inode)) { printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); } @@ -260,6 +262,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) struct nfs_fsinfo fsinfo; int tcp, version, maxlen; + memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb)); if (!data) goto out_miss_args; @@ -428,12 +431,20 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits); if (server->rsize > fsinfo.rtmax) server->rsize = fsinfo.rtmax; - if (server->rsize > PAGE_CACHE_SIZE) - server->rsize = PAGE_CACHE_SIZE; if (server->wsize > fsinfo.wtmax) server->wsize = fsinfo.wtmax; - if (server->wsize > NFS_WRITE_MAXIOV << PAGE_CACHE_SHIFT) - server->wsize = NFS_WRITE_MAXIOV << PAGE_CACHE_SHIFT; + + server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (server->rpages > NFS_READ_MAXIOV) { + server->rpages = NFS_READ_MAXIOV; + server->rsize = server->rpages << PAGE_CACHE_SHIFT; + } + + server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + if (server->wpages > NFS_WRITE_MAXIOV) { + server->wpages = NFS_WRITE_MAXIOV; + server->wsize = server->wpages << PAGE_CACHE_SHIFT; + } maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN; @@ -1145,6 +1156,8 @@ extern int nfs_init_fhcache(void); extern void nfs_destroy_fhcache(void); extern int nfs_init_nfspagecache(void); extern void nfs_destroy_nfspagecache(void); +extern int nfs_init_readpagecache(void); +extern int nfs_destroy_readpagecache(void); /* * Initialize NFS @@ -1162,6 +1175,10 @@ init_nfs_fs(void) if (err) return err; + err = nfs_init_readpagecache(); + if (err) + return err; + #ifdef CONFIG_PROC_FS rpc_proc_register(&nfs_rpcstat); #endif @@ -1186,6 +1203,7 @@ init_module(void) void cleanup_module(void) { + nfs_destroy_readpagecache(); nfs_destroy_nfspagecache(); nfs_destroy_fhcache(); #ifdef CONFIG_PROC_FS diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 0adfacd3e..980f90b98 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -120,10 +120,12 @@ xdr_encode_dirpath(struct rpc_rqst *req, u32 *p, const char *path) static int xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) { - memset((u8 *)res, 0, sizeof(*res)); + struct nfs_fh *fh = res->fh; + + memset((void *)fh, 0, sizeof(*fh)); if ((res->status = ntohl(*p++)) == 0) { - res->fh->size = NFS2_FHSIZE; - memcpy(res->fh->data, p, NFS2_FHSIZE); + fh->size = NFS2_FHSIZE; + memcpy(fh->data, p, NFS2_FHSIZE); } return 0; } @@ -131,12 +133,14 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) static int xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) { - memset((u8 *)res, 0, sizeof(*res)); + struct nfs_fh *fh = res->fh; + + memset((void *)fh, 0, sizeof(*fh)); if ((res->status = ntohl(*p++)) == 0) { int size = ntohl(*p++); if (size <= NFS3_FHSIZE) { - res->fh->size = size; - memcpy(res->fh->data, p, res->fh->size); + fh->size = size; + memcpy(fh->data, p, size); } else res->status = -EBADHANDLE; } diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index b632cf175..7c3a7a0cc 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -450,32 +450,16 @@ static int __init root_nfs_get_handle(void) return status; } - -/* - * Now actually mount the given directory. - */ -static int __init root_nfs_do_mount(struct super_block *sb) -{ - /* Pass the server address to NFS */ - set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port); - - /* Now (finally ;-)) read the super block for mounting */ - if (nfs_read_super(sb, &nfs_data, 1) == NULL) - return -1; - return 0; -} - - /* - * Get the NFS port numbers and file handle, and then read the super- - * block for mounting. + * Get the NFS port numbers and file handle, and return the prepared 'data' + * argument for ->read_super() if everything went OK. Return NULL otherwise. */ -int __init nfs_root_mount(struct super_block *sb) +void * __init nfs_root_data(void) { if (root_nfs_init() < 0 || root_nfs_ports() < 0 - || root_nfs_get_handle() < 0 - || root_nfs_do_mount(sb) < 0) - return -1; - return 0; + || root_nfs_get_handle() < 0) + return NULL; + set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port); + return (void*)&nfs_data; } diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 4cf51ec8d..aa01a2b64 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -25,6 +25,8 @@ #include <linux/pagemap.h> #include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> +#include <linux/nfs_page.h> +#include <linux/nfs_flushd.h> #include <linux/smp_lock.h> #include <asm/segment.h> @@ -32,54 +34,65 @@ #define NFSDBG_FACILITY NFSDBG_PAGECACHE -struct nfs_rreq { - struct inode * ra_inode; /* inode from which to read */ - struct page * ra_page; /* page to be read */ - struct nfs_readargs ra_args; /* XDR argument struct */ - struct nfs_readres ra_res; /* ... and result struct */ - struct nfs_fattr ra_fattr; /* fattr storage */ +struct nfs_read_data { + struct rpc_task task; + struct dentry *dentry; + struct rpc_cred *cred; + struct nfs_readargs args; /* XDR argument struct */ + struct nfs_readres res; /* ... and result struct */ + struct nfs_fattr fattr; /* fattr storage */ + struct list_head pages; /* Coalesced read requests */ }; +/* + * Local function declarations + */ +static void nfs_readpage_result(struct rpc_task *task); + /* Hack for future NFS swap support */ #ifndef IS_SWAPFILE # define IS_SWAPFILE(inode) (0) #endif +static kmem_cache_t *nfs_rdata_cachep = NULL; -/* - * Set up the NFS read request struct - */ -static inline void -nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh, - loff_t offset, void *buffer, unsigned int rsize) +static __inline__ struct nfs_read_data *nfs_readdata_alloc(void) { - req->ra_args.fh = fh; - req->ra_args.offset = offset; - req->ra_args.count = rsize; - req->ra_args.iov[0].iov_base = (void *)buffer; - req->ra_args.iov[0].iov_len = rsize; - req->ra_args.nriov = 1; - req->ra_fattr.valid = 0; - req->ra_res.fattr = &req->ra_fattr; - req->ra_res.count = rsize; - req->ra_res.eof = 0; + struct nfs_read_data *p; + p = kmem_cache_alloc(nfs_rdata_cachep, SLAB_NFS); + if (p) { + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->pages); + } + return p; } +static __inline__ void nfs_readdata_free(struct nfs_read_data *p) +{ + kmem_cache_free(nfs_rdata_cachep, p); +} + +static void nfs_readdata_release(struct rpc_task *task) +{ + struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; + nfs_readdata_free(data); +} /* * Read a page synchronously. */ static int -nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) +nfs_readpage_sync(struct dentry *dentry, struct page *page) { - struct nfs_rreq rqst; - struct rpc_message msg; + struct inode *inode = dentry->d_inode; + struct nfs_fattr fattr; loff_t offset = page_offset(page); char *buffer; int rsize = NFS_SERVER(inode)->rsize; - int result, refresh = 0; + int result; int count = PAGE_CACHE_SIZE; int flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0; + int eof; dprintk("NFS: nfs_readpage_sync(%p)\n", page); @@ -87,8 +100,7 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) * This works now because the socket layer never tries to DMA * into this buffer directly. */ - buffer = (char *) kmap(page); - + buffer = (char *) kmap(page); do { if (count < rsize) rsize = count; @@ -98,16 +110,11 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) dentry->d_parent->d_name.name, dentry->d_name.name, (long long)offset, rsize, buffer); - /* Set up arguments and perform rpc call */ - nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize); lock_kernel(); - msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ; - msg.rpc_argp = &rqst.ra_args; - msg.rpc_resp = &rqst.ra_res; - msg.rpc_cred = NULL; - result = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); + result = NFS_PROTO(inode)->read(dentry, &fattr, flags, offset, + rsize, buffer, &eof); unlock_kernel(); - nfs_refresh_inode(inode, &rqst.ra_fattr); + nfs_refresh_inode(inode, &fattr); /* * Even if we had a partial success we can't mark the page @@ -118,7 +125,6 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) result = -EINVAL; goto io_error; } - refresh = 1; count -= result; offset += result; buffer += result; @@ -128,6 +134,8 @@ nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page) memset(buffer, 0, count); SetPageUptodate(page); + if (PageError(page)) + ClearPageError(page); result = 0; io_error: @@ -136,92 +144,318 @@ io_error: return result; } +static inline struct nfs_page * +_nfs_find_read(struct inode *inode, struct page *page) +{ + struct list_head *head, *next; + + head = &inode->u.nfs_i.read; + next = head->next; + while (next != head) { + struct nfs_page *req = nfs_list_entry(next); + next = next->next; + if (page_index(req->wb_page) != page_index(page)) + continue; + req->wb_count++; + return req; + } + return NULL; +} + +static struct nfs_page * +nfs_find_read(struct inode *inode, struct page *page) +{ + struct nfs_page *req; + spin_lock(&nfs_wreq_lock); + req = _nfs_find_read(inode, page); + spin_unlock(&nfs_wreq_lock); + return req; +} + /* - * This is the callback from RPC telling us whether a reply was - * received or some error occurred (timeout or socket shutdown). + * Add a request to the inode's asynchronous read list. */ -static void -nfs_readpage_result(struct rpc_task *task) +static inline void +nfs_mark_request_read(struct nfs_page *req) { - struct nfs_rreq *req = (struct nfs_rreq *) task->tk_calldata; - struct page *page = req->ra_page; - char *address = req->ra_args.iov[0].iov_base; - int result = task->tk_status; - static int succ = 0, fail = 0; - - dprintk("NFS: %4d received callback for page %p, result %d\n", - task->tk_pid, address, result); - - nfs_refresh_inode(req->ra_inode, &req->ra_fattr); - if (result >= 0) { - result = req->ra_res.count; - if (result < PAGE_CACHE_SIZE) - memset(address + result, 0, PAGE_CACHE_SIZE - result); - SetPageUptodate(page); - succ++; - } else { - SetPageError(page); - fail++; - dprintk("NFS: %d successful reads, %d failures\n", succ, fail); + struct inode *inode = req->wb_dentry->d_inode; + + spin_lock(&nfs_wreq_lock); + if (list_empty(&req->wb_list)) { + nfs_list_add_request(req, &inode->u.nfs_i.read); + inode->u.nfs_i.nread++; } - kunmap(page); - UnlockPage(page); - page_cache_release(page); + spin_unlock(&nfs_wreq_lock); + /* + * NB: the call to inode_schedule_scan() must lie outside the + * spinlock since it can run flushd(). + */ + inode_schedule_scan(inode, req->wb_timeout); +} + +static int +nfs_readpage_async(struct dentry *dentry, struct page *page) +{ + struct inode *inode = dentry->d_inode; + struct nfs_page *req, *new = NULL; + int result; + + for (;;) { + result = 0; + if (Page_Uptodate(page)) + break; - kfree(req); + req = nfs_find_read(inode, page); + if (req) { + if (page != req->wb_page) { + nfs_release_request(req); + nfs_pagein_inode(inode, page_index(page), 0); + continue; + } + nfs_release_request(req); + break; + } + + if (new) { + nfs_lock_request(new); + new->wb_timeout = jiffies + NFS_READ_DELAY; + nfs_mark_request_read(new); + nfs_unlock_request(new); + new = NULL; + break; + } + + result = -ENOMEM; + new = nfs_create_request(dentry, page, 0, PAGE_CACHE_SIZE); + if (!new) + break; + } + + if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages || + page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) + nfs_pagein_inode(inode, 0, 0); + if (new) + nfs_release_request(new); + return result; +} + +/* + * Set up the NFS read request struct + */ +static void +nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data) +{ + struct nfs_page *req; + struct iovec *iov; + unsigned int count; + + iov = data->args.iov; + count = 0; + while (!list_empty(head)) { + struct nfs_page *req = nfs_list_entry(head->next); + nfs_list_remove_request(req); + nfs_list_add_request(req, &data->pages); + iov->iov_base = (void *)(kmap(req->wb_page) + req->wb_offset); + iov->iov_len = req->wb_bytes; + count += req->wb_bytes; + iov++; + data->args.nriov++; + } + req = nfs_list_entry(data->pages.next); + data->dentry = req->wb_dentry; + data->cred = req->wb_cred; + data->args.fh = NFS_FH(req->wb_dentry); + data->args.offset = page_offset(req->wb_page) + req->wb_offset; + data->args.count = count; + data->res.fattr = &data->fattr; + data->res.count = count; + data->res.eof = 0; } -static inline int -nfs_readpage_async(struct dentry *dentry, struct inode *inode, - struct page *page) +static void +nfs_async_read_error(struct list_head *head) { - struct rpc_message msg; - unsigned long address; - struct nfs_rreq *req; - int result = -1, flags; + struct nfs_page *req; + struct page *page; + + while (!list_empty(head)) { + req = nfs_list_entry(head->next); + page = req->wb_page; + nfs_list_remove_request(req); + SetPageError(page); + UnlockPage(page); + nfs_unlock_request(req); + nfs_release_request(req); + } +} - dprintk("NFS: nfs_readpage_async(%p)\n", page); - if (NFS_CONGESTED(inode)) - goto out_defer; +static int +nfs_pagein_one(struct list_head *head, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct rpc_task *task; + struct rpc_clnt *clnt = NFS_CLIENT(inode); + struct nfs_read_data *data; + struct rpc_message msg; + int flags; + sigset_t oldset; + + data = nfs_readdata_alloc(); + if (!data) + goto out_bad; + task = &data->task; /* N.B. Do we need to test? Never called for swapfile inode */ flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); - req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req)); - if (!req) - goto out_defer; - - address = kmap(page); - /* Initialize request */ - /* N.B. Will the dentry remain valid for life of request? */ - nfs_readreq_setup(req, NFS_FH(dentry), page_offset(page), - (void *) address, PAGE_CACHE_SIZE); - req->ra_inode = inode; - req->ra_page = page; /* count has been incremented by caller */ - /* Start the async call */ - dprintk("NFS: executing async READ request.\n"); + nfs_read_rpcsetup(head, data); + + /* Finalize the task. */ + rpc_init_task(task, clnt, nfs_readpage_result, flags); + task->tk_calldata = data; + /* Release requests */ + task->tk_release = nfs_readdata_release; +#ifdef CONFIG_NFS_V3 msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ; - msg.rpc_argp = &req->ra_args; - msg.rpc_resp = &req->ra_res; - msg.rpc_cred = NULL; - - result = rpc_call_async(NFS_CLIENT(inode), &msg, flags, - nfs_readpage_result, req); - if (result < 0) - goto out_free; - result = 0; -out: - return result; +#else + msg.rpc_proc = NFSPROC_READ; +#endif + msg.rpc_argp = &data->args; + msg.rpc_resp = &data->res; + msg.rpc_cred = data->cred; -out_defer: - dprintk("NFS: deferring async READ request.\n"); - goto out; -out_free: - dprintk("NFS: failed to enqueue async READ request.\n"); - kunmap(page); - kfree(req); - goto out; + /* Start the async call */ + dprintk("NFS: %4d initiated read call (req %s/%s count %d nriov %d.\n", + task->tk_pid, + dentry->d_parent->d_name.name, dentry->d_name.name, + data->args.count, data->args.nriov); + + rpc_clnt_sigmask(clnt, &oldset); + rpc_call_setup(task, &msg, 0); + rpc_execute(task); + rpc_clnt_sigunmask(clnt, &oldset); + return 0; +out_bad: + nfs_async_read_error(head); + return -ENOMEM; +} + +static int +nfs_pagein_list(struct inode *inode, struct list_head *head) +{ + LIST_HEAD(one_request); + struct nfs_page *req; + int error = 0; + unsigned int pages = 0, + rpages = NFS_SERVER(inode)->rpages; + + while (!list_empty(head)) { + pages += nfs_coalesce_requests(head, &one_request, rpages); + req = nfs_list_entry(one_request.next); + error = nfs_pagein_one(&one_request, req->wb_dentry); + if (error < 0) + break; + } + if (error >= 0) + return pages; + + nfs_async_read_error(head); + return error; +} + +static int +nfs_scan_read_timeout(struct inode *inode, struct list_head *dst) +{ + int pages; + spin_lock(&nfs_wreq_lock); + pages = nfs_scan_list_timeout(&inode->u.nfs_i.read, dst, inode); + inode->u.nfs_i.nread -= pages; + if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read)) + printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n"); + spin_unlock(&nfs_wreq_lock); + return pages; +} + +static int +nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) +{ + int res; + spin_lock(&nfs_wreq_lock); + res = nfs_scan_list(&inode->u.nfs_i.read, dst, NULL, idx_start, npages); + inode->u.nfs_i.nread -= res; + if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read)) + printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n"); + spin_unlock(&nfs_wreq_lock); + return res; +} + +int nfs_pagein_inode(struct inode *inode, unsigned long idx_start, + unsigned int npages) +{ + LIST_HEAD(head); + int res, + error = 0; + + res = nfs_scan_read(inode, &head, idx_start, npages); + if (res) + error = nfs_pagein_list(inode, &head); + if (error < 0) + return error; + return res; +} + +int nfs_pagein_timeout(struct inode *inode) +{ + LIST_HEAD(head); + int pages, + error = 0; + + pages = nfs_scan_read_timeout(inode, &head); + if (pages) + error = nfs_pagein_list(inode, &head); + if (error < 0) + return error; + return pages; +} + +/* + * This is the callback from RPC telling us whether a reply was + * received or some error occurred (timeout or socket shutdown). + */ +static void +nfs_readpage_result(struct rpc_task *task) +{ + struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; + struct dentry *dentry = data->dentry; + struct inode *inode = dentry->d_inode; + int count = data->res.count; + + dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", + task->tk_pid, task->tk_status); + + nfs_refresh_inode(inode, &data->fattr); + while (!list_empty(&data->pages)) { + struct nfs_page *req = nfs_list_entry(data->pages.next); + struct page *page = req->wb_page; + nfs_list_remove_request(req); + + if (task->tk_status >= 0 && count >= 0) { + SetPageUptodate(page); + count -= PAGE_CACHE_SIZE; + } else + SetPageError(page); + kunmap(page); + UnlockPage(page); + + dprintk("NFS: read (%s/%s %d@%Ld)\n", + req->wb_dentry->d_parent->d_name.name, + req->wb_dentry->d_name.name, + req->wb_bytes, + (long long)(page_offset(page) + req->wb_offset)); + nfs_unlock_request(req); + nfs_release_request(req); + } } /* @@ -242,11 +476,8 @@ nfs_readpage(struct dentry *dentry, struct page *page) struct inode *inode = dentry->d_inode; int error; - lock_kernel(); dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", page, PAGE_CACHE_SIZE, page->index); - get_page(page); - /* * Try to flush any pending writes to the file.. * @@ -259,22 +490,36 @@ nfs_readpage(struct dentry *dentry, struct page *page) goto out_error; error = -1; - if (!IS_SWAPFILE(inode) && !PageError(page) && - NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) - error = nfs_readpage_async(dentry, inode, page); + if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) + error = nfs_readpage_async(dentry, page); if (error >= 0) goto out; - error = nfs_readpage_sync(dentry, inode, page); + error = nfs_readpage_sync(dentry, page); if (error < 0 && IS_SWAPFILE(inode)) printk("Aiee.. nfs swap-in of page failed!\n"); - goto out_free; +out: + return error; out_error: UnlockPage(page); -out_free: - page_cache_release(page); -out: - unlock_kernel(); - return error; + goto out; +} + +int nfs_init_readpagecache(void) +{ + nfs_rdata_cachep = kmem_cache_create("nfs_read_data", + sizeof(struct nfs_read_data), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (nfs_rdata_cachep == NULL) + return -ENOMEM; + + return 0; +} + +void nfs_destroy_readpagecache(void) +{ + if (kmem_cache_destroy(nfs_rdata_cachep)) + printk(KERN_INFO "nfs_read_data: not all structures were freed\n"); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 651251548..5ca6430aa 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -57,6 +57,7 @@ #include <linux/nfs_fs.h> #include <linux/nfs_mount.h> #include <linux/nfs_flushd.h> +#include <linux/nfs_page.h> #include <asm/uaccess.h> #include <linux/smp_lock.h> @@ -72,16 +73,11 @@ static unsigned int nfs_nr_requests = 0; /* * Local structures * - * Valid flags for a dirty buffer - */ -#define PG_BUSY 0x0001 - -/* * This is the struct where the WRITE/COMMIT arguments go. */ struct nfs_write_data { struct rpc_task task; - struct file *file; + struct dentry *dentry; struct rpc_cred *cred; struct nfs_writeargs args; /* argument struct */ struct nfs_writeres res; /* result struct */ @@ -90,27 +86,13 @@ struct nfs_write_data { struct list_head pages; /* Coalesced requests we wish to flush */ }; -struct nfs_page { - struct list_head wb_hash, /* Inode */ - wb_list, - *wb_list_head; - struct file *wb_file; - struct rpc_cred *wb_cred; - struct page *wb_page; /* page to write out */ - wait_queue_head_t wb_wait; /* wait queue */ - unsigned long wb_timeout; /* when to write/commit */ - unsigned int wb_offset, /* Offset of write */ - wb_bytes, /* Length of request */ - wb_count, /* reference count */ - wb_flags; - struct nfs_writeverf wb_verf; /* Commit cookie */ -}; - -#define NFS_WBACK_BUSY(req) ((req)->wb_flags & PG_BUSY) - /* * Local function declarations */ +static struct nfs_page * nfs_update_request(struct file*, struct dentry *, + struct page *page, + unsigned int, unsigned int); +static void nfs_strategy(struct inode *inode); static void nfs_writeback_done(struct rpc_task *); #ifdef CONFIG_NFS_V3 static void nfs_commit_done(struct rpc_task *); @@ -186,9 +168,10 @@ nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr) * Offset is the data offset within the page. */ static int -nfs_writepage_sync(struct dentry *dentry, struct inode *inode, - struct page *page, unsigned long offset, unsigned int count) +nfs_writepage_sync(struct dentry *dentry, struct page *page, + unsigned int offset, unsigned int count) { + struct inode *inode = dentry->d_inode; loff_t base; unsigned int wsize = NFS_SERVER(inode)->wsize; int result, refresh = 0, written = 0, flags; @@ -197,9 +180,9 @@ nfs_writepage_sync(struct dentry *dentry, struct inode *inode, struct nfs_writeverf verf; lock_kernel(); - dprintk("NFS: nfs_writepage_sync(%s/%s %d@%lu/%ld)\n", + dprintk("NFS: nfs_writepage_sync(%s/%s %d@%Ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, - count, page->index, offset); + count, (long long)(page_offset(page) + offset)); buffer = (u8 *) kmap(page) + offset; base = page_offset(page) + offset; @@ -235,6 +218,9 @@ nfs_writepage_sync(struct dentry *dentry, struct inode *inode, inode->i_size = base; } while (count); + if (PageError(page)) + ClearPageError(page); + io_error: kunmap(page); @@ -242,14 +228,28 @@ io_error: return written? written : result; } +static int +nfs_writepage_async(struct file *file, struct dentry *dentry, struct page *page, + unsigned int offset, unsigned int count) +{ + struct nfs_page *req; + int status; + + req = nfs_update_request(file, dentry, page, offset, count); + status = (IS_ERR(req)) ? PTR_ERR(req) : 0; + if (status < 0) + goto out; + nfs_release_request(req); + nfs_strategy(dentry->d_inode); + out: + return status; +} + /* - * Write a page to the server. This was supposed to be used for - * NFS swapping only. - * FIXME: Using this for mmap is pointless, breaks asynchronous - * writebacks, and is extremely slow. + * Write an mmapped page to the server. */ int -nfs_writepage(struct dentry * dentry, struct page *page) +nfs_writepage(struct file *file, struct dentry * dentry, struct page *page) { struct inode *inode = dentry->d_inode; unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT; @@ -265,9 +265,17 @@ nfs_writepage(struct dentry * dentry, struct page *page) if (page->index >= end_index+1 || !offset) return -EIO; do_it: - err = nfs_writepage_sync(dentry, inode, page, 0, offset); - if ( err == offset) return 0; + if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) { + err = nfs_writepage_async(file, dentry, page, 0, offset); + if (err >= 0) + goto out_ok; + } + err = nfs_writepage_sync(dentry, page, 0, offset); + if ( err == offset) + goto out_ok; return err; + out_ok: + return 0; } /* @@ -297,12 +305,6 @@ region_locked(struct inode *inode, struct nfs_page *req) return 0; } -static inline struct nfs_page * -nfs_inode_wb_entry(struct list_head *head) -{ - return list_entry(head, struct nfs_page, wb_hash); -} - /* * Insert a write request into an inode */ @@ -332,13 +334,13 @@ nfs_inode_remove_request(struct nfs_page *req) } if (!NFS_WBACK_BUSY(req)) printk(KERN_ERR "NFS: unlocked request attempted unhashed!\n"); - inode = req->wb_file->f_dentry->d_inode; + inode = req->wb_dentry->d_inode; list_del(&req->wb_hash); INIT_LIST_HEAD(&req->wb_hash); inode->u.nfs_i.npages--; if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n"); - if (!nfs_have_writebacks(inode)) + if (!nfs_have_writebacks(inode) && !nfs_have_read(inode)) inode_remove_flushd(inode); spin_unlock(&nfs_wreq_lock); nfs_release_request(req); @@ -365,7 +367,7 @@ _nfs_find_request(struct inode *inode, struct page *page) return NULL; } -struct nfs_page * +static struct nfs_page * nfs_find_request(struct inode *inode, struct page *page) { struct nfs_page *req; @@ -376,17 +378,10 @@ nfs_find_request(struct inode *inode, struct page *page) return req; } -static inline struct nfs_page * -nfs_list_entry(struct list_head *head) -{ - return list_entry(head, struct nfs_page, wb_list); -} - /* * Insert a write request into a sorted list */ -static inline void -nfs_list_add_request(struct nfs_page *req, struct list_head *head) +void nfs_list_add_request(struct nfs_page *req, struct list_head *head) { struct list_head *prev; @@ -394,10 +389,6 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head) printk(KERN_ERR "NFS: Add to list failed!\n"); return; } - if (list_empty(&req->wb_hash)) { - printk(KERN_ERR "NFS: Unhashed request attempted added to a list!\n"); - return; - } if (!NFS_WBACK_BUSY(req)) printk(KERN_ERR "NFS: unlocked request attempted added to list!\n"); prev = head->prev; @@ -414,8 +405,7 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head) /* * Insert a write request into an inode */ -static inline void -nfs_list_remove_request(struct nfs_page *req) +void nfs_list_remove_request(struct nfs_page *req) { if (list_empty(&req->wb_list)) return; @@ -432,7 +422,7 @@ nfs_list_remove_request(struct nfs_page *req) static inline void nfs_mark_request_dirty(struct nfs_page *req) { - struct inode *inode = req->wb_file->f_dentry->d_inode; + struct inode *inode = req->wb_dentry->d_inode; spin_lock(&nfs_wreq_lock); if (list_empty(&req->wb_list)) { @@ -453,7 +443,7 @@ nfs_mark_request_dirty(struct nfs_page *req) static inline int nfs_dirty_request(struct nfs_page *req) { - struct inode *inode = req->wb_file->f_dentry->d_inode; + struct inode *inode = req->wb_dentry->d_inode; return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty; } @@ -464,7 +454,7 @@ nfs_dirty_request(struct nfs_page *req) static inline void nfs_mark_request_commit(struct nfs_page *req) { - struct inode *inode = req->wb_file->f_dentry->d_inode; + struct inode *inode = req->wb_dentry->d_inode; spin_lock(&nfs_wreq_lock); if (list_empty(&req->wb_list)) { @@ -481,40 +471,15 @@ nfs_mark_request_commit(struct nfs_page *req) #endif /* - * Lock the page of an asynchronous request - */ -static inline int -nfs_lock_request(struct nfs_page *req) -{ - if (NFS_WBACK_BUSY(req)) - return 0; - req->wb_count++; - req->wb_flags |= PG_BUSY; - return 1; -} - -static inline void -nfs_unlock_request(struct nfs_page *req) -{ - if (!NFS_WBACK_BUSY(req)) { - printk(KERN_ERR "NFS: Invalid unlock attempted\n"); - return; - } - req->wb_flags &= ~PG_BUSY; - wake_up(&req->wb_wait); - nfs_release_request(req); -} - -/* * Create a write request. * Page must be locked by the caller. This makes sure we never create * two different requests for the same page, and avoids possible deadlock * when we reach the hard limit on the number of dirty pages. */ -static struct nfs_page * -nfs_create_request(struct inode *inode, struct file *file, struct page *page, - unsigned int offset, unsigned int count) +struct nfs_page *nfs_create_request(struct dentry *dentry, struct page *page, + unsigned int offset, unsigned int count) { + struct inode *inode = dentry->d_inode; struct nfs_reqlist *cache = NFS_REQUESTLIST(inode); struct nfs_page *req = NULL; long timeout; @@ -522,11 +487,7 @@ nfs_create_request(struct inode *inode, struct file *file, struct page *page, /* Deal with hard/soft limits. */ do { - /* If we're over the soft limit, flush out old requests */ - if (nfs_nr_requests >= MAX_REQUEST_SOFT) - nfs_wb_file(inode, file); - - /* If we're still over the soft limit, wake up some requests */ + /* If we're over the global soft limit, wake up all requests */ if (nfs_nr_requests >= MAX_REQUEST_SOFT) { dprintk("NFS: hit soft limit (%d requests)\n", nfs_nr_requests); @@ -535,9 +496,9 @@ nfs_create_request(struct inode *inode, struct file *file, struct page *page, nfs_wake_flushd(); } - /* If we haven't reached the hard limit yet, + /* If we haven't reached the local hard limit yet, * try to allocate the request struct */ - if (nfs_nr_requests < MAX_REQUEST_HARD) { + if (cache->nr_requests < MAX_REQUEST_HARD) { req = nfs_page_alloc(); if (req != NULL) break; @@ -545,7 +506,7 @@ nfs_create_request(struct inode *inode, struct file *file, struct page *page, /* We're over the hard limit. Wait for better times */ dprintk("NFS: create_request sleeping (total %d pid %d)\n", - nfs_nr_requests, current->pid); + cache->nr_requests, current->pid); timeout = 1 * HZ; if (NFS_SERVER(inode)->flags & NFS_MOUNT_INTR) { @@ -557,7 +518,7 @@ nfs_create_request(struct inode *inode, struct file *file, struct page *page, sleep_on_timeout(&cache->request_wait, timeout); dprintk("NFS: create_request waking up (tot %d pid %d)\n", - nfs_nr_requests, current->pid); + cache->nr_requests, current->pid); } while (!req); if (!req) return NULL; @@ -566,17 +527,11 @@ nfs_create_request(struct inode *inode, struct file *file, struct page *page, * long write-back delay. This will be adjusted in * update_nfs_request below if the region is not locked. */ req->wb_page = page; - atomic_inc(&page->count); + get_page(page); req->wb_offset = offset; req->wb_bytes = count; - /* If the region is locked, adjust the timeout */ - if (region_locked(inode, req)) - req->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY; - else - req->wb_timeout = jiffies + NFS_WRITEBACK_DELAY; - req->wb_file = file; + req->wb_dentry = dget(dentry); req->wb_cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); - get_file(file); req->wb_count = 1; /* register request's existence */ @@ -595,7 +550,7 @@ nfs_create_request(struct inode *inode, struct file *file, struct page *page, void nfs_release_request(struct nfs_page *req) { - struct inode *inode = req->wb_file->f_dentry->d_inode; + struct inode *inode = req->wb_dentry->d_inode; struct nfs_reqlist *cache = NFS_REQUESTLIST(inode); struct page *page = req->wb_page; @@ -618,7 +573,11 @@ nfs_release_request(struct nfs_page *req) printk(KERN_ERR "NFS: Request released while still locked!\n"); rpcauth_releasecred(NFS_CLIENT(inode)->cl_auth, req->wb_cred); - fput(req->wb_file); + lock_kernel(); + if (req->wb_file) + fput(req->wb_file); + dput(req->wb_dentry); + unlock_kernel(); page_cache_release(page); nfs_page_free(req); /* wake up anyone waiting to allocate a request */ @@ -635,7 +594,7 @@ nfs_release_request(struct nfs_page *req) static int nfs_wait_on_request(struct nfs_page *req) { - struct inode *inode = req->wb_file->f_dentry->d_inode; + struct inode *inode = req->wb_dentry->d_inode; struct rpc_clnt *clnt = NFS_CLIENT(inode); int retval; @@ -701,8 +660,7 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s * Scan cluster for dirty pages and send as many of them to the * server as possible. */ -static int -nfs_scan_list_timeout(struct list_head *head, struct list_head *dst, struct inode *inode) +int nfs_scan_list_timeout(struct list_head *head, struct list_head *dst, struct inode *inode) { struct list_head *p; struct nfs_page *req; @@ -754,8 +712,7 @@ nfs_scan_commit_timeout(struct inode *inode, struct list_head *dst) } #endif -static int -nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) +int nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) { struct list_head *p; struct nfs_page *req; @@ -819,8 +776,7 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, u #endif -static int -coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int maxpages) +int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int maxpages) { struct nfs_page *req = NULL; unsigned int pages = 0; @@ -832,7 +788,10 @@ coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int max if (prev) { if (req->wb_file != prev->wb_file) break; - + if (req->wb_dentry != prev->wb_dentry) + break; + if (req->wb_cred != prev->wb_cred) + break; if (page_index(req->wb_page) != page_index(prev->wb_page)+1) break; @@ -858,10 +817,10 @@ coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int max * Note: Should always be called with the Page Lock held! */ static struct nfs_page * -nfs_update_request(struct file* file, struct page *page, - unsigned long offset, unsigned int bytes) +nfs_update_request(struct file* file, struct dentry *dentry, struct page *page, + unsigned int offset, unsigned int bytes) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = dentry->d_inode; struct nfs_page *req, *new = NULL; unsigned long rqend, end; @@ -896,12 +855,26 @@ nfs_update_request(struct file* file, struct page *page, } spin_unlock(&nfs_wreq_lock); + /* Create the request. It's safe to sleep in this call because * we only get here if the page is locked. + * + * If we're over the soft limit, flush out old requests */ - new = nfs_create_request(inode, file, page, offset, bytes); + if (file && nfs_nr_requests >= MAX_REQUEST_SOFT) + nfs_wb_file(inode, file); + new = nfs_create_request(dentry, page, offset, bytes); if (!new) return ERR_PTR(-ENOMEM); + if (file) { + new->wb_file = file; + get_file(file); + } + /* If the region is locked, adjust the timeout */ + if (region_locked(inode, new)) + new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY; + else + new->wb_timeout = jiffies + NFS_WRITEBACK_DELAY; } /* We have a request for our page. @@ -956,31 +929,33 @@ nfs_update_request(struct file* file, struct page *page, */ #define NFS_STRATEGY_PAGES 8 static void -nfs_strategy(struct file *file) +nfs_strategy(struct inode *inode) { - struct inode *inode = file->f_dentry->d_inode; unsigned int dirty, wpages; dirty = inode->u.nfs_i.ndirty; - wpages = NFS_SERVER(inode)->wsize >> PAGE_CACHE_SHIFT; + wpages = NFS_SERVER(inode)->wpages; #ifdef CONFIG_NFS_V3 if (NFS_PROTO(inode)->version == 2) { if (dirty >= NFS_STRATEGY_PAGES * wpages) - nfs_flush_file(inode, file, 0, 0, 0); + nfs_flush_file(inode, NULL, 0, 0, 0); } else { if (dirty >= wpages) - nfs_flush_file(inode, file, 0, 0, 0); + nfs_flush_file(inode, NULL, 0, 0, 0); + if (inode->u.nfs_i.ncommit > NFS_STRATEGY_PAGES * wpages && + nfs_nr_requests > MAX_REQUEST_SOFT) + nfs_commit_file(inode, NULL, 0, 0, 0); } #else if (dirty >= NFS_STRATEGY_PAGES * wpages) - nfs_flush_file(inode, file, 0, 0, 0); + nfs_flush_file(inode, NULL, 0, 0, 0); #endif /* - * If we're running out of requests, flush out everything + * If we're running out of free requests, flush out everything * in order to reduce memory useage... */ - if (nfs_nr_requests > MAX_REQUEST_SOFT) - nfs_wb_file(inode, file); + if (inode->u.nfs_i.npages > MAX_REQUEST_SOFT) + nfs_wb_all(inode); } int @@ -1013,7 +988,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) * things with a page scheduled for an RPC call (e.g. invalidate it). */ int -nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count) +nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsigned int count) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; @@ -1030,7 +1005,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * page synchronously. */ if (NFS_SERVER(inode)->wsize < PAGE_SIZE) - return nfs_writepage_sync(dentry, inode, page, offset, count); + return nfs_writepage_sync(dentry, page, offset, count); /* * Try to find an NFS request corresponding to this page @@ -1039,7 +1014,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * it out now. */ do { - req = nfs_update_request(file, page, offset, count); + req = nfs_update_request(file, dentry, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status != -EBUSY) break; @@ -1066,7 +1041,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * of requests. */ if (req->wb_offset == 0 && req->wb_bytes == PAGE_CACHE_SIZE) - nfs_strategy(file); + nfs_strategy(inode); } nfs_release_request(req); done: @@ -1103,9 +1078,9 @@ nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data) data->args.nriov++; } req = nfs_list_entry(data->pages.next); - data->file = req->wb_file; + data->dentry = req->wb_dentry; data->cred = req->wb_cred; - data->args.fh = NFS_FH(req->wb_file->f_dentry); + data->args.fh = NFS_FH(req->wb_dentry); data->args.offset = page_offset(req->wb_page) + req->wb_offset; data->args.count = count; data->res.fattr = &data->fattr; @@ -1123,9 +1098,8 @@ nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data) * that has been written but not committed. */ static int -nfs_flush_one(struct list_head *head, struct file *file, int how) +nfs_flush_one(struct list_head *head, struct dentry *dentry, int how) { - struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_write_data *data; @@ -1198,12 +1172,12 @@ nfs_flush_list(struct inode *inode, struct list_head *head, int how) struct nfs_page *req; int error = 0; unsigned int pages = 0, - wpages = NFS_SERVER(inode)->wsize >> PAGE_CACHE_SHIFT; + wpages = NFS_SERVER(inode)->wpages; while (!list_empty(head)) { - pages += coalesce_requests(head, &one_request, wpages); + pages += nfs_coalesce_requests(head, &one_request, wpages); req = nfs_list_entry(one_request.next); - error = nfs_flush_one(&one_request, req->wb_file, how); + error = nfs_flush_one(&one_request, req->wb_dentry, how); if (error < 0) break; } @@ -1229,9 +1203,10 @@ nfs_writeback_done(struct rpc_task *task) struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; - struct dentry *dentry = data->file->f_dentry; + struct dentry *dentry = data->dentry; struct inode *inode = dentry->d_inode; struct nfs_page *req; + struct page *page; dprintk("NFS: %4d nfs_writeback_done (status %d)\n", task->tk_pid, task->tk_status); @@ -1276,17 +1251,21 @@ nfs_writeback_done(struct rpc_task *task) while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); + page = req->wb_page; - kunmap(req->wb_page); + kunmap(page); dprintk("NFS: write (%s/%s %d@%Ld)", - req->wb_file->f_dentry->d_parent->d_name.name, - req->wb_file->f_dentry->d_name.name, + req->wb_dentry->d_parent->d_name.name, + req->wb_dentry->d_name.name, req->wb_bytes, - (long long)(page_offset(req->wb_page) + req->wb_offset)); + (long long)(page_offset(page) + req->wb_offset)); if (task->tk_status < 0) { - req->wb_file->f_error = task->tk_status; + ClearPageUptodate(page); + SetPageError(page); + if (req->wb_file) + req->wb_file->f_error = task->tk_status; nfs_inode_remove_request(req); dprintk(", error = %d\n", task->tk_status); goto next; @@ -1329,9 +1308,9 @@ nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data) end = 0; start = ~0; req = nfs_list_entry(head->next); - data->file = req->wb_file; + dentry = req->wb_dentry; + data->dentry = dentry; data->cred = req->wb_cred; - dentry = data->file->f_dentry; inode = dentry->d_inode; while (!list_empty(head)) { struct nfs_page *req; @@ -1364,7 +1343,6 @@ static int nfs_commit_list(struct list_head *head, int how) { struct rpc_message msg; - struct file *file; struct rpc_clnt *clnt; struct nfs_write_data *data; struct rpc_task *task; @@ -1384,8 +1362,7 @@ nfs_commit_list(struct list_head *head, int how) /* Set up the argument struct */ nfs_commit_rpcsetup(head, data); req = nfs_list_entry(data->pages.next); - file = req->wb_file; - clnt = NFS_CLIENT(file->f_dentry->d_inode); + clnt = NFS_CLIENT(req->wb_dentry->d_inode); rpc_init_task(task, clnt, nfs_commit_done, flags); task->tk_calldata = data; @@ -1422,7 +1399,7 @@ nfs_commit_done(struct rpc_task *task) struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; struct nfs_writeres *resp = &data->res; struct nfs_page *req; - struct dentry *dentry = data->file->f_dentry; + struct dentry *dentry = data->dentry; struct inode *inode = dentry->d_inode; dprintk("NFS: %4d nfs_commit_done (status %d)\n", @@ -1434,12 +1411,13 @@ nfs_commit_done(struct rpc_task *task) nfs_list_remove_request(req); dprintk("NFS: commit (%s/%s %d@%Ld)", - req->wb_file->f_dentry->d_parent->d_name.name, - req->wb_file->f_dentry->d_name.name, + req->wb_dentry->d_parent->d_name.name, + req->wb_dentry->d_name.name, req->wb_bytes, (long long)(page_offset(req->wb_page) + req->wb_offset)); if (task->tk_status < 0) { - req->wb_file->f_error = task->tk_status; + if (req->wb_file) + req->wb_file->f_error = task->tk_status; nfs_inode_remove_request(req); dprintk(", error = %d\n", task->tk_status); goto next; |