Commit 9e94cd4f authored by Jens Axboe's avatar Jens Axboe Committed by Jens Axboe

[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: default avatarJens Axboe <axboe@suse.de>
parent fd61af03
...@@ -55,31 +55,43 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, ...@@ -55,31 +55,43 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
struct page *page = buf->page; struct page *page = buf->page;
struct address_space *mapping = page_mapping(page); struct address_space *mapping;
lock_page(page); lock_page(page);
mapping = page_mapping(page);
if (mapping) {
WARN_ON(!PageUptodate(page)); WARN_ON(!PageUptodate(page));
/* /*
* At least for ext2 with nobh option, we need to wait on writeback * At least for ext2 with nobh option, we need to wait on
* completing on this page, since we'll remove it from the pagecache. * writeback completing on this page, since we'll remove it
* Otherwise truncate wont wait on the page, allowing the disk * from the pagecache. Otherwise truncate wont wait on the
* blocks to be reused by someone else before we actually wrote our * page, allowing the disk blocks to be reused by someone else
* data to them. fs corruption ensues. * before we actually wrote our data to them. fs corruption
* ensues.
*/ */
wait_on_page_writeback(page); wait_on_page_writeback(page);
if (PagePrivate(page)) if (PagePrivate(page))
try_to_release_page(page, mapping_gfp_mask(mapping)); try_to_release_page(page, mapping_gfp_mask(mapping));
if (!remove_mapping(mapping, page)) { /*
unlock_page(page); * If we succeeded in removing the mapping, set LRU flag
return 1; * and return good.
} */
if (remove_mapping(mapping, page)) {
buf->flags |= PIPE_BUF_FLAG_LRU; buf->flags |= PIPE_BUF_FLAG_LRU;
return 0; return 0;
}
}
/*
* Raced with truncate or failed to remove page from current
* address space, unlock and return failure.
*/
unlock_page(page);
return 1;
} }
static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment