diff options
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 301 |
1 files changed, 188 insertions, 113 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index febdade91670..797558941745 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/pagemap.h> | 12 | #include <linux/pagemap.h> |
13 | #include <linux/file.h> | 13 | #include <linux/file.h> |
14 | #include <linux/writeback.h> | 14 | #include <linux/writeback.h> |
15 | #include <linux/swap.h> | ||
15 | 16 | ||
16 | #include <linux/sunrpc/clnt.h> | 17 | #include <linux/sunrpc/clnt.h> |
17 | #include <linux/nfs_fs.h> | 18 | #include <linux/nfs_fs.h> |
@@ -37,8 +38,6 @@ | |||
37 | static struct nfs_page * nfs_update_request(struct nfs_open_context*, | 38 | static struct nfs_page * nfs_update_request(struct nfs_open_context*, |
38 | struct page *, | 39 | struct page *, |
39 | unsigned int, unsigned int); | 40 | unsigned int, unsigned int); |
40 | static void nfs_mark_request_dirty(struct nfs_page *req); | ||
41 | static int nfs_wait_on_write_congestion(struct address_space *, int); | ||
42 | static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); | 41 | static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); |
43 | static const struct rpc_call_ops nfs_write_partial_ops; | 42 | static const struct rpc_call_ops nfs_write_partial_ops; |
44 | static const struct rpc_call_ops nfs_write_full_ops; | 43 | static const struct rpc_call_ops nfs_write_full_ops; |
@@ -48,8 +47,6 @@ static struct kmem_cache *nfs_wdata_cachep; | |||
48 | static mempool_t *nfs_wdata_mempool; | 47 | static mempool_t *nfs_wdata_mempool; |
49 | static mempool_t *nfs_commit_mempool; | 48 | static mempool_t *nfs_commit_mempool; |
50 | 49 | ||
51 | static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); | ||
52 | |||
53 | struct nfs_write_data *nfs_commit_alloc(void) | 50 | struct nfs_write_data *nfs_commit_alloc(void) |
54 | { | 51 | { |
55 | struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); | 52 | struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); |
@@ -211,6 +208,43 @@ static int wb_priority(struct writeback_control *wbc) | |||
211 | } | 208 | } |
212 | 209 | ||
213 | /* | 210 | /* |
211 | * NFS congestion control | ||
212 | */ | ||
213 | |||
214 | int nfs_congestion_kb; | ||
215 | |||
216 | #define NFS_CONGESTION_ON_THRESH (nfs_congestion_kb >> (PAGE_SHIFT-10)) | ||
217 | #define NFS_CONGESTION_OFF_THRESH \ | ||
218 | (NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2)) | ||
219 | |||
220 | static int nfs_set_page_writeback(struct page *page) | ||
221 | { | ||
222 | int ret = test_set_page_writeback(page); | ||
223 | |||
224 | if (!ret) { | ||
225 | struct inode *inode = page->mapping->host; | ||
226 | struct nfs_server *nfss = NFS_SERVER(inode); | ||
227 | |||
228 | if (atomic_inc_return(&nfss->writeback) > | ||
229 | NFS_CONGESTION_ON_THRESH) | ||
230 | set_bdi_congested(&nfss->backing_dev_info, WRITE); | ||
231 | } | ||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | static void nfs_end_page_writeback(struct page *page) | ||
236 | { | ||
237 | struct inode *inode = page->mapping->host; | ||
238 | struct nfs_server *nfss = NFS_SERVER(inode); | ||
239 | |||
240 | end_page_writeback(page); | ||
241 | if (atomic_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) { | ||
242 | clear_bdi_congested(&nfss->backing_dev_info, WRITE); | ||
243 | congestion_end(WRITE); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* | ||
214 | * Find an associated nfs write request, and prepare to flush it out | 248 | * Find an associated nfs write request, and prepare to flush it out |
215 | * Returns 1 if there was no write request, or if the request was | 249 | * Returns 1 if there was no write request, or if the request was |
216 | * already tagged by nfs_set_page_dirty.Returns 0 if the request | 250 | * already tagged by nfs_set_page_dirty.Returns 0 if the request |
@@ -220,7 +254,8 @@ static int wb_priority(struct writeback_control *wbc) | |||
220 | static int nfs_page_mark_flush(struct page *page) | 254 | static int nfs_page_mark_flush(struct page *page) |
221 | { | 255 | { |
222 | struct nfs_page *req; | 256 | struct nfs_page *req; |
223 | spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; | 257 | struct nfs_inode *nfsi = NFS_I(page->mapping->host); |
258 | spinlock_t *req_lock = &nfsi->req_lock; | ||
224 | int ret; | 259 | int ret; |
225 | 260 | ||
226 | spin_lock(req_lock); | 261 | spin_lock(req_lock); |
@@ -244,11 +279,23 @@ static int nfs_page_mark_flush(struct page *page) | |||
244 | return ret; | 279 | return ret; |
245 | spin_lock(req_lock); | 280 | spin_lock(req_lock); |
246 | } | 281 | } |
247 | spin_unlock(req_lock); | 282 | if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { |
248 | if (test_and_set_bit(PG_FLUSHING, &req->wb_flags) == 0) { | 283 | /* This request is marked for commit */ |
249 | nfs_mark_request_dirty(req); | 284 | spin_unlock(req_lock); |
250 | set_page_writeback(page); | 285 | nfs_unlock_request(req); |
286 | return 1; | ||
251 | } | 287 | } |
288 | if (nfs_set_page_writeback(page) == 0) { | ||
289 | nfs_list_remove_request(req); | ||
290 | /* add the request to the inode's dirty list. */ | ||
291 | radix_tree_tag_set(&nfsi->nfs_page_tree, | ||
292 | req->wb_index, NFS_PAGE_TAG_DIRTY); | ||
293 | nfs_list_add_request(req, &nfsi->dirty); | ||
294 | nfsi->ndirty++; | ||
295 | spin_unlock(req_lock); | ||
296 | __mark_inode_dirty(page->mapping->host, I_DIRTY_PAGES); | ||
297 | } else | ||
298 | spin_unlock(req_lock); | ||
252 | ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); | 299 | ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); |
253 | nfs_unlock_request(req); | 300 | nfs_unlock_request(req); |
254 | return ret; | 301 | return ret; |
@@ -302,13 +349,8 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc) | |||
302 | return err; | 349 | return err; |
303 | } | 350 | } |
304 | 351 | ||
305 | /* | ||
306 | * Note: causes nfs_update_request() to block on the assumption | ||
307 | * that the writeback is generated due to memory pressure. | ||
308 | */ | ||
309 | int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) | 352 | int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) |
310 | { | 353 | { |
311 | struct backing_dev_info *bdi = mapping->backing_dev_info; | ||
312 | struct inode *inode = mapping->host; | 354 | struct inode *inode = mapping->host; |
313 | int err; | 355 | int err; |
314 | 356 | ||
@@ -317,20 +359,12 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) | |||
317 | err = generic_writepages(mapping, wbc); | 359 | err = generic_writepages(mapping, wbc); |
318 | if (err) | 360 | if (err) |
319 | return err; | 361 | return err; |
320 | while (test_and_set_bit(BDI_write_congested, &bdi->state) != 0) { | ||
321 | if (wbc->nonblocking) | ||
322 | return 0; | ||
323 | nfs_wait_on_write_congestion(mapping, 0); | ||
324 | } | ||
325 | err = nfs_flush_mapping(mapping, wbc, wb_priority(wbc)); | 362 | err = nfs_flush_mapping(mapping, wbc, wb_priority(wbc)); |
326 | if (err < 0) | 363 | if (err < 0) |
327 | goto out; | 364 | goto out; |
328 | nfs_add_stats(inode, NFSIOS_WRITEPAGES, err); | 365 | nfs_add_stats(inode, NFSIOS_WRITEPAGES, err); |
329 | err = 0; | 366 | err = 0; |
330 | out: | 367 | out: |
331 | clear_bit(BDI_write_congested, &bdi->state); | ||
332 | wake_up_all(&nfs_write_congestion); | ||
333 | congestion_end(WRITE); | ||
334 | return err; | 368 | return err; |
335 | } | 369 | } |
336 | 370 | ||
@@ -354,13 +388,15 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) | |||
354 | } | 388 | } |
355 | SetPagePrivate(req->wb_page); | 389 | SetPagePrivate(req->wb_page); |
356 | set_page_private(req->wb_page, (unsigned long)req); | 390 | set_page_private(req->wb_page, (unsigned long)req); |
391 | if (PageDirty(req->wb_page)) | ||
392 | set_bit(PG_NEED_FLUSH, &req->wb_flags); | ||
357 | nfsi->npages++; | 393 | nfsi->npages++; |
358 | atomic_inc(&req->wb_count); | 394 | atomic_inc(&req->wb_count); |
359 | return 0; | 395 | return 0; |
360 | } | 396 | } |
361 | 397 | ||
362 | /* | 398 | /* |
363 | * Insert a write request into an inode | 399 | * Remove a write request from an inode |
364 | */ | 400 | */ |
365 | static void nfs_inode_remove_request(struct nfs_page *req) | 401 | static void nfs_inode_remove_request(struct nfs_page *req) |
366 | { | 402 | { |
@@ -373,6 +409,8 @@ static void nfs_inode_remove_request(struct nfs_page *req) | |||
373 | set_page_private(req->wb_page, 0); | 409 | set_page_private(req->wb_page, 0); |
374 | ClearPagePrivate(req->wb_page); | 410 | ClearPagePrivate(req->wb_page); |
375 | radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); | 411 | radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); |
412 | if (test_and_clear_bit(PG_NEED_FLUSH, &req->wb_flags)) | ||
413 | __set_page_dirty_nobuffers(req->wb_page); | ||
376 | nfsi->npages--; | 414 | nfsi->npages--; |
377 | if (!nfsi->npages) { | 415 | if (!nfsi->npages) { |
378 | spin_unlock(&nfsi->req_lock); | 416 | spin_unlock(&nfsi->req_lock); |
@@ -384,28 +422,9 @@ static void nfs_inode_remove_request(struct nfs_page *req) | |||
384 | nfs_release_request(req); | 422 | nfs_release_request(req); |
385 | } | 423 | } |
386 | 424 | ||
387 | /* | ||
388 | * Add a request to the inode's dirty list. | ||
389 | */ | ||
390 | static void | ||
391 | nfs_mark_request_dirty(struct nfs_page *req) | ||
392 | { | ||
393 | struct inode *inode = req->wb_context->dentry->d_inode; | ||
394 | struct nfs_inode *nfsi = NFS_I(inode); | ||
395 | |||
396 | spin_lock(&nfsi->req_lock); | ||
397 | radix_tree_tag_set(&nfsi->nfs_page_tree, | ||
398 | req->wb_index, NFS_PAGE_TAG_DIRTY); | ||
399 | nfs_list_add_request(req, &nfsi->dirty); | ||
400 | nfsi->ndirty++; | ||
401 | spin_unlock(&nfsi->req_lock); | ||
402 | __mark_inode_dirty(inode, I_DIRTY_PAGES); | ||
403 | } | ||
404 | |||
405 | static void | 425 | static void |
406 | nfs_redirty_request(struct nfs_page *req) | 426 | nfs_redirty_request(struct nfs_page *req) |
407 | { | 427 | { |
408 | clear_bit(PG_FLUSHING, &req->wb_flags); | ||
409 | __set_page_dirty_nobuffers(req->wb_page); | 428 | __set_page_dirty_nobuffers(req->wb_page); |
410 | } | 429 | } |
411 | 430 | ||
@@ -415,7 +434,11 @@ nfs_redirty_request(struct nfs_page *req) | |||
415 | static inline int | 434 | static inline int |
416 | nfs_dirty_request(struct nfs_page *req) | 435 | nfs_dirty_request(struct nfs_page *req) |
417 | { | 436 | { |
418 | return test_bit(PG_FLUSHING, &req->wb_flags) == 0; | 437 | struct page *page = req->wb_page; |
438 | |||
439 | if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags)) | ||
440 | return 0; | ||
441 | return !PageWriteback(req->wb_page); | ||
419 | } | 442 | } |
420 | 443 | ||
421 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 444 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
@@ -431,10 +454,48 @@ nfs_mark_request_commit(struct nfs_page *req) | |||
431 | spin_lock(&nfsi->req_lock); | 454 | spin_lock(&nfsi->req_lock); |
432 | nfs_list_add_request(req, &nfsi->commit); | 455 | nfs_list_add_request(req, &nfsi->commit); |
433 | nfsi->ncommit++; | 456 | nfsi->ncommit++; |
457 | set_bit(PG_NEED_COMMIT, &(req)->wb_flags); | ||
434 | spin_unlock(&nfsi->req_lock); | 458 | spin_unlock(&nfsi->req_lock); |
435 | inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); | 459 | inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); |
436 | __mark_inode_dirty(inode, I_DIRTY_DATASYNC); | 460 | __mark_inode_dirty(inode, I_DIRTY_DATASYNC); |
437 | } | 461 | } |
462 | |||
463 | static inline | ||
464 | int nfs_write_need_commit(struct nfs_write_data *data) | ||
465 | { | ||
466 | return data->verf.committed != NFS_FILE_SYNC; | ||
467 | } | ||
468 | |||
469 | static inline | ||
470 | int nfs_reschedule_unstable_write(struct nfs_page *req) | ||
471 | { | ||
472 | if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { | ||
473 | nfs_mark_request_commit(req); | ||
474 | return 1; | ||
475 | } | ||
476 | if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) { | ||
477 | nfs_redirty_request(req); | ||
478 | return 1; | ||
479 | } | ||
480 | return 0; | ||
481 | } | ||
482 | #else | ||
483 | static inline void | ||
484 | nfs_mark_request_commit(struct nfs_page *req) | ||
485 | { | ||
486 | } | ||
487 | |||
488 | static inline | ||
489 | int nfs_write_need_commit(struct nfs_write_data *data) | ||
490 | { | ||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | static inline | ||
495 | int nfs_reschedule_unstable_write(struct nfs_page *req) | ||
496 | { | ||
497 | return 0; | ||
498 | } | ||
438 | #endif | 499 | #endif |
439 | 500 | ||
440 | /* | 501 | /* |
@@ -481,6 +542,7 @@ static void nfs_cancel_dirty_list(struct list_head *head) | |||
481 | while(!list_empty(head)) { | 542 | while(!list_empty(head)) { |
482 | req = nfs_list_entry(head->next); | 543 | req = nfs_list_entry(head->next); |
483 | nfs_list_remove_request(req); | 544 | nfs_list_remove_request(req); |
545 | nfs_end_page_writeback(req->wb_page); | ||
484 | nfs_inode_remove_request(req); | 546 | nfs_inode_remove_request(req); |
485 | nfs_clear_page_writeback(req); | 547 | nfs_clear_page_writeback(req); |
486 | } | 548 | } |
@@ -494,6 +556,7 @@ static void nfs_cancel_commit_list(struct list_head *head) | |||
494 | req = nfs_list_entry(head->next); | 556 | req = nfs_list_entry(head->next); |
495 | dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); | 557 | dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); |
496 | nfs_list_remove_request(req); | 558 | nfs_list_remove_request(req); |
559 | clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); | ||
497 | nfs_inode_remove_request(req); | 560 | nfs_inode_remove_request(req); |
498 | nfs_unlock_request(req); | 561 | nfs_unlock_request(req); |
499 | } | 562 | } |
@@ -531,10 +594,10 @@ static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, un | |||
531 | } | 594 | } |
532 | #endif | 595 | #endif |
533 | 596 | ||
534 | static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) | 597 | static int nfs_wait_on_write_congestion(struct address_space *mapping) |
535 | { | 598 | { |
599 | struct inode *inode = mapping->host; | ||
536 | struct backing_dev_info *bdi = mapping->backing_dev_info; | 600 | struct backing_dev_info *bdi = mapping->backing_dev_info; |
537 | DEFINE_WAIT(wait); | ||
538 | int ret = 0; | 601 | int ret = 0; |
539 | 602 | ||
540 | might_sleep(); | 603 | might_sleep(); |
@@ -542,31 +605,23 @@ static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) | |||
542 | if (!bdi_write_congested(bdi)) | 605 | if (!bdi_write_congested(bdi)) |
543 | return 0; | 606 | return 0; |
544 | 607 | ||
545 | nfs_inc_stats(mapping->host, NFSIOS_CONGESTIONWAIT); | 608 | nfs_inc_stats(inode, NFSIOS_CONGESTIONWAIT); |
546 | 609 | ||
547 | if (intr) { | 610 | do { |
548 | struct rpc_clnt *clnt = NFS_CLIENT(mapping->host); | 611 | struct rpc_clnt *clnt = NFS_CLIENT(inode); |
549 | sigset_t oldset; | 612 | sigset_t oldset; |
550 | 613 | ||
551 | rpc_clnt_sigmask(clnt, &oldset); | 614 | rpc_clnt_sigmask(clnt, &oldset); |
552 | prepare_to_wait(&nfs_write_congestion, &wait, TASK_INTERRUPTIBLE); | 615 | ret = congestion_wait_interruptible(WRITE, HZ/10); |
553 | if (bdi_write_congested(bdi)) { | ||
554 | if (signalled()) | ||
555 | ret = -ERESTARTSYS; | ||
556 | else | ||
557 | schedule(); | ||
558 | } | ||
559 | rpc_clnt_sigunmask(clnt, &oldset); | 616 | rpc_clnt_sigunmask(clnt, &oldset); |
560 | } else { | 617 | if (ret == -ERESTARTSYS) |
561 | prepare_to_wait(&nfs_write_congestion, &wait, TASK_UNINTERRUPTIBLE); | 618 | break; |
562 | if (bdi_write_congested(bdi)) | 619 | ret = 0; |
563 | schedule(); | 620 | } while (bdi_write_congested(bdi)); |
564 | } | 621 | |
565 | finish_wait(&nfs_write_congestion, &wait); | ||
566 | return ret; | 622 | return ret; |
567 | } | 623 | } |
568 | 624 | ||
569 | |||
570 | /* | 625 | /* |
571 | * Try to update any existing write request, or create one if there is none. | 626 | * Try to update any existing write request, or create one if there is none. |
572 | * In order to match, the request's credentials must match those of | 627 | * In order to match, the request's credentials must match those of |
@@ -577,14 +632,15 @@ static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) | |||
577 | static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, | 632 | static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, |
578 | struct page *page, unsigned int offset, unsigned int bytes) | 633 | struct page *page, unsigned int offset, unsigned int bytes) |
579 | { | 634 | { |
580 | struct inode *inode = page->mapping->host; | 635 | struct address_space *mapping = page->mapping; |
636 | struct inode *inode = mapping->host; | ||
581 | struct nfs_inode *nfsi = NFS_I(inode); | 637 | struct nfs_inode *nfsi = NFS_I(inode); |
582 | struct nfs_page *req, *new = NULL; | 638 | struct nfs_page *req, *new = NULL; |
583 | unsigned long rqend, end; | 639 | unsigned long rqend, end; |
584 | 640 | ||
585 | end = offset + bytes; | 641 | end = offset + bytes; |
586 | 642 | ||
587 | if (nfs_wait_on_write_congestion(page->mapping, NFS_SERVER(inode)->flags & NFS_MOUNT_INTR)) | 643 | if (nfs_wait_on_write_congestion(mapping)) |
588 | return ERR_PTR(-ERESTARTSYS); | 644 | return ERR_PTR(-ERESTARTSYS); |
589 | for (;;) { | 645 | for (;;) { |
590 | /* Loop over all inode entries and see if we find | 646 | /* Loop over all inode entries and see if we find |
@@ -727,26 +783,12 @@ int nfs_updatepage(struct file *file, struct page *page, | |||
727 | 783 | ||
728 | static void nfs_writepage_release(struct nfs_page *req) | 784 | static void nfs_writepage_release(struct nfs_page *req) |
729 | { | 785 | { |
730 | end_page_writeback(req->wb_page); | ||
731 | 786 | ||
732 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 787 | if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) { |
733 | if (!PageError(req->wb_page)) { | 788 | nfs_end_page_writeback(req->wb_page); |
734 | if (NFS_NEED_RESCHED(req)) { | 789 | nfs_inode_remove_request(req); |
735 | nfs_redirty_request(req); | 790 | } else |
736 | goto out; | 791 | nfs_end_page_writeback(req->wb_page); |
737 | } else if (NFS_NEED_COMMIT(req)) { | ||
738 | nfs_mark_request_commit(req); | ||
739 | goto out; | ||
740 | } | ||
741 | } | ||
742 | nfs_inode_remove_request(req); | ||
743 | |||
744 | out: | ||
745 | nfs_clear_commit(req); | ||
746 | nfs_clear_reschedule(req); | ||
747 | #else | ||
748 | nfs_inode_remove_request(req); | ||
749 | #endif | ||
750 | nfs_clear_page_writeback(req); | 792 | nfs_clear_page_writeback(req); |
751 | } | 793 | } |
752 | 794 | ||
@@ -879,6 +921,7 @@ out_bad: | |||
879 | nfs_writedata_release(data); | 921 | nfs_writedata_release(data); |
880 | } | 922 | } |
881 | nfs_redirty_request(req); | 923 | nfs_redirty_request(req); |
924 | nfs_end_page_writeback(req->wb_page); | ||
882 | nfs_clear_page_writeback(req); | 925 | nfs_clear_page_writeback(req); |
883 | return -ENOMEM; | 926 | return -ENOMEM; |
884 | } | 927 | } |
@@ -924,6 +967,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) | |||
924 | struct nfs_page *req = nfs_list_entry(head->next); | 967 | struct nfs_page *req = nfs_list_entry(head->next); |
925 | nfs_list_remove_request(req); | 968 | nfs_list_remove_request(req); |
926 | nfs_redirty_request(req); | 969 | nfs_redirty_request(req); |
970 | nfs_end_page_writeback(req->wb_page); | ||
927 | nfs_clear_page_writeback(req); | 971 | nfs_clear_page_writeback(req); |
928 | } | 972 | } |
929 | return -ENOMEM; | 973 | return -ENOMEM; |
@@ -959,6 +1003,7 @@ out_err: | |||
959 | req = nfs_list_entry(head->next); | 1003 | req = nfs_list_entry(head->next); |
960 | nfs_list_remove_request(req); | 1004 | nfs_list_remove_request(req); |
961 | nfs_redirty_request(req); | 1005 | nfs_redirty_request(req); |
1006 | nfs_end_page_writeback(req->wb_page); | ||
962 | nfs_clear_page_writeback(req); | 1007 | nfs_clear_page_writeback(req); |
963 | } | 1008 | } |
964 | return error; | 1009 | return error; |
@@ -986,22 +1031,28 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) | |||
986 | nfs_set_pageerror(page); | 1031 | nfs_set_pageerror(page); |
987 | req->wb_context->error = task->tk_status; | 1032 | req->wb_context->error = task->tk_status; |
988 | dprintk(", error = %d\n", task->tk_status); | 1033 | dprintk(", error = %d\n", task->tk_status); |
989 | } else { | 1034 | goto out; |
990 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | ||
991 | if (data->verf.committed < NFS_FILE_SYNC) { | ||
992 | if (!NFS_NEED_COMMIT(req)) { | ||
993 | nfs_defer_commit(req); | ||
994 | memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); | ||
995 | dprintk(" defer commit\n"); | ||
996 | } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) { | ||
997 | nfs_defer_reschedule(req); | ||
998 | dprintk(" server reboot detected\n"); | ||
999 | } | ||
1000 | } else | ||
1001 | #endif | ||
1002 | dprintk(" OK\n"); | ||
1003 | } | 1035 | } |
1004 | 1036 | ||
1037 | if (nfs_write_need_commit(data)) { | ||
1038 | spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; | ||
1039 | |||
1040 | spin_lock(req_lock); | ||
1041 | if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) { | ||
1042 | /* Do nothing we need to resend the writes */ | ||
1043 | } else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) { | ||
1044 | memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); | ||
1045 | dprintk(" defer commit\n"); | ||
1046 | } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) { | ||
1047 | set_bit(PG_NEED_RESCHED, &req->wb_flags); | ||
1048 | clear_bit(PG_NEED_COMMIT, &req->wb_flags); | ||
1049 | dprintk(" server reboot detected\n"); | ||
1050 | } | ||
1051 | spin_unlock(req_lock); | ||
1052 | } else | ||
1053 | dprintk(" OK\n"); | ||
1054 | |||
1055 | out: | ||
1005 | if (atomic_dec_and_test(&req->wb_complete)) | 1056 | if (atomic_dec_and_test(&req->wb_complete)) |
1006 | nfs_writepage_release(req); | 1057 | nfs_writepage_release(req); |
1007 | } | 1058 | } |
@@ -1042,25 +1093,21 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) | |||
1042 | if (task->tk_status < 0) { | 1093 | if (task->tk_status < 0) { |
1043 | nfs_set_pageerror(page); | 1094 | nfs_set_pageerror(page); |
1044 | req->wb_context->error = task->tk_status; | 1095 | req->wb_context->error = task->tk_status; |
1045 | end_page_writeback(page); | ||
1046 | nfs_inode_remove_request(req); | ||
1047 | dprintk(", error = %d\n", task->tk_status); | 1096 | dprintk(", error = %d\n", task->tk_status); |
1048 | goto next; | 1097 | goto remove_request; |
1049 | } | 1098 | } |
1050 | end_page_writeback(page); | ||
1051 | 1099 | ||
1052 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 1100 | if (nfs_write_need_commit(data)) { |
1053 | if (data->args.stable != NFS_UNSTABLE || data->verf.committed == NFS_FILE_SYNC) { | 1101 | memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); |
1054 | nfs_inode_remove_request(req); | 1102 | nfs_mark_request_commit(req); |
1055 | dprintk(" OK\n"); | 1103 | nfs_end_page_writeback(page); |
1104 | dprintk(" marked for commit\n"); | ||
1056 | goto next; | 1105 | goto next; |
1057 | } | 1106 | } |
1058 | memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); | 1107 | dprintk(" OK\n"); |
1059 | nfs_mark_request_commit(req); | 1108 | remove_request: |
1060 | dprintk(" marked for commit\n"); | 1109 | nfs_end_page_writeback(page); |
1061 | #else | ||
1062 | nfs_inode_remove_request(req); | 1110 | nfs_inode_remove_request(req); |
1063 | #endif | ||
1064 | next: | 1111 | next: |
1065 | nfs_clear_page_writeback(req); | 1112 | nfs_clear_page_writeback(req); |
1066 | } | 1113 | } |
@@ -1248,6 +1295,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) | |||
1248 | while (!list_empty(&data->pages)) { | 1295 | while (!list_empty(&data->pages)) { |
1249 | req = nfs_list_entry(data->pages.next); | 1296 | req = nfs_list_entry(data->pages.next); |
1250 | nfs_list_remove_request(req); | 1297 | nfs_list_remove_request(req); |
1298 | clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); | ||
1251 | dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); | 1299 | dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); |
1252 | 1300 | ||
1253 | dprintk("NFS: commit (%s/%Ld %d@%Ld)", | 1301 | dprintk("NFS: commit (%s/%Ld %d@%Ld)", |
@@ -1483,15 +1531,22 @@ int nfs_wb_page(struct inode *inode, struct page* page) | |||
1483 | 1531 | ||
1484 | int nfs_set_page_dirty(struct page *page) | 1532 | int nfs_set_page_dirty(struct page *page) |
1485 | { | 1533 | { |
1534 | spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; | ||
1486 | struct nfs_page *req; | 1535 | struct nfs_page *req; |
1536 | int ret; | ||
1487 | 1537 | ||
1488 | req = nfs_page_find_request(page); | 1538 | spin_lock(req_lock); |
1539 | req = nfs_page_find_request_locked(page); | ||
1489 | if (req != NULL) { | 1540 | if (req != NULL) { |
1490 | /* Mark any existing write requests for flushing */ | 1541 | /* Mark any existing write requests for flushing */ |
1491 | set_bit(PG_NEED_FLUSH, &req->wb_flags); | 1542 | ret = !test_and_set_bit(PG_NEED_FLUSH, &req->wb_flags); |
1543 | spin_unlock(req_lock); | ||
1492 | nfs_release_request(req); | 1544 | nfs_release_request(req); |
1545 | return ret; | ||
1493 | } | 1546 | } |
1494 | return __set_page_dirty_nobuffers(page); | 1547 | ret = __set_page_dirty_nobuffers(page); |
1548 | spin_unlock(req_lock); | ||
1549 | return ret; | ||
1495 | } | 1550 | } |
1496 | 1551 | ||
1497 | 1552 | ||
@@ -1514,6 +1569,26 @@ int __init nfs_init_writepagecache(void) | |||
1514 | if (nfs_commit_mempool == NULL) | 1569 | if (nfs_commit_mempool == NULL) |
1515 | return -ENOMEM; | 1570 | return -ENOMEM; |
1516 | 1571 | ||
1572 | /* | ||
1573 | * NFS congestion size, scale with available memory. | ||
1574 | * | ||
1575 | * 64MB: 8192k | ||
1576 | * 128MB: 11585k | ||
1577 | * 256MB: 16384k | ||
1578 | * 512MB: 23170k | ||
1579 | * 1GB: 32768k | ||
1580 | * 2GB: 46340k | ||
1581 | * 4GB: 65536k | ||
1582 | * 8GB: 92681k | ||
1583 | * 16GB: 131072k | ||
1584 | * | ||
1585 | * This allows larger machines to have larger/more transfers. | ||
1586 | * Limit the default to 256M | ||
1587 | */ | ||
1588 | nfs_congestion_kb = (16*int_sqrt(totalram_pages)) << (PAGE_SHIFT-10); | ||
1589 | if (nfs_congestion_kb > 256*1024) | ||
1590 | nfs_congestion_kb = 256*1024; | ||
1591 | |||
1517 | return 0; | 1592 | return 0; |
1518 | } | 1593 | } |
1519 | 1594 | ||