X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=fs%2Fnfs%2Fread.c;h=40d17987d0e847a87fa4596a9a4c9f39a464288e;hb=441dbb500b4344a8e3125e5aabab4f9dbf72514f;hp=be9e8270f4d7bdaceef2e132a43ddca84c66c81b;hpb=5e4424af9a1f062c6451681dff24a26e27741cc6;p=linux-2.6 diff --git a/fs/nfs/read.c b/fs/nfs/read.c index be9e8270f4..40d17987d0 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -153,7 +153,7 @@ static void nfs_readpage_release(struct nfs_page *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) { @@ -202,8 +202,10 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, (unsigned long long)data->args.offset); task = rpc_run_task(&task_setup_data); - if (!IS_ERR(task)) - rpc_put_task(task); + if (IS_ERR(task)) + return PTR_ERR(task); + rpc_put_task(task); + return 0; } static void @@ -240,6 +242,7 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne 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); @@ -251,7 +254,6 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne data = nfs_readdata_alloc(1); if (!data) goto out_bad; - INIT_LIST_HEAD(&data->pages); list_add(&data->pages, &list); requests++; nbytes -= len; @@ -262,6 +264,8 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne offset = 0; nbytes = count; do { + int ret2; + data = list_entry(list.next, struct nfs_read_data, pages); list_del_init(&data->pages); @@ -269,13 +273,15 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne if (nbytes < rsize) rsize = nbytes; - nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, + 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)) { @@ -293,12 +299,12 @@ static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned struct nfs_page *req; struct page **pages; struct nfs_read_data *data; + int ret = -ENOMEM; data = nfs_readdata_alloc(npages); if (!data) goto out_bad; - INIT_LIST_HEAD(&data->pages); pages = data->pagevec; while (!list_empty(head)) { req = nfs_list_entry(head->next); @@ -309,11 +315,10 @@ static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned } req = nfs_list_entry(data->pages.next); - nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0); - 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; } /* @@ -324,7 +329,7 @@ int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) { int status; - dprintk("NFS: %s: %5u, (status %d)\n", __FUNCTION__, task->tk_pid, + dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, task->tk_status); status = NFS_PROTO(data->inode)->read_done(task, data); @@ -340,26 +345,25 @@ int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) return 0; } -static int nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) +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) - return 0; + return; /* This is a short read! */ nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); /* Has the server at least made some progress? */ if (resp->count == 0) - return 0; + return; /* 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 -EAGAIN; } /* @@ -368,29 +372,37 @@ static int nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; - struct nfs_page *req = data->req; - struct page *page = req->wb_page; if (nfs_readpage_result(task, data) != 0) return; + if (task->tk_status < 0) + return; - if (likely(task->tk_status >= 0)) { - nfs_readpage_truncate_uninitialised_page(data); - if (nfs_readpage_retry(task, data) != 0) - return; - } - if (unlikely(task->tk_status < 0)) + 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; + int status = data->task.tk_status; + + if (status < 0) SetPageError(page); + if (atomic_dec_and_test(&req->wb_complete)) { if (!PageError(page)) SetPageUptodate(page); nfs_readpage_release(req); } + nfs_readdata_release(calldata); } static const struct rpc_call_ops nfs_read_partial_ops = { .rpc_call_done = nfs_readpage_result_partial, - .rpc_release = nfs_readdata_release, + .rpc_release = nfs_readpage_release_partial, }; static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data) @@ -425,29 +437,35 @@ static void nfs_readpage_result_full(struct rpc_task *task, void *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. */ - if (likely(task->tk_status >= 0)) { - nfs_readpage_truncate_uninitialised_page(data); - nfs_readpage_set_pages_uptodate(data); - if (nfs_readpage_retry(task, data) != 0) - return; - } + 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); nfs_list_remove_request(req); nfs_readpage_release(req); } + nfs_readdata_release(calldata); } static const struct rpc_call_ops nfs_read_full_ops = { .rpc_call_done = nfs_readpage_result_full, - .rpc_release = nfs_readdata_release, + .rpc_release = nfs_readpage_release_full, }; /* @@ -531,7 +549,10 @@ readpage_async_filler(void *data, struct page *page) if (len < PAGE_CACHE_SIZE) zero_user_segment(page, len, PAGE_CACHE_SIZE); - nfs_pageio_add_request(desc->pgio, new); + if (!nfs_pageio_add_request(desc->pgio, new)) { + error = desc->pgio->pg_error; + goto out_unlock; + } return 0; out_error: error = PTR_ERR(new);