diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2009-04-14 13:48:39 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-04-15 06:10:12 -0400 |
commit | 328eaaba4e41a04c1dc4679d65bea3fee4349d86 (patch) | |
tree | 171bf86b590a841729bab11feec874869bcab49e | |
parent | eb443e5a25d43996deb62b9bcee1a4ce5dea2ead (diff) |
ocfs2: fix i_mutex locking in ocfs2_splice_to_file()
Rearrange locking of i_mutex on destination and call to
ocfs2_rw_lock() so locks are only held while buffers are copied with
the pipe_to_file() actor, and not while waiting for more data on the
pipe.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r-- | fs/ocfs2/file.c | 94 | ||||
-rw-r--r-- | fs/splice.c | 5 | ||||
-rw-r--r-- | include/linux/splice.h | 2 |
3 files changed, 79 insertions, 22 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 8672b9536039..c2a87c885b73 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c | |||
@@ -1912,6 +1912,22 @@ out_sems: | |||
1912 | return written ? written : ret; | 1912 | return written ? written : ret; |
1913 | } | 1913 | } |
1914 | 1914 | ||
1915 | static int ocfs2_splice_to_file(struct pipe_inode_info *pipe, | ||
1916 | struct file *out, | ||
1917 | struct splice_desc *sd) | ||
1918 | { | ||
1919 | int ret; | ||
1920 | |||
1921 | ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, &sd->pos, | ||
1922 | sd->total_len, 0, NULL); | ||
1923 | if (ret < 0) { | ||
1924 | mlog_errno(ret); | ||
1925 | return ret; | ||
1926 | } | ||
1927 | |||
1928 | return splice_from_pipe_feed(pipe, sd, pipe_to_file); | ||
1929 | } | ||
1930 | |||
1915 | static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe, | 1931 | static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe, |
1916 | struct file *out, | 1932 | struct file *out, |
1917 | loff_t *ppos, | 1933 | loff_t *ppos, |
@@ -1919,38 +1935,76 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe, | |||
1919 | unsigned int flags) | 1935 | unsigned int flags) |
1920 | { | 1936 | { |
1921 | int ret; | 1937 | int ret; |
1922 | struct inode *inode = out->f_path.dentry->d_inode; | 1938 | struct address_space *mapping = out->f_mapping; |
1939 | struct inode *inode = mapping->host; | ||
1940 | struct splice_desc sd = { | ||
1941 | .total_len = len, | ||
1942 | .flags = flags, | ||
1943 | .pos = *ppos, | ||
1944 | .u.file = out, | ||
1945 | }; | ||
1923 | 1946 | ||
1924 | mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", out, pipe, | 1947 | mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", out, pipe, |
1925 | (unsigned int)len, | 1948 | (unsigned int)len, |
1926 | out->f_path.dentry->d_name.len, | 1949 | out->f_path.dentry->d_name.len, |
1927 | out->f_path.dentry->d_name.name); | 1950 | out->f_path.dentry->d_name.name); |
1928 | 1951 | ||
1929 | mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); | 1952 | if (pipe->inode) |
1953 | mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_PARENT); | ||
1930 | 1954 | ||
1931 | ret = ocfs2_rw_lock(inode, 1); | 1955 | splice_from_pipe_begin(&sd); |
1932 | if (ret < 0) { | 1956 | do { |
1933 | mlog_errno(ret); | 1957 | ret = splice_from_pipe_next(pipe, &sd); |
1934 | goto out; | 1958 | if (ret <= 0) |
1935 | } | 1959 | break; |
1936 | 1960 | ||
1937 | ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0, | 1961 | mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); |
1938 | NULL); | 1962 | ret = ocfs2_rw_lock(inode, 1); |
1939 | if (ret < 0) { | 1963 | if (ret < 0) |
1940 | mlog_errno(ret); | 1964 | mlog_errno(ret); |
1941 | goto out_unlock; | 1965 | else { |
1942 | } | 1966 | ret = ocfs2_splice_to_file(pipe, out, &sd); |
1967 | ocfs2_rw_unlock(inode, 1); | ||
1968 | } | ||
1969 | mutex_unlock(&inode->i_mutex); | ||
1970 | } while (ret > 0); | ||
1971 | splice_from_pipe_end(pipe, &sd); | ||
1943 | 1972 | ||
1944 | if (pipe->inode) | 1973 | if (pipe->inode) |
1945 | mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD); | ||
1946 | ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags); | ||
1947 | if (pipe->inode) | ||
1948 | mutex_unlock(&pipe->inode->i_mutex); | 1974 | mutex_unlock(&pipe->inode->i_mutex); |
1949 | 1975 | ||
1950 | out_unlock: | 1976 | if (sd.num_spliced) |
1951 | ocfs2_rw_unlock(inode, 1); | 1977 | ret = sd.num_spliced; |
1952 | out: | 1978 | |
1953 | mutex_unlock(&inode->i_mutex); | 1979 | if (ret > 0) { |
1980 | unsigned long nr_pages; | ||
1981 | |||
1982 | *ppos += ret; | ||
1983 | nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
1984 | |||
1985 | /* | ||
1986 | * If file or inode is SYNC and we actually wrote some data, | ||
1987 | * sync it. | ||
1988 | */ | ||
1989 | if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) { | ||
1990 | int err; | ||
1991 | |||
1992 | mutex_lock(&inode->i_mutex); | ||
1993 | err = ocfs2_rw_lock(inode, 1); | ||
1994 | if (err < 0) { | ||
1995 | mlog_errno(err); | ||
1996 | } else { | ||
1997 | err = generic_osync_inode(inode, mapping, | ||
1998 | OSYNC_METADATA|OSYNC_DATA); | ||
1999 | ocfs2_rw_unlock(inode, 1); | ||
2000 | } | ||
2001 | mutex_unlock(&inode->i_mutex); | ||
2002 | |||
2003 | if (err) | ||
2004 | ret = err; | ||
2005 | } | ||
2006 | balance_dirty_pages_ratelimited_nr(mapping, nr_pages); | ||
2007 | } | ||
1954 | 2008 | ||
1955 | mlog_exit(ret); | 2009 | mlog_exit(ret); |
1956 | return ret; | 2010 | return ret; |
diff --git a/fs/splice.c b/fs/splice.c index a1f595b9db40..584b2b7a1dbe 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -555,8 +555,8 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, | |||
555 | * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create | 555 | * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create |
556 | * a new page in the output file page cache and fill/dirty that. | 556 | * a new page in the output file page cache and fill/dirty that. |
557 | */ | 557 | */ |
558 | static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | 558 | int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, |
559 | struct splice_desc *sd) | 559 | struct splice_desc *sd) |
560 | { | 560 | { |
561 | struct file *file = sd->u.file; | 561 | struct file *file = sd->u.file; |
562 | struct address_space *mapping = file->f_mapping; | 562 | struct address_space *mapping = file->f_mapping; |
@@ -600,6 +600,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | |||
600 | out: | 600 | out: |
601 | return ret; | 601 | return ret; |
602 | } | 602 | } |
603 | EXPORT_SYMBOL(pipe_to_file); | ||
603 | 604 | ||
604 | static void wakeup_pipe_writers(struct pipe_inode_info *pipe) | 605 | static void wakeup_pipe_writers(struct pipe_inode_info *pipe) |
605 | { | 606 | { |
diff --git a/include/linux/splice.h b/include/linux/splice.h index 8fc2a635586e..5f3faa9d15ae 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h | |||
@@ -75,6 +75,8 @@ extern int splice_from_pipe_next(struct pipe_inode_info *, | |||
75 | extern void splice_from_pipe_begin(struct splice_desc *); | 75 | extern void splice_from_pipe_begin(struct splice_desc *); |
76 | extern void splice_from_pipe_end(struct pipe_inode_info *, | 76 | extern void splice_from_pipe_end(struct pipe_inode_info *, |
77 | struct splice_desc *); | 77 | struct splice_desc *); |
78 | extern int pipe_to_file(struct pipe_inode_info *, struct pipe_buffer *, | ||
79 | struct splice_desc *); | ||
78 | 80 | ||
79 | extern ssize_t splice_to_pipe(struct pipe_inode_info *, | 81 | extern ssize_t splice_to_pipe(struct pipe_inode_info *, |
80 | struct splice_pipe_desc *); | 82 | struct splice_pipe_desc *); |