Skip to content

Commit

Permalink
GFS2: Fix truncation of journaled data files
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Steven Whitehouse committed Nov 13, 2012
1 parent 9dbe961 commit fa731fc
Showing 1 changed file with 49 additions and 3 deletions.
52 changes: 49 additions & 3 deletions fs/gfs2/bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,41 @@ static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from)
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);
Expand All @@ -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;

Expand All @@ -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:
Expand Down

0 comments on commit fa731fc

Please sign in to comment.