GFS2: Fix truncation of journaled data files
authorSteven Whitehouse <swhiteho@redhat.com>
Tue, 13 Nov 2012 09:50:28 +0000 (09:50 +0000)
committerSteven Whitehouse <swhiteho@redhat.com>
Tue, 13 Nov 2012 09:50:28 +0000 (09:50 +0000)
This patch fixes an issue relating to not having enough revokes
available when truncating journaled data files. In order to ensure
that we do no run out, the truncation is broken into separate pieces
if it is large enough.

Tested using fsx on a journaled data file.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
fs/gfs2/bmap.c

index de70e52caf3adc7660c2fb19f4833b701cd7c0de..a68e91bcef3ddf7d005665955ecff7909ccb171f 100644 (file)
@@ -991,6 +991,41 @@ unlock:
        return err;
 }
 
+/**
+ * gfs2_journaled_truncate - Wrapper for truncate_pagecache for jdata files
+ * @inode: The inode being truncated
+ * @oldsize: The original (larger) size
+ * @newsize: The new smaller size
+ *
+ * With jdata files, we have to journal a revoke for each block which is
+ * truncated. As a result, we need to split this into separate transactions
+ * if the number of pages being truncated gets too large.
+ */
+
+#define GFS2_JTRUNC_REVOKES 8192
+
+static int gfs2_journaled_truncate(struct inode *inode, u64 oldsize, u64 newsize)
+{
+       struct gfs2_sbd *sdp = GFS2_SB(inode);
+       u64 max_chunk = GFS2_JTRUNC_REVOKES * sdp->sd_vfs->s_blocksize;
+       u64 chunk;
+       int error;
+
+       while (oldsize != newsize) {
+               chunk = oldsize - newsize;
+               if (chunk > max_chunk)
+                       chunk = max_chunk;
+               truncate_pagecache(inode, oldsize, oldsize - chunk);
+               oldsize -= chunk;
+               gfs2_trans_end(sdp);
+               error = gfs2_trans_begin(sdp, RES_DINODE, GFS2_JTRUNC_REVOKES);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
 static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
 {
        struct gfs2_inode *ip = GFS2_I(inode);
@@ -1000,8 +1035,10 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
        int journaled = gfs2_is_jdata(ip);
        int error;
 
-       error = gfs2_trans_begin(sdp,
-                                RES_DINODE + (journaled ? RES_JDATA : 0), 0);
+       if (journaled)
+               error = gfs2_trans_begin(sdp, RES_DINODE + RES_JDATA, GFS2_JTRUNC_REVOKES);
+       else
+               error = gfs2_trans_begin(sdp, RES_DINODE, 0);
        if (error)
                return error;
 
@@ -1026,7 +1063,16 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
        ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
        gfs2_dinode_out(ip, dibh->b_data);
 
-       truncate_pagecache(inode, oldsize, newsize);
+       if (journaled)
+               error = gfs2_journaled_truncate(inode, oldsize, newsize);
+       else
+               truncate_pagecache(inode, oldsize, newsize);
+
+       if (error) {
+               brelse(dibh);
+               return error;
+       }
+
 out_brelse:
        brelse(dibh);
 out:
This page took 0.041226 seconds and 5 git commands to generate.