diff options
author | Jens Axboe <axboe@suse.de> | 2006-06-20 09:01:12 -0400 |
---|---|---|
committer | Jens Axboe <axboe@nelson.home.kernel.dk> | 2006-06-23 11:10:39 -0400 |
commit | 9e94cd4fd1812bab45237f998b3c6fa1b24023fd (patch) | |
tree | e196939ff1df56179387f0ec440cc9ce52cf1f55 /fs | |
parent | fd61af0384014ca29428ace7c17a978b755aeddd (diff) |
[PATCH] splice: retrieve mapping after locking the page
Otherwise we could be racing with truncate/mapping removal.
Problem found/fixed by Nick Piggin <npiggin@suse.de>, logic rewritten
by me.
Signed-off-by: Jens Axboe <axboe@suse.de>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/splice.c | 46 |
1 files changed, 29 insertions, 17 deletions
diff --git a/fs/splice.c b/fs/splice.c index a285fd746dc0..05fd2787be98 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -55,31 +55,43 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, | |||
55 | struct pipe_buffer *buf) | 55 | struct pipe_buffer *buf) |
56 | { | 56 | { |
57 | struct page *page = buf->page; | 57 | struct page *page = buf->page; |
58 | struct address_space *mapping = page_mapping(page); | 58 | struct address_space *mapping; |
59 | 59 | ||
60 | lock_page(page); | 60 | lock_page(page); |
61 | 61 | ||
62 | WARN_ON(!PageUptodate(page)); | 62 | mapping = page_mapping(page); |
63 | if (mapping) { | ||
64 | WARN_ON(!PageUptodate(page)); | ||
63 | 65 | ||
64 | /* | 66 | /* |
65 | * At least for ext2 with nobh option, we need to wait on writeback | 67 | * At least for ext2 with nobh option, we need to wait on |
66 | * completing on this page, since we'll remove it from the pagecache. | 68 | * writeback completing on this page, since we'll remove it |
67 | * Otherwise truncate wont wait on the page, allowing the disk | 69 | * from the pagecache. Otherwise truncate wont wait on the |
68 | * blocks to be reused by someone else before we actually wrote our | 70 | * page, allowing the disk blocks to be reused by someone else |
69 | * data to them. fs corruption ensues. | 71 | * before we actually wrote our data to them. fs corruption |
70 | */ | 72 | * ensues. |
71 | wait_on_page_writeback(page); | 73 | */ |
74 | wait_on_page_writeback(page); | ||
72 | 75 | ||
73 | if (PagePrivate(page)) | 76 | if (PagePrivate(page)) |
74 | try_to_release_page(page, mapping_gfp_mask(mapping)); | 77 | try_to_release_page(page, mapping_gfp_mask(mapping)); |
75 | 78 | ||
76 | if (!remove_mapping(mapping, page)) { | 79 | /* |
77 | unlock_page(page); | 80 | * If we succeeded in removing the mapping, set LRU flag |
78 | return 1; | 81 | * and return good. |
82 | */ | ||
83 | if (remove_mapping(mapping, page)) { | ||
84 | buf->flags |= PIPE_BUF_FLAG_LRU; | ||
85 | return 0; | ||
86 | } | ||
79 | } | 87 | } |
80 | 88 | ||
81 | buf->flags |= PIPE_BUF_FLAG_LRU; | 89 | /* |
82 | return 0; | 90 | * Raced with truncate or failed to remove page from current |
91 | * address space, unlock and return failure. | ||
92 | */ | ||
93 | unlock_page(page); | ||
94 | return 1; | ||
83 | } | 95 | } |
84 | 96 | ||
85 | static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, | 97 | static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, |