diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2009-04-06 11:41:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-07 11:34:46 -0400 |
commit | 7bfac9ecf0585962fe13584f5cf526d8c8e76f17 (patch) | |
tree | 7e7c98311bb0d4c26e514b337216bbc8ade8d078 /fs/ocfs2 | |
parent | 612392307cb09e49051225092cbbd7049bd8db93 (diff) |
splice: fix deadlock in splicing to file
There's a possible deadlock in generic_file_splice_write(),
splice_from_pipe() and ocfs2_file_splice_write():
- task A calls generic_file_splice_write()
- this calls inode_double_lock(), which locks i_mutex on both
pipe->inode and target inode
- ordering depends on inode pointers, can happen that pipe->inode is
locked first
- __splice_from_pipe() needs more data, calls pipe_wait()
- this releases lock on pipe->inode, goes to interruptible sleep
- task B calls generic_file_splice_write(), similarly to the first
- this locks pipe->inode, then tries to lock inode, but that is
already held by task A
- task A is interrupted, it tries to lock pipe->inode, but fails, as
it is already held by task B
- ABBA deadlock
Fix this by explicitly ordering locks: the outer lock must be on
target inode and the inner lock (which is later unlocked and relocked)
must be on pipe->inode. This is OK, pipe inodes and target inodes
form two nonoverlapping sets, generic_file_splice_write() and friends
are not called with a target which is a pipe.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Acked-by: Mark Fasheh <mfasheh@suse.com>
Acked-by: Jens Axboe <jens.axboe@oracle.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r-- | fs/ocfs2/file.c | 8 |
1 files changed, 6 insertions, 2 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index a5887df2cd8a..8672b9536039 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c | |||
@@ -1926,7 +1926,7 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe, | |||
1926 | out->f_path.dentry->d_name.len, | 1926 | out->f_path.dentry->d_name.len, |
1927 | out->f_path.dentry->d_name.name); | 1927 | out->f_path.dentry->d_name.name); |
1928 | 1928 | ||
1929 | inode_double_lock(inode, pipe->inode); | 1929 | mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); |
1930 | 1930 | ||
1931 | ret = ocfs2_rw_lock(inode, 1); | 1931 | ret = ocfs2_rw_lock(inode, 1); |
1932 | if (ret < 0) { | 1932 | if (ret < 0) { |
@@ -1941,12 +1941,16 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe, | |||
1941 | goto out_unlock; | 1941 | goto out_unlock; |
1942 | } | 1942 | } |
1943 | 1943 | ||
1944 | if (pipe->inode) | ||
1945 | mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD); | ||
1944 | ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags); | 1946 | ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags); |
1947 | if (pipe->inode) | ||
1948 | mutex_unlock(&pipe->inode->i_mutex); | ||
1945 | 1949 | ||
1946 | out_unlock: | 1950 | out_unlock: |
1947 | ocfs2_rw_unlock(inode, 1); | 1951 | ocfs2_rw_unlock(inode, 1); |
1948 | out: | 1952 | out: |
1949 | inode_double_unlock(inode, pipe->inode); | 1953 | mutex_unlock(&inode->i_mutex); |
1950 | 1954 | ||
1951 | mlog_exit(ret); | 1955 | mlog_exit(ret); |
1952 | return ret; | 1956 | return ret; |