diff options
author | Jan Kara <jack@suse.cz> | 2012-06-12 10:20:37 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-07-31 01:45:47 -0400 |
commit | 14da9200140f8d722ad1767dfabadebd8b34f2ad (patch) | |
tree | ea5d88b091999f7a64af0b9d335d7cad4c79edfb /fs/splice.c | |
parent | 5d37e9e6dec65cd21be68ee92de99686213e916b (diff) |
fs: Protect write paths by sb_start_write - sb_end_write
There are several entry points which dirty pages in a filesystem. mmap
(handled by block_page_mkwrite()), buffered write (handled by
__generic_file_aio_write()), splice write (generic_file_splice_write),
truncate, and fallocate (these can dirty last partial page - handled inside
each filesystem separately). Protect these places with sb_start_write() and
sb_end_write().
->page_mkwrite() calls are particularly complex since they are called with
mmap_sem held and thus we cannot use standard sb_start_write() due to lock
ordering constraints. We solve the problem by using a special freeze protection
sb_start_pagefault() which ranks below mmap_sem.
BugLink: https://bugs.launchpad.net/bugs/897421
Tested-by: Kamal Mostafa <kamal@canonical.com>
Tested-by: Peter M. Petrakis <peter.petrakis@canonical.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
Tested-by: Massimo Morana <massimo.morana@canonical.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/fs/splice.c b/fs/splice.c index 7bf08fa22ec9..41514dd89462 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -996,6 +996,8 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, | |||
996 | }; | 996 | }; |
997 | ssize_t ret; | 997 | ssize_t ret; |
998 | 998 | ||
999 | sb_start_write(inode->i_sb); | ||
1000 | |||
999 | pipe_lock(pipe); | 1001 | pipe_lock(pipe); |
1000 | 1002 | ||
1001 | splice_from_pipe_begin(&sd); | 1003 | splice_from_pipe_begin(&sd); |
@@ -1034,6 +1036,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, | |||
1034 | *ppos += ret; | 1036 | *ppos += ret; |
1035 | balance_dirty_pages_ratelimited_nr(mapping, nr_pages); | 1037 | balance_dirty_pages_ratelimited_nr(mapping, nr_pages); |
1036 | } | 1038 | } |
1039 | sb_end_write(inode->i_sb); | ||
1037 | 1040 | ||
1038 | return ret; | 1041 | return ret; |
1039 | } | 1042 | } |