diff options
Diffstat (limited to 'fs/nfs/read.c')
-rw-r--r-- | fs/nfs/read.c | 94 |
1 files changed, 55 insertions, 39 deletions
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 5a70be589bbe..16f57e0af999 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -58,22 +58,19 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) | |||
58 | return p; | 58 | return p; |
59 | } | 59 | } |
60 | 60 | ||
61 | static void nfs_readdata_rcu_free(struct rcu_head *head) | 61 | static void nfs_readdata_free(struct nfs_read_data *p) |
62 | { | 62 | { |
63 | struct nfs_read_data *p = container_of(head, struct nfs_read_data, task.u.tk_rcu); | ||
64 | if (p && (p->pagevec != &p->page_array[0])) | 63 | if (p && (p->pagevec != &p->page_array[0])) |
65 | kfree(p->pagevec); | 64 | kfree(p->pagevec); |
66 | mempool_free(p, nfs_rdata_mempool); | 65 | mempool_free(p, nfs_rdata_mempool); |
67 | } | 66 | } |
68 | 67 | ||
69 | static void nfs_readdata_free(struct nfs_read_data *rdata) | ||
70 | { | ||
71 | call_rcu_bh(&rdata->task.u.tk_rcu, nfs_readdata_rcu_free); | ||
72 | } | ||
73 | |||
74 | void nfs_readdata_release(void *data) | 68 | void nfs_readdata_release(void *data) |
75 | { | 69 | { |
76 | nfs_readdata_free(data); | 70 | struct nfs_read_data *rdata = data; |
71 | |||
72 | put_nfs_open_context(rdata->args.context); | ||
73 | nfs_readdata_free(rdata); | ||
77 | } | 74 | } |
78 | 75 | ||
79 | static | 76 | static |
@@ -156,7 +153,7 @@ static void nfs_readpage_release(struct nfs_page *req) | |||
156 | /* | 153 | /* |
157 | * Set up the NFS read request struct | 154 | * Set up the NFS read request struct |
158 | */ | 155 | */ |
159 | static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | 156 | static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, |
160 | const struct rpc_call_ops *call_ops, | 157 | const struct rpc_call_ops *call_ops, |
161 | unsigned int count, unsigned int offset) | 158 | unsigned int count, unsigned int offset) |
162 | { | 159 | { |
@@ -174,6 +171,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | |||
174 | .rpc_message = &msg, | 171 | .rpc_message = &msg, |
175 | .callback_ops = call_ops, | 172 | .callback_ops = call_ops, |
176 | .callback_data = data, | 173 | .callback_data = data, |
174 | .workqueue = nfsiod_workqueue, | ||
177 | .flags = RPC_TASK_ASYNC | swap_flags, | 175 | .flags = RPC_TASK_ASYNC | swap_flags, |
178 | }; | 176 | }; |
179 | 177 | ||
@@ -186,7 +184,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | |||
186 | data->args.pgbase = req->wb_pgbase + offset; | 184 | data->args.pgbase = req->wb_pgbase + offset; |
187 | data->args.pages = data->pagevec; | 185 | data->args.pages = data->pagevec; |
188 | data->args.count = count; | 186 | data->args.count = count; |
189 | data->args.context = req->wb_context; | 187 | data->args.context = get_nfs_open_context(req->wb_context); |
190 | 188 | ||
191 | data->res.fattr = &data->fattr; | 189 | data->res.fattr = &data->fattr; |
192 | data->res.count = count; | 190 | data->res.count = count; |
@@ -204,8 +202,10 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, | |||
204 | (unsigned long long)data->args.offset); | 202 | (unsigned long long)data->args.offset); |
205 | 203 | ||
206 | task = rpc_run_task(&task_setup_data); | 204 | task = rpc_run_task(&task_setup_data); |
207 | if (!IS_ERR(task)) | 205 | if (IS_ERR(task)) |
208 | rpc_put_task(task); | 206 | return PTR_ERR(task); |
207 | rpc_put_task(task); | ||
208 | return 0; | ||
209 | } | 209 | } |
210 | 210 | ||
211 | static void | 211 | static void |
@@ -242,6 +242,7 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne | |||
242 | size_t rsize = NFS_SERVER(inode)->rsize, nbytes; | 242 | size_t rsize = NFS_SERVER(inode)->rsize, nbytes; |
243 | unsigned int offset; | 243 | unsigned int offset; |
244 | int requests = 0; | 244 | int requests = 0; |
245 | int ret = 0; | ||
245 | LIST_HEAD(list); | 246 | LIST_HEAD(list); |
246 | 247 | ||
247 | nfs_list_remove_request(req); | 248 | nfs_list_remove_request(req); |
@@ -253,7 +254,6 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne | |||
253 | data = nfs_readdata_alloc(1); | 254 | data = nfs_readdata_alloc(1); |
254 | if (!data) | 255 | if (!data) |
255 | goto out_bad; | 256 | goto out_bad; |
256 | INIT_LIST_HEAD(&data->pages); | ||
257 | list_add(&data->pages, &list); | 257 | list_add(&data->pages, &list); |
258 | requests++; | 258 | requests++; |
259 | nbytes -= len; | 259 | nbytes -= len; |
@@ -264,6 +264,8 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne | |||
264 | offset = 0; | 264 | offset = 0; |
265 | nbytes = count; | 265 | nbytes = count; |
266 | do { | 266 | do { |
267 | int ret2; | ||
268 | |||
267 | data = list_entry(list.next, struct nfs_read_data, pages); | 269 | data = list_entry(list.next, struct nfs_read_data, pages); |
268 | list_del_init(&data->pages); | 270 | list_del_init(&data->pages); |
269 | 271 | ||
@@ -271,13 +273,15 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne | |||
271 | 273 | ||
272 | if (nbytes < rsize) | 274 | if (nbytes < rsize) |
273 | rsize = nbytes; | 275 | rsize = nbytes; |
274 | nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, | 276 | ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, |
275 | rsize, offset); | 277 | rsize, offset); |
278 | if (ret == 0) | ||
279 | ret = ret2; | ||
276 | offset += rsize; | 280 | offset += rsize; |
277 | nbytes -= rsize; | 281 | nbytes -= rsize; |
278 | } while (nbytes != 0); | 282 | } while (nbytes != 0); |
279 | 283 | ||
280 | return 0; | 284 | return ret; |
281 | 285 | ||
282 | out_bad: | 286 | out_bad: |
283 | while (!list_empty(&list)) { | 287 | while (!list_empty(&list)) { |
@@ -295,12 +299,12 @@ static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned | |||
295 | struct nfs_page *req; | 299 | struct nfs_page *req; |
296 | struct page **pages; | 300 | struct page **pages; |
297 | struct nfs_read_data *data; | 301 | struct nfs_read_data *data; |
302 | int ret = -ENOMEM; | ||
298 | 303 | ||
299 | data = nfs_readdata_alloc(npages); | 304 | data = nfs_readdata_alloc(npages); |
300 | if (!data) | 305 | if (!data) |
301 | goto out_bad; | 306 | goto out_bad; |
302 | 307 | ||
303 | INIT_LIST_HEAD(&data->pages); | ||
304 | pages = data->pagevec; | 308 | pages = data->pagevec; |
305 | while (!list_empty(head)) { | 309 | while (!list_empty(head)) { |
306 | req = nfs_list_entry(head->next); | 310 | req = nfs_list_entry(head->next); |
@@ -311,11 +315,10 @@ static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned | |||
311 | } | 315 | } |
312 | req = nfs_list_entry(data->pages.next); | 316 | req = nfs_list_entry(data->pages.next); |
313 | 317 | ||
314 | nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0); | 318 | return nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0); |
315 | return 0; | ||
316 | out_bad: | 319 | out_bad: |
317 | nfs_async_read_error(head); | 320 | nfs_async_read_error(head); |
318 | return -ENOMEM; | 321 | return ret; |
319 | } | 322 | } |
320 | 323 | ||
321 | /* | 324 | /* |
@@ -342,26 +345,25 @@ int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data) | |||
342 | return 0; | 345 | return 0; |
343 | } | 346 | } |
344 | 347 | ||
345 | static int nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) | 348 | static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) |
346 | { | 349 | { |
347 | struct nfs_readargs *argp = &data->args; | 350 | struct nfs_readargs *argp = &data->args; |
348 | struct nfs_readres *resp = &data->res; | 351 | struct nfs_readres *resp = &data->res; |
349 | 352 | ||
350 | if (resp->eof || resp->count == argp->count) | 353 | if (resp->eof || resp->count == argp->count) |
351 | return 0; | 354 | return; |
352 | 355 | ||
353 | /* This is a short read! */ | 356 | /* This is a short read! */ |
354 | nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); | 357 | nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); |
355 | /* Has the server at least made some progress? */ | 358 | /* Has the server at least made some progress? */ |
356 | if (resp->count == 0) | 359 | if (resp->count == 0) |
357 | return 0; | 360 | return; |
358 | 361 | ||
359 | /* Yes, so retry the read at the end of the data */ | 362 | /* Yes, so retry the read at the end of the data */ |
360 | argp->offset += resp->count; | 363 | argp->offset += resp->count; |
361 | argp->pgbase += resp->count; | 364 | argp->pgbase += resp->count; |
362 | argp->count -= resp->count; | 365 | argp->count -= resp->count; |
363 | rpc_restart_call(task); | 366 | rpc_restart_call(task); |
364 | return -EAGAIN; | ||
365 | } | 367 | } |
366 | 368 | ||
367 | /* | 369 | /* |
@@ -370,29 +372,37 @@ static int nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data) | |||
370 | static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) | 372 | static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata) |
371 | { | 373 | { |
372 | struct nfs_read_data *data = calldata; | 374 | struct nfs_read_data *data = calldata; |
373 | struct nfs_page *req = data->req; | ||
374 | struct page *page = req->wb_page; | ||
375 | 375 | ||
376 | if (nfs_readpage_result(task, data) != 0) | 376 | if (nfs_readpage_result(task, data) != 0) |
377 | return; | 377 | return; |
378 | if (task->tk_status < 0) | ||
379 | return; | ||
378 | 380 | ||
379 | if (likely(task->tk_status >= 0)) { | 381 | nfs_readpage_truncate_uninitialised_page(data); |
380 | nfs_readpage_truncate_uninitialised_page(data); | 382 | nfs_readpage_retry(task, data); |
381 | if (nfs_readpage_retry(task, data) != 0) | 383 | } |
382 | return; | 384 | |
383 | } | 385 | static void nfs_readpage_release_partial(void *calldata) |
384 | if (unlikely(task->tk_status < 0)) | 386 | { |
387 | struct nfs_read_data *data = calldata; | ||
388 | struct nfs_page *req = data->req; | ||
389 | struct page *page = req->wb_page; | ||
390 | int status = data->task.tk_status; | ||
391 | |||
392 | if (status < 0) | ||
385 | SetPageError(page); | 393 | SetPageError(page); |
394 | |||
386 | if (atomic_dec_and_test(&req->wb_complete)) { | 395 | if (atomic_dec_and_test(&req->wb_complete)) { |
387 | if (!PageError(page)) | 396 | if (!PageError(page)) |
388 | SetPageUptodate(page); | 397 | SetPageUptodate(page); |
389 | nfs_readpage_release(req); | 398 | nfs_readpage_release(req); |
390 | } | 399 | } |
400 | nfs_readdata_release(calldata); | ||
391 | } | 401 | } |
392 | 402 | ||
393 | static const struct rpc_call_ops nfs_read_partial_ops = { | 403 | static const struct rpc_call_ops nfs_read_partial_ops = { |
394 | .rpc_call_done = nfs_readpage_result_partial, | 404 | .rpc_call_done = nfs_readpage_result_partial, |
395 | .rpc_release = nfs_readdata_release, | 405 | .rpc_release = nfs_readpage_release_partial, |
396 | }; | 406 | }; |
397 | 407 | ||
398 | static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data) | 408 | static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data) |
@@ -427,29 +437,35 @@ static void nfs_readpage_result_full(struct rpc_task *task, void *calldata) | |||
427 | 437 | ||
428 | if (nfs_readpage_result(task, data) != 0) | 438 | if (nfs_readpage_result(task, data) != 0) |
429 | return; | 439 | return; |
440 | if (task->tk_status < 0) | ||
441 | return; | ||
430 | /* | 442 | /* |
431 | * Note: nfs_readpage_retry may change the values of | 443 | * Note: nfs_readpage_retry may change the values of |
432 | * data->args. In the multi-page case, we therefore need | 444 | * data->args. In the multi-page case, we therefore need |
433 | * to ensure that we call nfs_readpage_set_pages_uptodate() | 445 | * to ensure that we call nfs_readpage_set_pages_uptodate() |
434 | * first. | 446 | * first. |
435 | */ | 447 | */ |
436 | if (likely(task->tk_status >= 0)) { | 448 | nfs_readpage_truncate_uninitialised_page(data); |
437 | nfs_readpage_truncate_uninitialised_page(data); | 449 | nfs_readpage_set_pages_uptodate(data); |
438 | nfs_readpage_set_pages_uptodate(data); | 450 | nfs_readpage_retry(task, data); |
439 | if (nfs_readpage_retry(task, data) != 0) | 451 | } |
440 | return; | 452 | |
441 | } | 453 | static void nfs_readpage_release_full(void *calldata) |
454 | { | ||
455 | struct nfs_read_data *data = calldata; | ||
456 | |||
442 | while (!list_empty(&data->pages)) { | 457 | while (!list_empty(&data->pages)) { |
443 | struct nfs_page *req = nfs_list_entry(data->pages.next); | 458 | struct nfs_page *req = nfs_list_entry(data->pages.next); |
444 | 459 | ||
445 | nfs_list_remove_request(req); | 460 | nfs_list_remove_request(req); |
446 | nfs_readpage_release(req); | 461 | nfs_readpage_release(req); |
447 | } | 462 | } |
463 | nfs_readdata_release(calldata); | ||
448 | } | 464 | } |
449 | 465 | ||
450 | static const struct rpc_call_ops nfs_read_full_ops = { | 466 | static const struct rpc_call_ops nfs_read_full_ops = { |
451 | .rpc_call_done = nfs_readpage_result_full, | 467 | .rpc_call_done = nfs_readpage_result_full, |
452 | .rpc_release = nfs_readdata_release, | 468 | .rpc_release = nfs_readpage_release_full, |
453 | }; | 469 | }; |
454 | 470 | ||
455 | /* | 471 | /* |