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 | ||
