diff options
Diffstat (limited to 'fs/gfs2/bmap.c')
| -rw-r--r-- | fs/gfs2/bmap.c | 490 | 
1 files changed, 352 insertions, 138 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 5476c066d4e..e6ee5b6e8d9 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -10,6 +10,7 @@  #include <linux/spinlock.h>  #include <linux/completion.h>  #include <linux/buffer_head.h> +#include <linux/blkdev.h>  #include <linux/gfs2_ondisk.h>  #include <linux/crc32.h> @@ -21,6 +22,8 @@  #include "meta_io.h"  #include "quota.h"  #include "rgrp.h" +#include "log.h" +#include "super.h"  #include "trans.h"  #include "dir.h"  #include "util.h" @@ -35,11 +38,6 @@ struct metapath {  	__u16 mp_list[GFS2_MAX_META_HEIGHT];  }; -typedef int (*block_call_t) (struct gfs2_inode *ip, struct buffer_head *dibh, -			     struct buffer_head *bh, __be64 *top, -			     __be64 *bottom, unsigned int height, -			     void *data); -  struct strip_mine {  	int sm_first;  	unsigned int sm_height; @@ -63,7 +61,7 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,  	int release = 0;  	if (!page || page->index) { -		page = grab_cache_page(inode->i_mapping, 0); +		page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);  		if (!page)  			return -ENOMEM;  		release = 1; @@ -96,7 +94,7 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,  	if (!gfs2_is_jdata(ip))  		mark_buffer_dirty(bh);  	if (!gfs2_is_writeback(ip)) -		gfs2_trans_add_bh(ip->i_gl, bh, 0); +		gfs2_trans_add_data(ip->i_gl, bh);  	if (release) {  		unlock_page(page); @@ -136,7 +134,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page)  		   and write it out to disk */  		unsigned int n = 1; -		error = gfs2_alloc_block(ip, &block, &n); +		error = gfs2_alloc_blocks(ip, &block, &n, 0, NULL);  		if (error)  			goto out_brelse;  		if (isdir) { @@ -156,7 +154,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page)  	/*  Set up the pointer to the new block  */ -	gfs2_trans_add_bh(ip->i_gl, dibh, 1); +	gfs2_trans_add_meta(ip->i_gl, dibh);  	di = (struct gfs2_dinode *)dibh->b_data;  	gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); @@ -272,6 +270,30 @@ static inline __be64 *metapointer(unsigned int height, const struct metapath *mp  	return ((__be64 *)(bh->b_data + head_size)) + mp->mp_list[height];  } +static void gfs2_metapath_ra(struct gfs2_glock *gl, +			     const struct buffer_head *bh, const __be64 *pos) +{ +	struct buffer_head *rabh; +	const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size); +	const __be64 *t; + +	for (t = pos; t < endp; t++) { +		if (!*t) +			continue; + +		rabh = gfs2_getbuf(gl, be64_to_cpu(*t), CREATE); +		if (trylock_buffer(rabh)) { +			if (!buffer_uptodate(rabh)) { +				rabh->b_end_io = end_buffer_read_sync; +				submit_bh(READA | REQ_META, rabh); +				continue; +			} +			unlock_buffer(rabh); +		} +		brelse(rabh); +	} +} +  /**   * lookup_metapath - Walk the metadata tree to a specific point   * @ip: The inode @@ -303,7 +325,7 @@ static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp)  		if (!dblock)  			return x + 1; -		ret = gfs2_meta_indirect_buffer(ip, x+1, dblock, 0, &mp->mp_bh[x+1]); +		ret = gfs2_meta_indirect_buffer(ip, x+1, dblock, &mp->mp_bh[x+1]);  		if (ret)  			return ret;  	} @@ -384,7 +406,7 @@ static inline __be64 *gfs2_indirect_init(struct metapath *mp,  	BUG_ON(i < 1);  	BUG_ON(mp->mp_bh[i] != NULL);  	mp->mp_bh[i] = gfs2_meta_new(gl, bn); -	gfs2_trans_add_bh(gl, mp->mp_bh[i], 1); +	gfs2_trans_add_meta(gl, mp->mp_bh[i]);  	gfs2_metatype_set(mp->mp_bh[i], GFS2_METATYPE_IN, GFS2_FORMAT_IN);  	gfs2_buffer_clear_tail(mp->mp_bh[i], sizeof(struct gfs2_meta_header));  	ptr += offset; @@ -431,12 +453,14 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,  {  	struct gfs2_inode *ip = GFS2_I(inode);  	struct gfs2_sbd *sdp = GFS2_SB(inode); +	struct super_block *sb = sdp->sd_vfs;  	struct buffer_head *dibh = mp->mp_bh[0];  	u64 bn, dblock = 0;  	unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0;  	unsigned dblks = 0;  	unsigned ptrs_per_blk;  	const unsigned end_of_metadata = height - 1; +	int ret;  	int eob = 0;  	enum alloc_state state;  	__be64 *ptr; @@ -445,7 +469,7 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,  	BUG_ON(sheight < 1);  	BUG_ON(dibh == NULL); -	gfs2_trans_add_bh(ip->i_gl, dibh, 1); +	gfs2_trans_add_meta(ip->i_gl, dibh);  	if (height == sheight) {  		struct buffer_head *bh; @@ -480,7 +504,7 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,  	do {  		int error;  		n = blks - alloced; -		error = gfs2_alloc_block(ip, &bn, &n); +		error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);  		if (error)  			return error;  		alloced += n; @@ -521,7 +545,7 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,  		/* Branching from existing tree */  		case ALLOC_GROW_DEPTH:  			if (i > 1 && i < height) -				gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[i-1], 1); +				gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[i-1]);  			for (; i < height && n > 0; i++, n--)  				gfs2_indirect_init(mp, ip->i_gl, i,  						   mp->mp_list[i-1], bn++); @@ -533,12 +557,21 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,  		case ALLOC_DATA:  			BUG_ON(n > dblks);  			BUG_ON(mp->mp_bh[end_of_metadata] == NULL); -			gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[end_of_metadata], 1); +			gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[end_of_metadata]);  			dblks = n;  			ptr = metapointer(end_of_metadata, mp);  			dblock = bn;  			while (n-- > 0)  				*ptr++ = cpu_to_be64(bn++); +			if (buffer_zeronew(bh_map)) { +				ret = sb_issue_zeroout(sb, dblock, dblks, +						       GFP_NOFS); +				if (ret) { +					fs_err(sdp, +					       "Failed to zero data buffers\n"); +					clear_buffer_zeronew(bh_map); +				} +			}  			break;  		}  	} while ((state != ALLOC_DATA) || !dblock); @@ -667,76 +700,6 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi  }  /** - * recursive_scan - recursively scan through the end of a file - * @ip: the inode - * @dibh: the dinode buffer - * @mp: the path through the metadata to the point to start - * @height: the height the recursion is at - * @block: the indirect block to look at - * @first: 1 if this is the first block - * @bc: the call to make for each piece of metadata - * @data: data opaque to this function to pass to @bc - * - * When this is first called @height and @block should be zero and - * @first should be 1. - * - * Returns: errno - */ - -static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, -			  struct metapath *mp, unsigned int height, -			  u64 block, int first, block_call_t bc, -			  void *data) -{ -	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); -	struct buffer_head *bh = NULL; -	__be64 *top, *bottom; -	u64 bn; -	int error; -	int mh_size = sizeof(struct gfs2_meta_header); - -	if (!height) { -		error = gfs2_meta_inode_buffer(ip, &bh); -		if (error) -			return error; -		dibh = bh; - -		top = (__be64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + mp->mp_list[0]; -		bottom = (__be64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + sdp->sd_diptrs; -	} else { -		error = gfs2_meta_indirect_buffer(ip, height, block, 0, &bh); -		if (error) -			return error; - -		top = (__be64 *)(bh->b_data + mh_size) + -				  (first ? mp->mp_list[height] : 0); - -		bottom = (__be64 *)(bh->b_data + mh_size) + sdp->sd_inptrs; -	} - -	error = bc(ip, dibh, bh, top, bottom, height, data); -	if (error) -		goto out; - -	if (height < ip->i_height - 1) -		for (; top < bottom; top++, first = 0) { -			if (!*top) -				continue; - -			bn = be64_to_cpu(*top); - -			error = recursive_scan(ip, dibh, mp, height + 1, bn, -					       first, bc, data); -			if (error) -				break; -		} - -out: -	brelse(bh); -	return error; -} - -/**   * do_strip - Look for a layer a particular layer of the file and strip it off   * @ip: the inode   * @dibh: the dinode buffer @@ -744,20 +707,19 @@ out:   * @top: The first pointer in the buffer   * @bottom: One more than the last pointer   * @height: the height this buffer is at - * @data: a pointer to a struct strip_mine + * @sm: a pointer to a struct strip_mine   *   * Returns: errno   */  static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  		    struct buffer_head *bh, __be64 *top, __be64 *bottom, -		    unsigned int height, void *data) +		    unsigned int height, struct strip_mine *sm)  { -	struct strip_mine *sm = data;  	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);  	struct gfs2_rgrp_list rlist;  	u64 bn, bstart; -	u32 blen; +	u32 blen, btotal;  	__be64 *p;  	unsigned int rg_blocks = 0;  	int metadata; @@ -765,6 +727,10 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  	int x;  	int error; +	error = gfs2_rindex_update(sdp); +	if (error) +		return error; +  	if (!*top)  		sm->sm_first = 0; @@ -779,10 +745,8 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  	metadata = (height != ip->i_height - 1);  	if (metadata)  		revokes = (height) ? sdp->sd_inptrs : sdp->sd_diptrs; - -	error = gfs2_rindex_hold(sdp, &ip->i_alloc->al_ri_gh); -	if (error) -		return error; +	else if (ip->i_depth) +		revokes = sdp->sd_inptrs;  	memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));  	bstart = 0; @@ -798,7 +762,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  			blen++;  		else {  			if (bstart) -				gfs2_rlist_add(sdp, &rlist, bstart); +				gfs2_rlist_add(ip, &rlist, bstart);  			bstart = bn;  			blen = 1; @@ -806,7 +770,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  	}  	if (bstart) -		gfs2_rlist_add(sdp, &rlist, bstart); +		gfs2_rlist_add(ip, &rlist, bstart);  	else  		goto out; /* Nothing to do */ @@ -822,6 +786,9 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  	if (error)  		goto out_rlist; +	if (gfs2_rs_active(ip->i_res)) /* needs to be done with the rgrp glock held */ +		gfs2_rs_deltree(ip->i_res); +  	error = gfs2_trans_begin(sdp, rg_blocks + RES_DINODE +  				 RES_INDIRECT + RES_STATFS + RES_QUOTA,  				 revokes); @@ -830,11 +797,12 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  	down_write(&ip->i_rw_mutex); -	gfs2_trans_add_bh(ip->i_gl, dibh, 1); -	gfs2_trans_add_bh(ip->i_gl, bh, 1); +	gfs2_trans_add_meta(ip->i_gl, dibh); +	gfs2_trans_add_meta(ip->i_gl, bh);  	bstart = 0;  	blen = 0; +	btotal = 0;  	for (p = top; p < bottom; p++) {  		if (!*p) @@ -846,10 +814,8 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  			blen++;  		else {  			if (bstart) { -				if (metadata) -					gfs2_free_meta(ip, bstart, blen); -				else -					gfs2_free_data(ip, bstart, blen); +				__gfs2_free_blocks(ip, bstart, blen, metadata); +				btotal += blen;  			}  			bstart = bn; @@ -860,12 +826,14 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,  		gfs2_add_inode_blocks(&ip->i_inode, -1);  	}  	if (bstart) { -		if (metadata) -			gfs2_free_meta(ip, bstart, blen); -		else -			gfs2_free_data(ip, bstart, blen); +		__gfs2_free_blocks(ip, bstart, blen, metadata); +		btotal += blen;  	} +	gfs2_statfs_change(sdp, 0, +btotal, 0); +	gfs2_quota_change(ip, -(s64)btotal, ip->i_inode.i_uid, +			  ip->i_inode.i_gid); +  	ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;  	gfs2_dinode_out(ip, dibh->b_data); @@ -879,11 +847,82 @@ out_rg_gunlock:  out_rlist:  	gfs2_rlist_free(&rlist);  out: -	gfs2_glock_dq_uninit(&ip->i_alloc->al_ri_gh);  	return error;  }  /** + * recursive_scan - recursively scan through the end of a file + * @ip: the inode + * @dibh: the dinode buffer + * @mp: the path through the metadata to the point to start + * @height: the height the recursion is at + * @block: the indirect block to look at + * @first: 1 if this is the first block + * @sm: data opaque to this function to pass to @bc + * + * When this is first called @height and @block should be zero and + * @first should be 1. + * + * Returns: errno + */ + +static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, +			  struct metapath *mp, unsigned int height, +			  u64 block, int first, struct strip_mine *sm) +{ +	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); +	struct buffer_head *bh = NULL; +	__be64 *top, *bottom; +	u64 bn; +	int error; +	int mh_size = sizeof(struct gfs2_meta_header); + +	if (!height) { +		error = gfs2_meta_inode_buffer(ip, &bh); +		if (error) +			return error; +		dibh = bh; + +		top = (__be64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + mp->mp_list[0]; +		bottom = (__be64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + sdp->sd_diptrs; +	} else { +		error = gfs2_meta_indirect_buffer(ip, height, block, &bh); +		if (error) +			return error; + +		top = (__be64 *)(bh->b_data + mh_size) + +				  (first ? mp->mp_list[height] : 0); + +		bottom = (__be64 *)(bh->b_data + mh_size) + sdp->sd_inptrs; +	} + +	error = do_strip(ip, dibh, bh, top, bottom, height, sm); +	if (error) +		goto out; + +	if (height < ip->i_height - 1) { + +		gfs2_metapath_ra(ip->i_gl, bh, top); + +		for (; top < bottom; top++, first = 0) { +			if (!*top) +				continue; + +			bn = be64_to_cpu(*top); + +			error = recursive_scan(ip, dibh, mp, height + 1, bn, +					       first, sm); +			if (error) +				break; +		} +	} +out: +	brelse(bh); +	return error; +} + + +/**   * gfs2_block_truncate_page - Deal with zeroing out data for truncate   *   * This is partly borrowed from ext3. @@ -899,7 +938,7 @@ static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from)  	struct page *page;  	int err; -	page = grab_cache_page(mapping, index); +	page = find_or_create_page(mapping, index, GFP_NOFS);  	if (!page)  		return 0; @@ -943,7 +982,7 @@ static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from)  	}  	if (!gfs2_is_writeback(ip)) -		gfs2_trans_add_bh(ip->i_gl, bh, 0); +		gfs2_trans_add_data(ip->i_gl, bh);  	zero_user(page, offset, length);  	mark_buffer_dirty(bh); @@ -953,6 +992,41 @@ unlock:  	return err;  } +#define GFS2_JTRUNC_REVOKES 8192 + +/** + * 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. + */ + +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 - 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); @@ -962,8 +1036,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; @@ -971,7 +1047,7 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)  	if (error)  		goto out; -	gfs2_trans_add_bh(ip->i_gl, dibh, 1); +	gfs2_trans_add_meta(ip->i_gl, dibh);  	if (gfs2_is_stuffed(ip)) {  		gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize); @@ -988,7 +1064,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, newsize); + +	if (error) { +		brelse(dibh); +		return error; +	} +  out_brelse:  	brelse(dibh);  out: @@ -1010,27 +1095,26 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 size)  		lblock = (size - 1) >> sdp->sd_sb.sb_bsize_shift;  	find_metapath(sdp, lblock, &mp, ip->i_height); -	if (!gfs2_alloc_get(ip)) -		return -ENOMEM; +	error = gfs2_rindex_update(sdp); +	if (error) +		return error; -	error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); +	error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE);  	if (error) -		goto out; +		return error;  	while (height--) {  		struct strip_mine sm;  		sm.sm_first = !!size;  		sm.sm_height = height; -		error = recursive_scan(ip, NULL, &mp, 0, 0, 1, do_strip, &sm); +		error = recursive_scan(ip, NULL, &mp, 0, 0, 1, &sm);  		if (error)  			break;  	}  	gfs2_quota_unhold(ip); -out: -	gfs2_alloc_put(ip);  	return error;  } @@ -1054,11 +1138,12 @@ static int trunc_end(struct gfs2_inode *ip)  		ip->i_height = 0;  		ip->i_goal = ip->i_no_addr;  		gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); +		gfs2_ordered_del_inode(ip);  	}  	ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;  	ip->i_diskflags &= ~GFS2_DIF_TRUNC_IN_PROG; -	gfs2_trans_add_bh(ip->i_gl, dibh, 1); +	gfs2_trans_add_meta(ip->i_gl, dibh);  	gfs2_dinode_out(ip, dibh->b_data);  	brelse(dibh); @@ -1121,7 +1206,7 @@ void gfs2_trim_blocks(struct inode *inode)   * earlier versions of GFS2 have a bug in the stuffed file reading   * code which will result in a buffer overrun if the size is larger   * than the max stuffed file size. In order to prevent this from - * occuring, such files are unstuffed, but in other cases we can + * occurring, such files are unstuffed, but in other cases we can   * just update the inode size directly.   *   * Returns: 0 on success, or -ve on error @@ -1131,31 +1216,30 @@ static int do_grow(struct inode *inode, u64 size)  {  	struct gfs2_inode *ip = GFS2_I(inode);  	struct gfs2_sbd *sdp = GFS2_SB(inode); +	struct gfs2_alloc_parms ap = { .target = 1, };  	struct buffer_head *dibh; -	struct gfs2_alloc *al = NULL;  	int error; +	int unstuff = 0;  	if (gfs2_is_stuffed(ip) &&  	    (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) { -		al = gfs2_alloc_get(ip); -		if (al == NULL) -			return -ENOMEM; -  		error = gfs2_quota_lock_check(ip);  		if (error) -			goto do_grow_alloc_put; +			return error; -		al->al_requested = 1; -		error = gfs2_inplace_reserve(ip); +		error = gfs2_inplace_reserve(ip, &ap);  		if (error)  			goto do_grow_qunlock; +		unstuff = 1;  	} -	error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT, 0); +	error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT + +				 (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF ? +				  0 : RES_QUOTA), 0);  	if (error)  		goto do_grow_release; -	if (al) { +	if (unstuff) {  		error = gfs2_unstuff_dinode(ip, NULL);  		if (error)  			goto do_end_trans; @@ -1167,19 +1251,17 @@ static int do_grow(struct inode *inode, u64 size)  	i_size_write(inode, size);  	ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; -	gfs2_trans_add_bh(ip->i_gl, dibh, 1); +	gfs2_trans_add_meta(ip->i_gl, dibh);  	gfs2_dinode_out(ip, dibh->b_data);  	brelse(dibh);  do_end_trans:  	gfs2_trans_end(sdp);  do_grow_release: -	if (al) { +	if (unstuff) {  		gfs2_inplace_release(ip);  do_grow_qunlock:  		gfs2_quota_unlock(ip); -do_grow_alloc_put: -		gfs2_alloc_put(ip);  	}  	return error;  } @@ -1198,6 +1280,7 @@ do_grow_alloc_put:  int gfs2_setattr_size(struct inode *inode, u64 newsize)  { +	struct gfs2_inode *ip = GFS2_I(inode);  	int ret;  	u64 oldsize; @@ -1207,11 +1290,27 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)  	if (ret)  		return ret; +	ret = get_write_access(inode); +	if (ret) +		return ret; + +	inode_dio_wait(inode); + +	ret = gfs2_rs_alloc(ip); +	if (ret) +		goto out; +  	oldsize = inode->i_size; -	if (newsize >= oldsize) -		return do_grow(inode, newsize); +	if (newsize >= oldsize) { +		ret = do_grow(inode, newsize); +		goto out; +	} -	return do_shrink(inode, oldsize, newsize); +	gfs2_rs_deltree(ip->i_res); +	ret = do_shrink(inode, oldsize, newsize); +out: +	put_write_access(inode); +	return ret;  }  int gfs2_truncatei_resume(struct gfs2_inode *ip) @@ -1229,6 +1328,121 @@ int gfs2_file_dealloc(struct gfs2_inode *ip)  }  /** + * gfs2_free_journal_extents - Free cached journal bmap info + * @jd: The journal + * + */ + +void gfs2_free_journal_extents(struct gfs2_jdesc *jd) +{ +	struct gfs2_journal_extent *jext; + +	while(!list_empty(&jd->extent_list)) { +		jext = list_entry(jd->extent_list.next, struct gfs2_journal_extent, list); +		list_del(&jext->list); +		kfree(jext); +	} +} + +/** + * gfs2_add_jextent - Add or merge a new extent to extent cache + * @jd: The journal descriptor + * @lblock: The logical block at start of new extent + * @dblock: The physical block at start of new extent + * @blocks: Size of extent in fs blocks + * + * Returns: 0 on success or -ENOMEM + */ + +static int gfs2_add_jextent(struct gfs2_jdesc *jd, u64 lblock, u64 dblock, u64 blocks) +{ +	struct gfs2_journal_extent *jext; + +	if (!list_empty(&jd->extent_list)) { +		jext = list_entry(jd->extent_list.prev, struct gfs2_journal_extent, list); +		if ((jext->dblock + jext->blocks) == dblock) { +			jext->blocks += blocks; +			return 0; +		} +	} + +	jext = kzalloc(sizeof(struct gfs2_journal_extent), GFP_NOFS); +	if (jext == NULL) +		return -ENOMEM; +	jext->dblock = dblock; +	jext->lblock = lblock; +	jext->blocks = blocks; +	list_add_tail(&jext->list, &jd->extent_list); +	jd->nr_extents++; +	return 0; +} + +/** + * gfs2_map_journal_extents - Cache journal bmap info + * @sdp: The super block + * @jd: The journal to map + * + * Create a reusable "extent" mapping from all logical + * blocks to all physical blocks for the given journal.  This will save + * us time when writing journal blocks.  Most journals will have only one + * extent that maps all their logical blocks.  That's because gfs2.mkfs + * arranges the journal blocks sequentially to maximize performance. + * So the extent would map the first block for the entire file length. + * However, gfs2_jadd can happen while file activity is happening, so + * those journals may not be sequential.  Less likely is the case where + * the users created their own journals by mounting the metafs and + * laying it out.  But it's still possible.  These journals might have + * several extents. + * + * Returns: 0 on success, or error on failure + */ + +int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd) +{ +	u64 lblock = 0; +	u64 lblock_stop; +	struct gfs2_inode *ip = GFS2_I(jd->jd_inode); +	struct buffer_head bh; +	unsigned int shift = sdp->sd_sb.sb_bsize_shift; +	u64 size; +	int rc; + +	lblock_stop = i_size_read(jd->jd_inode) >> shift; +	size = (lblock_stop - lblock) << shift; +	jd->nr_extents = 0; +	WARN_ON(!list_empty(&jd->extent_list)); + +	do { +		bh.b_state = 0; +		bh.b_blocknr = 0; +		bh.b_size = size; +		rc = gfs2_block_map(jd->jd_inode, lblock, &bh, 0); +		if (rc || !buffer_mapped(&bh)) +			goto fail; +		rc = gfs2_add_jextent(jd, lblock, bh.b_blocknr, bh.b_size >> shift); +		if (rc) +			goto fail; +		size -= bh.b_size; +		lblock += (bh.b_size >> ip->i_inode.i_blkbits); +	} while(size > 0); + +	fs_info(sdp, "journal %d mapped with %u extents\n", jd->jd_jid, +		jd->nr_extents); +	return 0; + +fail: +	fs_warn(sdp, "error %d mapping journal %u at offset %llu (extent %u)\n", +		rc, jd->jd_jid, +		(unsigned long long)(i_size_read(jd->jd_inode) - size), +		jd->nr_extents); +	fs_warn(sdp, "bmap=%d lblock=%llu block=%llu, state=0x%08lx, size=%llu\n", +		rc, (unsigned long long)lblock, (unsigned long long)bh.b_blocknr, +		bh.b_state, (unsigned long long)bh.b_size); +	gfs2_free_journal_extents(jd); +	return rc; +} + +/**   * gfs2_write_alloc_required - figure out if a write will require an allocation   * @ip: the file being written to   * @offset: the offset to write to  | 
