diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-12-05 00:35:42 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-12-06 10:46:40 -0500 |
commit | 61822ab5e3ed09fcfc49e37227b655202adf6130 (patch) | |
tree | 224a470689463469672ed38b0da3d9f47bc8a245 | |
parent | e261f51f25b98c213e0b3d7f2109b117d714f69d (diff) |
NFS: Ensure we only call set_page_writeback() under the page lock
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/file.c | 6 | ||||
-rw-r--r-- | fs/nfs/write.c | 38 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 1 |
3 files changed, 30 insertions, 15 deletions
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index c2fe3bd83ab1..238fb6641aae 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -307,14 +307,10 @@ static int nfs_commit_write(struct file *file, struct page *page, unsigned offse | |||
307 | 307 | ||
308 | static void nfs_invalidate_page(struct page *page, unsigned long offset) | 308 | static void nfs_invalidate_page(struct page *page, unsigned long offset) |
309 | { | 309 | { |
310 | loff_t range_start, range_end; | ||
311 | |||
312 | if (offset != 0) | 310 | if (offset != 0) |
313 | return; | 311 | return; |
314 | /* Cancel any unstarted writes on this page */ | 312 | /* Cancel any unstarted writes on this page */ |
315 | range_start = page_offset(page); | 313 | nfs_wb_page_priority(page->mapping->host, page, FLUSH_INVALIDATE); |
316 | range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); | ||
317 | nfs_sync_mapping_range(page->mapping, range_start, range_end, FLUSH_INVALIDATE); | ||
318 | } | 314 | } |
319 | 315 | ||
320 | static int nfs_release_page(struct page *page, gfp_t gfp) | 316 | static int nfs_release_page(struct page *page, gfp_t gfp) |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 130528d09a26..bd4dff9dbd69 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -81,7 +81,6 @@ static void nfs_mark_request_dirty(struct nfs_page *req); | |||
81 | static int nfs_wait_on_write_congestion(struct address_space *, int); | 81 | static int nfs_wait_on_write_congestion(struct address_space *, int); |
82 | static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); | 82 | static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); |
83 | static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); | 83 | static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); |
84 | static int nfs_wb_page_priority(struct inode *inode, struct page *page, int how); | ||
85 | static const struct rpc_call_ops nfs_write_partial_ops; | 84 | static const struct rpc_call_ops nfs_write_partial_ops; |
86 | static const struct rpc_call_ops nfs_write_full_ops; | 85 | static const struct rpc_call_ops nfs_write_full_ops; |
87 | static const struct rpc_call_ops nfs_commit_ops; | 86 | static const struct rpc_call_ops nfs_commit_ops; |
@@ -280,8 +279,10 @@ static int nfs_page_mark_flush(struct page *page) | |||
280 | spin_lock(req_lock); | 279 | spin_lock(req_lock); |
281 | } | 280 | } |
282 | spin_unlock(req_lock); | 281 | spin_unlock(req_lock); |
283 | if (test_and_set_bit(PG_FLUSHING, &req->wb_flags) == 0) | 282 | if (test_and_set_bit(PG_FLUSHING, &req->wb_flags) == 0) { |
284 | nfs_mark_request_dirty(req); | 283 | nfs_mark_request_dirty(req); |
284 | set_page_writeback(page); | ||
285 | } | ||
285 | ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); | 286 | ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); |
286 | nfs_unlock_request(req); | 287 | nfs_unlock_request(req); |
287 | return ret; | 288 | return ret; |
@@ -443,6 +444,13 @@ nfs_mark_request_dirty(struct nfs_page *req) | |||
443 | mark_inode_dirty(inode); | 444 | mark_inode_dirty(inode); |
444 | } | 445 | } |
445 | 446 | ||
447 | static void | ||
448 | nfs_redirty_request(struct nfs_page *req) | ||
449 | { | ||
450 | clear_bit(PG_FLUSHING, &req->wb_flags); | ||
451 | __set_page_dirty_nobuffers(req->wb_page); | ||
452 | } | ||
453 | |||
446 | /* | 454 | /* |
447 | * Check if a request is dirty | 455 | * Check if a request is dirty |
448 | */ | 456 | */ |
@@ -777,7 +785,7 @@ static void nfs_writepage_release(struct nfs_page *req) | |||
777 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 785 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
778 | if (!PageError(req->wb_page)) { | 786 | if (!PageError(req->wb_page)) { |
779 | if (NFS_NEED_RESCHED(req)) { | 787 | if (NFS_NEED_RESCHED(req)) { |
780 | nfs_mark_request_dirty(req); | 788 | nfs_redirty_request(req); |
781 | goto out; | 789 | goto out; |
782 | } else if (NFS_NEED_COMMIT(req)) { | 790 | } else if (NFS_NEED_COMMIT(req)) { |
783 | nfs_mark_request_commit(req); | 791 | nfs_mark_request_commit(req); |
@@ -893,7 +901,6 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) | |||
893 | atomic_set(&req->wb_complete, requests); | 901 | atomic_set(&req->wb_complete, requests); |
894 | 902 | ||
895 | ClearPageError(page); | 903 | ClearPageError(page); |
896 | set_page_writeback(page); | ||
897 | offset = 0; | 904 | offset = 0; |
898 | nbytes = req->wb_bytes; | 905 | nbytes = req->wb_bytes; |
899 | do { | 906 | do { |
@@ -923,7 +930,7 @@ out_bad: | |||
923 | list_del(&data->pages); | 930 | list_del(&data->pages); |
924 | nfs_writedata_release(data); | 931 | nfs_writedata_release(data); |
925 | } | 932 | } |
926 | nfs_mark_request_dirty(req); | 933 | nfs_redirty_request(req); |
927 | nfs_clear_page_writeback(req); | 934 | nfs_clear_page_writeback(req); |
928 | return -ENOMEM; | 935 | return -ENOMEM; |
929 | } | 936 | } |
@@ -954,7 +961,6 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) | |||
954 | nfs_list_remove_request(req); | 961 | nfs_list_remove_request(req); |
955 | nfs_list_add_request(req, &data->pages); | 962 | nfs_list_add_request(req, &data->pages); |
956 | ClearPageError(req->wb_page); | 963 | ClearPageError(req->wb_page); |
957 | set_page_writeback(req->wb_page); | ||
958 | *pages++ = req->wb_page; | 964 | *pages++ = req->wb_page; |
959 | count += req->wb_bytes; | 965 | count += req->wb_bytes; |
960 | } | 966 | } |
@@ -969,7 +975,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) | |||
969 | while (!list_empty(head)) { | 975 | while (!list_empty(head)) { |
970 | struct nfs_page *req = nfs_list_entry(head->next); | 976 | struct nfs_page *req = nfs_list_entry(head->next); |
971 | nfs_list_remove_request(req); | 977 | nfs_list_remove_request(req); |
972 | nfs_mark_request_dirty(req); | 978 | nfs_redirty_request(req); |
973 | nfs_clear_page_writeback(req); | 979 | nfs_clear_page_writeback(req); |
974 | } | 980 | } |
975 | return -ENOMEM; | 981 | return -ENOMEM; |
@@ -1004,7 +1010,7 @@ out_err: | |||
1004 | while (!list_empty(head)) { | 1010 | while (!list_empty(head)) { |
1005 | req = nfs_list_entry(head->next); | 1011 | req = nfs_list_entry(head->next); |
1006 | nfs_list_remove_request(req); | 1012 | nfs_list_remove_request(req); |
1007 | nfs_mark_request_dirty(req); | 1013 | nfs_redirty_request(req); |
1008 | nfs_clear_page_writeback(req); | 1014 | nfs_clear_page_writeback(req); |
1009 | } | 1015 | } |
1010 | return error; | 1016 | return error; |
@@ -1320,7 +1326,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) | |||
1320 | } | 1326 | } |
1321 | /* We have a mismatch. Write the page again */ | 1327 | /* We have a mismatch. Write the page again */ |
1322 | dprintk(" mismatch\n"); | 1328 | dprintk(" mismatch\n"); |
1323 | nfs_mark_request_dirty(req); | 1329 | nfs_redirty_request(req); |
1324 | next: | 1330 | next: |
1325 | nfs_clear_page_writeback(req); | 1331 | nfs_clear_page_writeback(req); |
1326 | } | 1332 | } |
@@ -1451,13 +1457,18 @@ int nfs_wb_all(struct inode *inode) | |||
1451 | .bdi = mapping->backing_dev_info, | 1457 | .bdi = mapping->backing_dev_info, |
1452 | .sync_mode = WB_SYNC_ALL, | 1458 | .sync_mode = WB_SYNC_ALL, |
1453 | .nr_to_write = LONG_MAX, | 1459 | .nr_to_write = LONG_MAX, |
1460 | .for_writepages = 1, | ||
1454 | .range_cyclic = 1, | 1461 | .range_cyclic = 1, |
1455 | }; | 1462 | }; |
1456 | int ret; | 1463 | int ret; |
1457 | 1464 | ||
1465 | ret = generic_writepages(mapping, &wbc); | ||
1466 | if (ret < 0) | ||
1467 | goto out; | ||
1458 | ret = nfs_sync_mapping_wait(mapping, &wbc, 0); | 1468 | ret = nfs_sync_mapping_wait(mapping, &wbc, 0); |
1459 | if (ret >= 0) | 1469 | if (ret >= 0) |
1460 | return 0; | 1470 | return 0; |
1471 | out: | ||
1461 | return ret; | 1472 | return ret; |
1462 | } | 1473 | } |
1463 | 1474 | ||
@@ -1469,16 +1480,23 @@ int nfs_sync_mapping_range(struct address_space *mapping, loff_t range_start, lo | |||
1469 | .nr_to_write = LONG_MAX, | 1480 | .nr_to_write = LONG_MAX, |
1470 | .range_start = range_start, | 1481 | .range_start = range_start, |
1471 | .range_end = range_end, | 1482 | .range_end = range_end, |
1483 | .for_writepages = 1, | ||
1472 | }; | 1484 | }; |
1473 | int ret; | 1485 | int ret; |
1474 | 1486 | ||
1487 | if (!(how & FLUSH_NOWRITEPAGE)) { | ||
1488 | ret = generic_writepages(mapping, &wbc); | ||
1489 | if (ret < 0) | ||
1490 | goto out; | ||
1491 | } | ||
1475 | ret = nfs_sync_mapping_wait(mapping, &wbc, how); | 1492 | ret = nfs_sync_mapping_wait(mapping, &wbc, how); |
1476 | if (ret >= 0) | 1493 | if (ret >= 0) |
1477 | return 0; | 1494 | return 0; |
1495 | out: | ||
1478 | return ret; | 1496 | return ret; |
1479 | } | 1497 | } |
1480 | 1498 | ||
1481 | static int nfs_wb_page_priority(struct inode *inode, struct page *page, int how) | 1499 | int nfs_wb_page_priority(struct inode *inode, struct page *page, int how) |
1482 | { | 1500 | { |
1483 | loff_t range_start = page_offset(page); | 1501 | loff_t range_start = page_offset(page); |
1484 | loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); | 1502 | loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 3de334c72b1f..04963063e620 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -438,6 +438,7 @@ extern long nfs_sync_mapping_wait(struct address_space *, struct writeback_contr | |||
438 | extern int nfs_sync_mapping_range(struct address_space *, loff_t, loff_t, int); | 438 | extern int nfs_sync_mapping_range(struct address_space *, loff_t, loff_t, int); |
439 | extern int nfs_wb_all(struct inode *inode); | 439 | extern int nfs_wb_all(struct inode *inode); |
440 | extern int nfs_wb_page(struct inode *inode, struct page* page); | 440 | extern int nfs_wb_page(struct inode *inode, struct page* page); |
441 | extern int nfs_wb_page_priority(struct inode *inode, struct page* page, int how); | ||
441 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 442 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
442 | extern int nfs_commit_inode(struct inode *, int); | 443 | extern int nfs_commit_inode(struct inode *, int); |
443 | extern struct nfs_write_data *nfs_commit_alloc(void); | 444 | extern struct nfs_write_data *nfs_commit_alloc(void); |