diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-04-20 16:12:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-04-21 01:56:30 -0400 |
commit | 2b82f190c81bf1524447c021df4e9ce8ef379bd5 (patch) | |
tree | 3f4ebb8c0379715fe38897474856bdbab5750250 /fs/nfs/write.c | |
parent | 612c9384fd0486686699f7d49b774f0c7a79c511 (diff) |
NFS: Fix race in nfs_set_page_dirty
Protect nfs_set_page_dirty() against races with nfs_inode_add_request.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index ce5b4a9f2d8b..797558941745 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -388,6 +388,8 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) | |||
388 | } | 388 | } |
389 | SetPagePrivate(req->wb_page); | 389 | SetPagePrivate(req->wb_page); |
390 | 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); | ||
391 | nfsi->npages++; | 393 | nfsi->npages++; |
392 | atomic_inc(&req->wb_count); | 394 | atomic_inc(&req->wb_count); |
393 | return 0; | 395 | return 0; |
@@ -407,6 +409,8 @@ static void nfs_inode_remove_request(struct nfs_page *req) | |||
407 | set_page_private(req->wb_page, 0); | 409 | set_page_private(req->wb_page, 0); |
408 | ClearPagePrivate(req->wb_page); | 410 | ClearPagePrivate(req->wb_page); |
409 | 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); | ||
410 | nfsi->npages--; | 414 | nfsi->npages--; |
411 | if (!nfsi->npages) { | 415 | if (!nfsi->npages) { |
412 | spin_unlock(&nfsi->req_lock); | 416 | spin_unlock(&nfsi->req_lock); |
@@ -1527,15 +1531,22 @@ int nfs_wb_page(struct inode *inode, struct page* page) | |||
1527 | 1531 | ||
1528 | int nfs_set_page_dirty(struct page *page) | 1532 | int nfs_set_page_dirty(struct page *page) |
1529 | { | 1533 | { |
1534 | spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; | ||
1530 | struct nfs_page *req; | 1535 | struct nfs_page *req; |
1536 | int ret; | ||
1531 | 1537 | ||
1532 | req = nfs_page_find_request(page); | 1538 | spin_lock(req_lock); |
1539 | req = nfs_page_find_request_locked(page); | ||
1533 | if (req != NULL) { | 1540 | if (req != NULL) { |
1534 | /* Mark any existing write requests for flushing */ | 1541 | /* Mark any existing write requests for flushing */ |
1535 | 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); | ||
1536 | nfs_release_request(req); | 1544 | nfs_release_request(req); |
1545 | return ret; | ||
1537 | } | 1546 | } |
1538 | return __set_page_dirty_nobuffers(page); | 1547 | ret = __set_page_dirty_nobuffers(page); |
1548 | spin_unlock(req_lock); | ||
1549 | return ret; | ||
1539 | } | 1550 | } |
1540 | 1551 | ||
1541 | 1552 | ||