mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 04:06:22 +00:00
[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>
This commit is contained in:
parent
fd61af0384
commit
9e94cd4fd1
1 changed files with 29 additions and 17 deletions
46
fs/splice.c
46
fs/splice.c
|
@ -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);
|
||||||
|
|
||||||
WARN_ON(!PageUptodate(page));
|
mapping = page_mapping(page);
|
||||||
|
if (mapping) {
|
||||||
|
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;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf->flags |= PIPE_BUF_FLAG_LRU;
|
/*
|
||||||
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,
|
||||||
|
|
Loading…
Reference in a new issue