aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-06-20 09:01:12 -0400
committerJens Axboe <axboe@nelson.home.kernel.dk>2006-06-23 11:10:39 -0400
commit9e94cd4fd1812bab45237f998b3c6fa1b24023fd (patch)
treee196939ff1df56179387f0ec440cc9ce52cf1f55 /fs
parentfd61af0384014ca29428ace7c17a978b755aeddd (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.c46
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
85static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, 97static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,