summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorBob Peterson <rpeterso@redhat.com>2017-09-20 09:30:04 -0400
committerAndreas Gruenbacher <agruenba@redhat.com>2017-10-31 09:26:47 -0400
commitcc555b09d8c3817aeebda43a14ab67049a5653f7 (patch)
treeff41f49be34a10e3205317fcea455c530a449ef1 /fs
parentadbc3ddf28ad9c2742fb9fc82e2aacfd414a16c1 (diff)
GFS2: Take inode off order_write list when setting jdata flag
This patch fixes a deadlock caused when the jdata flag is set for inodes that are already on the ordered write list. Since it is on the ordered write list, log_flush calls gfs2_ordered_write which calls filemap_fdatawrite. But since the inode had the jdata flag set, that calls gfs2_jdata_writepages, which tries to start a new transaction. A new transaction cannot be started because it tries to acquire the log_flush rwsem which is already locked by the log flush operation. The bottom line is: We cannot switch an inode from ordered to jdata until we eliminate any ordered data pages (via log flush) or any log_flush operation afterward will create the circular dependency above. So we need to flush the log before setting the diskflags to switch the file mode, then we need to remove the inode from the ordered writes list. Before this patch, the log flush was done for jdata->ordered, but that's wrong. If we're going from jdata to ordered, we don't need to call gfs2_log_flush because the call to filemap_fdatawrite will do it for us: filemap_fdatawrite() -> __filemap_fdatawrite_range() __filemap_fdatawrite_range() -> do_writepages() do_writepages() -> gfs2_jdata_writepages() gfs2_jdata_writepages() -> gfs2_log_flush() This patch modifies function do_gfs2_set_flags so that if a file has its jdata flag set, and it's already on the ordered write list, the log will be flushed and it will be removed from the list before setting the flag. Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Acked-by: Abhijith Das <adas@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/gfs2/file.c4
1 files changed, 3 insertions, 1 deletions
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 8fefb80fe830..c7aea96144b4 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -267,7 +267,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
267 goto out; 267 goto out;
268 } 268 }
269 if ((flags ^ new_flags) & GFS2_DIF_JDATA) { 269 if ((flags ^ new_flags) & GFS2_DIF_JDATA) {
270 if (flags & GFS2_DIF_JDATA) 270 if (new_flags & GFS2_DIF_JDATA)
271 gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); 271 gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH);
272 error = filemap_fdatawrite(inode->i_mapping); 272 error = filemap_fdatawrite(inode->i_mapping);
273 if (error) 273 if (error)
@@ -275,6 +275,8 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
275 error = filemap_fdatawait(inode->i_mapping); 275 error = filemap_fdatawait(inode->i_mapping);
276 if (error) 276 if (error)
277 goto out; 277 goto out;
278 if (new_flags & GFS2_DIF_JDATA)
279 gfs2_ordered_del_inode(ip);
278 } 280 }
279 error = gfs2_trans_begin(sdp, RES_DINODE, 0); 281 error = gfs2_trans_begin(sdp, RES_DINODE, 0);
280 if (error) 282 if (error)