X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fnfs%2Fread.c;h=12c9e66d3f1d9739147573ecd00cbc703f9f70a8;hb=2da3e160cb3d226d87b907fab26850d838ed8d7c;hp=90df0500ca1b1f89b7e08f6b91c7e9c49c91b292;hpb=5529680981807b44abf3be30fb6d612ff04f68ff;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 90df050..12c9e66 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -5,17 +5,8 @@ * * Partial copy of Linus' read cache modifications to fs/nfs/file.c * modified for async RPC by okir@monad.swb.de - * - * We do an ugly hack here in order to return proper error codes to the - * user program when a read request failed: since generic_file_read - * only checks the return value of inode->i_op->readpage() which is always 0 - * for async RPC, we set the error bit of the page to 1 when an error occurs, - * and make nfs_readpage transmit requests synchronously when encountering this. - * This is only a small problem, though, since we now retry all operations - * within the RPC code when root squashing is suspected. */ -#include #include #include #include @@ -27,142 +18,107 @@ #include #include #include -#include #include +#include "nfs4_fs.h" +#include "internal.h" +#include "iostat.h" +#include "fscache.h" + #define NFSDBG_FACILITY NFSDBG_PAGECACHE -static int nfs_pagein_one(struct list_head *, struct inode *); -static void nfs_readpage_result_partial(struct nfs_read_data *, int); -static void nfs_readpage_result_full(struct nfs_read_data *, int); +static int nfs_pagein_multi(struct inode *, struct list_head *, unsigned int, size_t, int); +static int nfs_pagein_one(struct inode *, struct list_head *, unsigned int, size_t, int); +static const struct rpc_call_ops nfs_read_partial_ops; +static const struct rpc_call_ops nfs_read_full_ops; -static kmem_cache_t *nfs_rdata_cachep; -mempool_t *nfs_rdata_mempool; +static struct kmem_cache *nfs_rdata_cachep; +static mempool_t *nfs_rdata_mempool; #define MIN_POOL_READ (32) -void nfs_readdata_release(struct rpc_task *task) +struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) { - struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; - nfs_readdata_free(data); + struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS); + + if (p) { + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->pages); + p->npages = pagecount; + p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; + if (pagecount <= ARRAY_SIZE(p->page_array)) + p->pagevec = p->page_array; + else { + p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); + if (!p->pagevec) { + mempool_free(p, nfs_rdata_mempool); + p = NULL; + } + } + } + return p; } -static -unsigned int nfs_page_length(struct inode *inode, struct page *page) +void nfs_readdata_free(struct nfs_read_data *p) { - loff_t i_size = i_size_read(inode); - unsigned long idx; - - if (i_size <= 0) - return 0; - idx = (i_size - 1) >> PAGE_CACHE_SHIFT; - if (page->index > idx) - return 0; - if (page->index != idx) - return PAGE_CACHE_SIZE; - return 1 + ((i_size - 1) & (PAGE_CACHE_SIZE - 1)); + if (p && (p->pagevec != &p->page_array[0])) + kfree(p->pagevec); + mempool_free(p, nfs_rdata_mempool); +} + +static void nfs_readdata_release(struct nfs_read_data *rdata) +{ + put_nfs_open_context(rdata->args.context); + nfs_readdata_free(rdata); } static int nfs_return_empty_page(struct page *page) { - memclear_highpage_flush(page, 0, PAGE_CACHE_SIZE); + zero_user(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); unlock_page(page); return 0; } -/* - * Read a page synchronously. - */ -static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode, - struct page *page) +static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data) { - unsigned int rsize = NFS_SERVER(inode)->rsize; - unsigned int count = PAGE_CACHE_SIZE; - int result; - struct nfs_read_data *rdata; - - rdata = nfs_readdata_alloc(); - if (!rdata) - return -ENOMEM; - - memset(rdata, 0, sizeof(*rdata)); - rdata->flags = (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); - rdata->cred = ctx->cred; - rdata->inode = inode; - INIT_LIST_HEAD(&rdata->pages); - rdata->args.fh = NFS_FH(inode); - rdata->args.context = ctx; - rdata->args.pages = &page; - rdata->args.pgbase = 0UL; - rdata->args.count = rsize; - rdata->res.fattr = &rdata->fattr; - - dprintk("NFS: nfs_readpage_sync(%p)\n", page); + unsigned int remainder = data->args.count - data->res.count; + unsigned int base = data->args.pgbase + data->res.count; + unsigned int pglen; + struct page **pages; + if (data->res.eof == 0 || remainder == 0) + return; /* - * This works now because the socket layer never tries to DMA - * into this buffer directly. + * Note: "remainder" can never be negative, since we check for + * this in the XDR code. */ - do { - if (count < rsize) - rdata->args.count = count; - rdata->res.count = rdata->args.count; - rdata->args.offset = page_offset(page) + rdata->args.pgbase; - - dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Lu, %u)\n", - NFS_SERVER(inode)->hostname, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - (unsigned long long)rdata->args.pgbase, - rdata->args.count); - - lock_kernel(); - result = NFS_PROTO(inode)->read(rdata); - unlock_kernel(); - - /* - * Even if we had a partial success we can't mark the page - * cache valid. - */ - if (result < 0) { - if (result == -EISDIR) - result = -EINVAL; - goto io_error; - } - count -= result; - rdata->args.pgbase += result; - /* Note: result == 0 should only happen if we're caching - * a write that extends the file and punches a hole. - */ - if (rdata->res.eof != 0 || result == 0) + pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; + base &= ~PAGE_CACHE_MASK; + pglen = PAGE_CACHE_SIZE - base; + for (;;) { + if (remainder <= pglen) { + zero_user(*pages, base, remainder); break; - } while (count); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME; - - if (count) - memclear_highpage_flush(page, rdata->args.pgbase, count); - SetPageUptodate(page); - if (PageError(page)) - ClearPageError(page); - result = 0; - -io_error: - unlock_page(page); - nfs_readdata_free(rdata); - return result; + } + zero_user(*pages, base, pglen); + pages++; + remainder -= pglen; + pglen = PAGE_CACHE_SIZE; + base = 0; + } } -static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, - struct page *page) +int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, + struct page *page) { LIST_HEAD(one_request); struct nfs_page *new; unsigned int len; - len = nfs_page_length(inode, page); + len = nfs_page_length(page); if (len == 0) return nfs_return_empty_page(page); new = nfs_create_request(ctx, inode, page, 0, len); @@ -171,63 +127,90 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, return PTR_ERR(new); } if (len < PAGE_CACHE_SIZE) - memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); + zero_user_segment(page, len, PAGE_CACHE_SIZE); nfs_list_add_request(new, &one_request); - nfs_pagein_one(&one_request, inode); + if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) + nfs_pagein_multi(inode, &one_request, 1, len, 0); + else + nfs_pagein_one(inode, &one_request, 1, len, 0); return 0; } static void nfs_readpage_release(struct nfs_page *req) { - unlock_page(req->wb_page); + struct inode *d_inode = req->wb_context->path.dentry->d_inode; - nfs_clear_request(req); - nfs_release_request(req); + if (PageUptodate(req->wb_page)) + nfs_readpage_to_fscache(d_inode, req->wb_page, 0); + + unlock_page(req->wb_page); dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), + req->wb_context->path.dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); + nfs_clear_request(req); + nfs_release_request(req); } /* * Set up the NFS read request struct */ -static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, +static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, + const struct rpc_call_ops *call_ops, unsigned int count, unsigned int offset) { - struct inode *inode; + struct inode *inode = req->wb_context->path.dentry->d_inode; + int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0; + struct rpc_task *task; + struct rpc_message msg = { + .rpc_argp = &data->args, + .rpc_resp = &data->res, + .rpc_cred = req->wb_context->cred, + }; + struct rpc_task_setup task_setup_data = { + .task = &data->task, + .rpc_client = NFS_CLIENT(inode), + .rpc_message = &msg, + .callback_ops = call_ops, + .callback_data = data, + .workqueue = nfsiod_workqueue, + .flags = RPC_TASK_ASYNC | swap_flags, + }; data->req = req; - data->inode = inode = req->wb_context->dentry->d_inode; - data->cred = req->wb_context->cred; + data->inode = inode; + data->cred = msg.rpc_cred; data->args.fh = NFS_FH(inode); data->args.offset = req_offset(req) + offset; data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; - data->args.context = req->wb_context; + data->args.context = get_nfs_open_context(req->wb_context); data->res.fattr = &data->fattr; data->res.count = count; data->res.eof = 0; + nfs_fattr_init(&data->fattr); - NFS_PROTO(inode)->read_setup(data); + /* Set up the initial task struct. */ + NFS_PROTO(inode)->read_setup(data, &msg); - data->task.tk_cookie = (unsigned long)inode; - data->task.tk_calldata = data; - /* Release requests */ - data->task.tk_release = nfs_readdata_release; - - dprintk("NFS: %4d initiated read call (req %s/%Ld, %u bytes @ offset %Lu)\n", + dprintk("NFS: %5u initiated read call (req %s/%Ld, %u bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), count, (unsigned long long)data->args.offset); + + task = rpc_run_task(&task_setup_data); + if (IS_ERR(task)) + return PTR_ERR(task); + rpc_put_task(task); + return 0; } static void @@ -244,21 +227,6 @@ nfs_async_read_error(struct list_head *head) } /* - * Start an async read operation - */ -static void nfs_execute_read(struct nfs_read_data *data) -{ - struct rpc_clnt *clnt = NFS_CLIENT(data->inode); - sigset_t oldset; - - rpc_clnt_sigmask(clnt, &oldset); - lock_kernel(); - rpc_execute(&data->task); - unlock_kernel(); - rpc_clnt_sigunmask(clnt, &oldset); -} - -/* * Generate multiple requests to fill a single page. * * We optimize to reduce the number of read operations on the wire. If we @@ -271,54 +239,54 @@ static void nfs_execute_read(struct nfs_read_data *data) * won't see the new data until our attribute cache is updated. This is more * or less conventional NFS client behavior. */ -static int nfs_pagein_multi(struct list_head *head, struct inode *inode) +static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags) { struct nfs_page *req = nfs_list_entry(head->next); struct page *page = req->wb_page; struct nfs_read_data *data; - unsigned int rsize = NFS_SERVER(inode)->rsize; - unsigned int nbytes, offset; + size_t rsize = NFS_SERVER(inode)->rsize, nbytes; + unsigned int offset; int requests = 0; + int ret = 0; LIST_HEAD(list); nfs_list_remove_request(req); - nbytes = req->wb_bytes; - for(;;) { - data = nfs_readdata_alloc(); + nbytes = count; + do { + size_t len = min(nbytes,rsize); + + data = nfs_readdata_alloc(1); if (!data) goto out_bad; - INIT_LIST_HEAD(&data->pages); list_add(&data->pages, &list); requests++; - if (nbytes <= rsize) - break; - nbytes -= rsize; - } + nbytes -= len; + } while(nbytes != 0); atomic_set(&req->wb_complete, requests); ClearPageError(page); offset = 0; - nbytes = req->wb_bytes; + nbytes = count; do { + int ret2; + data = list_entry(list.next, struct nfs_read_data, pages); list_del_init(&data->pages); data->pagevec[0] = page; - data->complete = nfs_readpage_result_partial; - - if (nbytes > rsize) { - nfs_read_rpcsetup(req, data, rsize, offset); - offset += rsize; - nbytes -= rsize; - } else { - nfs_read_rpcsetup(req, data, nbytes, offset); - nbytes = 0; - } - nfs_execute_read(data); + + if (nbytes < rsize) + rsize = nbytes; + ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, + rsize, offset); + if (ret == 0) + ret = ret2; + offset += rsize; + nbytes -= rsize; } while (nbytes != 0); - return 0; + return ret; out_bad: while (!list_empty(&list)) { @@ -331,83 +299,108 @@ out_bad: return -ENOMEM; } -static int nfs_pagein_one(struct list_head *head, struct inode *inode) +static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags) { struct nfs_page *req; struct page **pages; struct nfs_read_data *data; - unsigned int count; + int ret = -ENOMEM; - if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) - return nfs_pagein_multi(head, inode); - - data = nfs_readdata_alloc(); + data = nfs_readdata_alloc(npages); if (!data) goto out_bad; - INIT_LIST_HEAD(&data->pages); pages = data->pagevec; - count = 0; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_list_add_request(req, &data->pages); ClearPageError(req->wb_page); *pages++ = req->wb_page; - count += req->wb_bytes; } req = nfs_list_entry(data->pages.next); - data->complete = nfs_readpage_result_full; - nfs_read_rpcsetup(req, data, count, 0); - - nfs_execute_read(data); - return 0; + return nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0); out_bad: nfs_async_read_error(head); - return -ENOMEM; + return ret; } -static int -nfs_pagein_list(struct list_head *head, int rpages) +/* + * This is the callback from RPC telling us whether a reply was + * received or some error occurred (timeout or socket shutdown). + */ +int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) { - LIST_HEAD(one_request); - struct nfs_page *req; - int error = 0; - unsigned int pages = 0; + int status; - 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_context->dentry->d_inode); - if (error < 0) - break; + dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, + task->tk_status); + + status = NFS_PROTO(data->inode)->read_done(task, data); + if (status != 0) + return status; + + nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, data->res.count); + + if (task->tk_status == -ESTALE) { + set_bit(NFS_INO_STALE, &NFS_I(data->inode)->flags); + nfs_mark_for_revalidate(data->inode); } - if (error >= 0) - return pages; + return 0; +} + +static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) +{ + struct nfs_readargs *argp = &data->args; + struct nfs_readres *resp = &data->res; + + if (resp->eof || resp->count == argp->count) + goto out; + + /* This is a short read! */ + nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); + /* Has the server at least made some progress? */ + if (resp->count == 0) + goto out; + + /* Yes, so retry the read at the end of the data */ + argp->offset += resp->count; + argp->pgbase += resp->count; + argp->count -= resp->count; + nfs4_restart_rpc(task, NFS_SERVER(data->inode)->nfs_client); + return; +out: + nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client, + &data->res.seq_res); + return; - nfs_async_read_error(head); - return error; } /* * Handle a read reply that fills part of a page. */ -static void nfs_readpage_result_partial(struct nfs_read_data *data, int status) +static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) { + struct nfs_read_data *data = calldata; + + if (nfs_readpage_result(task, data) != 0) + return; + if (task->tk_status < 0) + return; + + nfs_readpage_truncate_uninitialised_page(data); + nfs_readpage_retry(task, data); +} + +static void nfs_readpage_release_partial(void *calldata) +{ + struct nfs_read_data *data = calldata; struct nfs_page *req = data->req; struct page *page = req->wb_page; - - if (status >= 0) { - unsigned int request = data->args.count; - unsigned int result = data->res.count; - - if (result < request) { - memclear_highpage_flush(page, - data->args.pgbase + result, - request - result); - } - } else + int status = data->task.tk_status; + + if (status < 0) SetPageError(page); if (atomic_dec_and_test(&req->wb_complete)) { @@ -415,67 +408,95 @@ static void nfs_readpage_result_partial(struct nfs_read_data *data, int status) SetPageUptodate(page); nfs_readpage_release(req); } + nfs_readdata_release(calldata); +} + +#if defined(CONFIG_NFS_V4_1) +void nfs_read_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs_read_data *data = calldata; + + if (nfs4_setup_sequence(NFS_SERVER(data->inode)->nfs_client, + &data->args.seq_args, &data->res.seq_res, + 0, task)) + return; + rpc_call_start(task); +} +#endif /* CONFIG_NFS_V4_1 */ + +static const struct rpc_call_ops nfs_read_partial_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_read_prepare, +#endif /* CONFIG_NFS_V4_1 */ + .rpc_call_done = nfs_readpage_result_partial, + .rpc_release = nfs_readpage_release_partial, +}; + +static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data) +{ + unsigned int count = data->res.count; + unsigned int base = data->args.pgbase; + struct page **pages; + + if (data->res.eof) + count = data->args.count; + if (unlikely(count == 0)) + return; + pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; + base &= ~PAGE_CACHE_MASK; + count += base; + for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++) + SetPageUptodate(*pages); + if (count == 0) + return; + /* Was this a short read? */ + if (data->res.eof || data->res.count == data->args.count) + SetPageUptodate(*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_full(struct nfs_read_data *data, int status) +static void nfs_readpage_result_full(struct rpc_task *task, void *calldata) { - unsigned int count = data->res.count; + struct nfs_read_data *data = calldata; + + if (nfs_readpage_result(task, data) != 0) + return; + if (task->tk_status < 0) + return; + /* + * Note: nfs_readpage_retry may change the values of + * data->args. In the multi-page case, we therefore need + * to ensure that we call nfs_readpage_set_pages_uptodate() + * first. + */ + nfs_readpage_truncate_uninitialised_page(data); + nfs_readpage_set_pages_uptodate(data); + nfs_readpage_retry(task, data); +} + +static void nfs_readpage_release_full(void *calldata) +{ + struct nfs_read_data *data = calldata; 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 (status >= 0) { - if (count < PAGE_CACHE_SIZE) { - if (count < req->wb_bytes) - memclear_highpage_flush(page, - req->wb_pgbase + count, - req->wb_bytes - count); - count = 0; - } else - count -= PAGE_CACHE_SIZE; - SetPageUptodate(page); - } else - SetPageError(page); + nfs_list_remove_request(req); nfs_readpage_release(req); } + nfs_readdata_release(calldata); } -/* - * This is the callback from RPC telling us whether a reply was - * received or some error occurred (timeout or socket shutdown). - */ -void nfs_readpage_result(struct rpc_task *task) -{ - struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; - struct nfs_readargs *argp = &data->args; - struct nfs_readres *resp = &data->res; - int status = task->tk_status; - - dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", - task->tk_pid, status); - - /* Is this a short read? */ - if (task->tk_status >= 0 && resp->count < argp->count && !resp->eof) { - /* Has the server at least made some progress? */ - if (resp->count != 0) { - /* Yes, so retry the read at the end of the data */ - argp->offset += resp->count; - argp->pgbase += resp->count; - argp->count -= resp->count; - rpc_restart_call(task); - return; - } - task->tk_status = -EIO; - } - NFS_I(data->inode)->cache_validity |= NFS_INO_INVALID_ATIME; - data->complete(data, status); -} +static const struct rpc_call_ops nfs_read_full_ops = { +#if defined(CONFIG_NFS_V4_1) + .rpc_call_prepare = nfs_read_prepare, +#endif /* CONFIG_NFS_V4_1 */ + .rpc_call_done = nfs_readpage_result_full, + .rpc_release = nfs_readpage_release_full, +}; /* * Read a page over NFS. @@ -491,6 +512,9 @@ int nfs_readpage(struct file *file, struct page *page) dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", page, PAGE_CACHE_SIZE, page->index); + nfs_inc_stats(inode, NFSIOS_VFSREADPAGE); + nfs_add_stats(inode, NFSIOS_READPAGES, 1); + /* * Try to flush any pending writes to the file.. * @@ -500,34 +524,40 @@ int nfs_readpage(struct file *file, struct page *page) */ error = nfs_wb_page(inode, page); if (error) - goto out_error; + goto out_unlock; + if (PageUptodate(page)) + goto out_unlock; + + error = -ESTALE; + if (NFS_STALE(inode)) + goto out_unlock; if (file == NULL) { - ctx = nfs_find_open_context(inode, FMODE_READ); + error = -EBADF; + ctx = nfs_find_open_context(inode, NULL, FMODE_READ); if (ctx == NULL) - return -EBADF; + goto out_unlock; } else - ctx = get_nfs_open_context((struct nfs_open_context *) - file->private_data); + ctx = get_nfs_open_context(nfs_file_open_context(file)); + if (!IS_SYNC(inode)) { - error = nfs_readpage_async(ctx, inode, page); - goto out; + error = nfs_readpage_from_fscache(ctx, inode, page); + if (error == 0) + goto out; } - error = nfs_readpage_sync(ctx, inode, page); - if (error < 0 && IS_SWAPFILE(inode)) - printk("Aiee.. nfs swap-in of page failed!\n"); + error = nfs_readpage_async(ctx, inode, page); + out: put_nfs_open_context(ctx); return error; - -out_error: +out_unlock: unlock_page(page); return error; } struct nfs_readdesc { - struct list_head *head; + struct nfs_pageio_descriptor *pgio; struct nfs_open_context *ctx; }; @@ -538,69 +568,95 @@ readpage_async_filler(void *data, struct page *page) struct inode *inode = page->mapping->host; struct nfs_page *new; unsigned int len; + int error; - nfs_wb_page(inode, page); - len = nfs_page_length(inode, page); + len = nfs_page_length(page); if (len == 0) return nfs_return_empty_page(page); + new = nfs_create_request(desc->ctx, inode, page, 0, len); - if (IS_ERR(new)) { - SetPageError(page); - unlock_page(page); - return PTR_ERR(new); - } + if (IS_ERR(new)) + goto out_error; + if (len < PAGE_CACHE_SIZE) - memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); - nfs_list_add_request(new, desc->head); + zero_user_segment(page, len, PAGE_CACHE_SIZE); + if (!nfs_pageio_add_request(desc->pgio, new)) { + error = desc->pgio->pg_error; + goto out_unlock; + } return 0; +out_error: + error = PTR_ERR(new); + SetPageError(page); +out_unlock: + unlock_page(page); + return error; } int nfs_readpages(struct file *filp, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { - LIST_HEAD(head); + struct nfs_pageio_descriptor pgio; struct nfs_readdesc desc = { - .head = &head, + .pgio = &pgio, }; struct inode *inode = mapping->host; struct nfs_server *server = NFS_SERVER(inode); - int ret; + size_t rsize = server->rsize; + unsigned long npages; + int ret = -ESTALE; dprintk("NFS: nfs_readpages (%s/%Ld %d)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode), nr_pages); + nfs_inc_stats(inode, NFSIOS_VFSREADPAGES); + + if (NFS_STALE(inode)) + goto out; if (filp == NULL) { - desc.ctx = nfs_find_open_context(inode, FMODE_READ); + desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ); if (desc.ctx == NULL) return -EBADF; } else - desc.ctx = get_nfs_open_context((struct nfs_open_context *) - filp->private_data); + desc.ctx = get_nfs_open_context(nfs_file_open_context(filp)); + + /* attempt to read as many of the pages as possible from the cache + * - this returns -ENOBUFS immediately if the cookie is negative + */ + ret = nfs_readpages_from_fscache(desc.ctx, inode, mapping, + pages, &nr_pages); + if (ret == 0) + goto read_complete; /* all pages were read */ + + if (rsize < PAGE_CACHE_SIZE) + nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0); + else + nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0); + ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); - if (!list_empty(&head)) { - int err = nfs_pagein_list(&head, server->rpages); - if (!ret) - ret = err; - } + + nfs_pageio_complete(&pgio); + npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + nfs_add_stats(inode, NFSIOS_READPAGES, npages); +read_complete: put_nfs_open_context(desc.ctx); +out: return ret; } -int nfs_init_readpagecache(void) +int __init nfs_init_readpagecache(void) { nfs_rdata_cachep = kmem_cache_create("nfs_read_data", sizeof(struct nfs_read_data), 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + NULL); if (nfs_rdata_cachep == NULL) return -ENOMEM; - nfs_rdata_mempool = mempool_create(MIN_POOL_READ, - mempool_alloc_slab, - mempool_free_slab, - nfs_rdata_cachep); + nfs_rdata_mempool = mempool_create_slab_pool(MIN_POOL_READ, + nfs_rdata_cachep); if (nfs_rdata_mempool == NULL) return -ENOMEM; @@ -610,6 +666,5 @@ int nfs_init_readpagecache(void) void nfs_destroy_readpagecache(void) { mempool_destroy(nfs_rdata_mempool); - if (kmem_cache_destroy(nfs_rdata_cachep)) - printk(KERN_INFO "nfs_read_data: not all structures were freed\n"); + kmem_cache_destroy(nfs_rdata_cachep); }