diff options
Diffstat (limited to 'fs/ocfs2/suballoc.c')
| -rw-r--r-- | fs/ocfs2/suballoc.c | 219 | 
1 files changed, 194 insertions, 25 deletions
| diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index a8e6a95a353..8a286f54dca 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -57,11 +57,28 @@ struct ocfs2_suballoc_result {  	u64		sr_bg_blkno;	/* The bg we allocated from.  Set  					   to 0 when a block group is  					   contiguous. */ +	u64		sr_bg_stable_blkno; /* +					     * Doesn't change, always +					     * set to target block +					     * group descriptor +					     * block. +					     */  	u64		sr_blkno;	/* The first allocated block */  	unsigned int	sr_bit_offset;	/* The bit in the bg */  	unsigned int	sr_bits;	/* How many bits we claimed */  }; +static u64 ocfs2_group_from_res(struct ocfs2_suballoc_result *res) +{ +	if (res->sr_blkno == 0) +		return 0; + +	if (res->sr_bg_blkno) +		return res->sr_bg_blkno; + +	return ocfs2_which_suballoc_group(res->sr_blkno, res->sr_bit_offset); +} +  static inline void ocfs2_debug_bg(struct ocfs2_group_desc *bg);  static inline void ocfs2_debug_suballoc_inode(struct ocfs2_dinode *fe);  static inline u16 ocfs2_find_victim_chain(struct ocfs2_chain_list *cl); @@ -138,6 +155,10 @@ void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)  	brelse(ac->ac_bh);  	ac->ac_bh = NULL;  	ac->ac_resv = NULL; +	if (ac->ac_find_loc_priv) { +		kfree(ac->ac_find_loc_priv); +		ac->ac_find_loc_priv = NULL; +	}  }  void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac) @@ -1678,6 +1699,15 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,  	if (!ret)  		ocfs2_bg_discontig_fix_result(ac, gd, res); +	/* +	 * sr_bg_blkno might have been changed by +	 * ocfs2_bg_discontig_fix_result +	 */ +	res->sr_bg_stable_blkno = group_bh->b_blocknr; + +	if (ac->ac_find_loc_only) +		goto out_loc_only; +  	ret = ocfs2_alloc_dinode_update_counts(alloc_inode, handle, ac->ac_bh,  					       res->sr_bits,  					       le16_to_cpu(gd->bg_chain)); @@ -1691,6 +1721,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,  	if (ret < 0)  		mlog_errno(ret); +out_loc_only:  	*bits_left = le16_to_cpu(gd->bg_free_bits_count);  out: @@ -1708,7 +1739,6 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,  {  	int status;  	u16 chain; -	u32 tmp_used;  	u64 next_group;  	struct inode *alloc_inode = ac->ac_inode;  	struct buffer_head *group_bh = NULL; @@ -1770,6 +1800,11 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,  	if (!status)  		ocfs2_bg_discontig_fix_result(ac, bg, res); +	/* +	 * sr_bg_blkno might have been changed by +	 * ocfs2_bg_discontig_fix_result +	 */ +	res->sr_bg_stable_blkno = group_bh->b_blocknr;  	/*  	 * Keep track of previous block descriptor read. When @@ -1796,22 +1831,17 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,  		}  	} -	/* Ok, claim our bits now: set the info on dinode, chainlist -	 * and then the group */ -	status = ocfs2_journal_access_di(handle, -					 INODE_CACHE(alloc_inode), -					 ac->ac_bh, -					 OCFS2_JOURNAL_ACCESS_WRITE); -	if (status < 0) { +	if (ac->ac_find_loc_only) +		goto out_loc_only; + +	status = ocfs2_alloc_dinode_update_counts(alloc_inode, handle, +						  ac->ac_bh, res->sr_bits, +						  chain); +	if (status) {  		mlog_errno(status);  		goto bail;  	} -	tmp_used = le32_to_cpu(fe->id1.bitmap1.i_used); -	fe->id1.bitmap1.i_used = cpu_to_le32(res->sr_bits + tmp_used); -	le32_add_cpu(&cl->cl_recs[chain].c_free, -res->sr_bits); -	ocfs2_journal_dirty(handle, ac->ac_bh); -  	status = ocfs2_block_group_set_bits(handle,  					    alloc_inode,  					    bg, @@ -1826,6 +1856,7 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,  	mlog(0, "Allocated %u bits from suballocator %llu\n", res->sr_bits,  	     (unsigned long long)le64_to_cpu(fe->i_blkno)); +out_loc_only:  	*bits_left = le16_to_cpu(bg->bg_free_bits_count);  bail:  	brelse(group_bh); @@ -1845,6 +1876,7 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,  	int status;  	u16 victim, i;  	u16 bits_left = 0; +	u64 hint = ac->ac_last_group;  	struct ocfs2_chain_list *cl;  	struct ocfs2_dinode *fe; @@ -1872,7 +1904,7 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,  		goto bail;  	} -	res->sr_bg_blkno = ac->ac_last_group; +	res->sr_bg_blkno = hint;  	if (res->sr_bg_blkno) {  		/* Attempt to short-circuit the usual search mechanism  		 * by jumping straight to the most recently used @@ -1896,8 +1928,10 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,  	status = ocfs2_search_chain(ac, handle, bits_wanted, min_bits,  				    res, &bits_left); -	if (!status) +	if (!status) { +		hint = ocfs2_group_from_res(res);  		goto set_hint; +	}  	if (status < 0 && status != -ENOSPC) {  		mlog_errno(status);  		goto bail; @@ -1920,8 +1954,10 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,  		ac->ac_chain = i;  		status = ocfs2_search_chain(ac, handle, bits_wanted, min_bits,  					    res, &bits_left); -		if (!status) +		if (!status) { +			hint = ocfs2_group_from_res(res);  			break; +		}  		if (status < 0 && status != -ENOSPC) {  			mlog_errno(status);  			goto bail; @@ -1936,7 +1972,7 @@ set_hint:  		if (bits_left < min_bits)  			ac->ac_last_group = 0;  		else -			ac->ac_last_group = res->sr_bg_blkno; +			ac->ac_last_group = hint;  	}  bail: @@ -2016,6 +2052,136 @@ static inline void ocfs2_save_inode_ac_group(struct inode *dir,  	OCFS2_I(dir)->ip_last_used_slot = ac->ac_alloc_slot;  } +int ocfs2_find_new_inode_loc(struct inode *dir, +			     struct buffer_head *parent_fe_bh, +			     struct ocfs2_alloc_context *ac, +			     u64 *fe_blkno) +{ +	int ret; +	handle_t *handle = NULL; +	struct ocfs2_suballoc_result *res; + +	BUG_ON(!ac); +	BUG_ON(ac->ac_bits_given != 0); +	BUG_ON(ac->ac_bits_wanted != 1); +	BUG_ON(ac->ac_which != OCFS2_AC_USE_INODE); + +	res = kzalloc(sizeof(*res), GFP_NOFS); +	if (res == NULL) { +		ret = -ENOMEM; +		mlog_errno(ret); +		goto out; +	} + +	ocfs2_init_inode_ac_group(dir, parent_fe_bh, ac); + +	/* +	 * The handle started here is for chain relink. Alternatively, +	 * we could just disable relink for these calls. +	 */ +	handle = ocfs2_start_trans(OCFS2_SB(dir->i_sb), OCFS2_SUBALLOC_ALLOC); +	if (IS_ERR(handle)) { +		ret = PTR_ERR(handle); +		handle = NULL; +		mlog_errno(ret); +		goto out; +	} + +	/* +	 * This will instruct ocfs2_claim_suballoc_bits and +	 * ocfs2_search_one_group to search but save actual allocation +	 * for later. +	 */ +	ac->ac_find_loc_only = 1; + +	ret = ocfs2_claim_suballoc_bits(ac, handle, 1, 1, res); +	if (ret < 0) { +		mlog_errno(ret); +		goto out; +	} + +	ac->ac_find_loc_priv = res; +	*fe_blkno = res->sr_blkno; + +out: +	if (handle) +		ocfs2_commit_trans(OCFS2_SB(dir->i_sb), handle); + +	if (ret) +		kfree(res); + +	return ret; +} + +int ocfs2_claim_new_inode_at_loc(handle_t *handle, +				 struct inode *dir, +				 struct ocfs2_alloc_context *ac, +				 u64 *suballoc_loc, +				 u16 *suballoc_bit, +				 u64 di_blkno) +{ +	int ret; +	u16 chain; +	struct ocfs2_suballoc_result *res = ac->ac_find_loc_priv; +	struct buffer_head *bg_bh = NULL; +	struct ocfs2_group_desc *bg; +	struct ocfs2_dinode *di = (struct ocfs2_dinode *) ac->ac_bh->b_data; + +	/* +	 * Since di_blkno is being passed back in, we check for any +	 * inconsistencies which may have happened between +	 * calls. These are code bugs as di_blkno is not expected to +	 * change once returned from ocfs2_find_new_inode_loc() +	 */ +	BUG_ON(res->sr_blkno != di_blkno); + +	ret = ocfs2_read_group_descriptor(ac->ac_inode, di, +					  res->sr_bg_stable_blkno, &bg_bh); +	if (ret) { +		mlog_errno(ret); +		goto out; +	} + +	bg = (struct ocfs2_group_desc *) bg_bh->b_data; +	chain = le16_to_cpu(bg->bg_chain); + +	ret = ocfs2_alloc_dinode_update_counts(ac->ac_inode, handle, +					       ac->ac_bh, res->sr_bits, +					       chain); +	if (ret) { +		mlog_errno(ret); +		goto out; +	} + +	ret = ocfs2_block_group_set_bits(handle, +					 ac->ac_inode, +					 bg, +					 bg_bh, +					 res->sr_bit_offset, +					 res->sr_bits); +	if (ret < 0) { +		mlog_errno(ret); +		goto out; +	} + +	mlog(0, "Allocated %u bits from suballocator %llu\n", res->sr_bits, +	     (unsigned long long)di_blkno); + +	atomic_inc(&OCFS2_SB(ac->ac_inode->i_sb)->alloc_stats.bg_allocs); + +	BUG_ON(res->sr_bits != 1); + +	*suballoc_loc = res->sr_bg_blkno; +	*suballoc_bit = res->sr_bit_offset; +	ac->ac_bits_given++; +	ocfs2_save_inode_ac_group(dir, ac); + +out: +	brelse(bg_bh); + +	return ret; +} +  int ocfs2_claim_new_inode(handle_t *handle,  			  struct inode *dir,  			  struct buffer_head *parent_fe_bh, @@ -2567,7 +2733,8 @@ out:   * suballoc_bit.   */  static int ocfs2_get_suballoc_slot_bit(struct ocfs2_super *osb, u64 blkno, -				       u16 *suballoc_slot, u16 *suballoc_bit) +				       u16 *suballoc_slot, u64 *group_blkno, +				       u16 *suballoc_bit)  {  	int status;  	struct buffer_head *inode_bh = NULL; @@ -2604,6 +2771,8 @@ static int ocfs2_get_suballoc_slot_bit(struct ocfs2_super *osb, u64 blkno,  		*suballoc_slot = le16_to_cpu(inode_fe->i_suballoc_slot);  	if (suballoc_bit)  		*suballoc_bit = le16_to_cpu(inode_fe->i_suballoc_bit); +	if (group_blkno) +		*group_blkno = le64_to_cpu(inode_fe->i_suballoc_loc);  bail:  	brelse(inode_bh); @@ -2621,7 +2790,8 @@ bail:   */  static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb,  				   struct inode *suballoc, -				   struct buffer_head *alloc_bh, u64 blkno, +				   struct buffer_head *alloc_bh, +				   u64 group_blkno, u64 blkno,  				   u16 bit, int *res)  {  	struct ocfs2_dinode *alloc_di; @@ -2642,10 +2812,8 @@ static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb,  		goto bail;  	} -	if (alloc_di->i_suballoc_loc) -		bg_blkno = le64_to_cpu(alloc_di->i_suballoc_loc); -	else -		bg_blkno = ocfs2_which_suballoc_group(blkno, bit); +	bg_blkno = group_blkno ? group_blkno : +		   ocfs2_which_suballoc_group(blkno, bit);  	status = ocfs2_read_group_descriptor(suballoc, alloc_di, bg_blkno,  					     &group_bh);  	if (status < 0) { @@ -2680,6 +2848,7 @@ bail:  int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res)  {  	int status; +	u64 group_blkno = 0;  	u16 suballoc_bit = 0, suballoc_slot = 0;  	struct inode *inode_alloc_inode;  	struct buffer_head *alloc_bh = NULL; @@ -2687,7 +2856,7 @@ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res)  	mlog_entry("blkno: %llu", (unsigned long long)blkno);  	status = ocfs2_get_suballoc_slot_bit(osb, blkno, &suballoc_slot, -					     &suballoc_bit); +					     &group_blkno, &suballoc_bit);  	if (status < 0) {  		mlog(ML_ERROR, "get alloc slot and bit failed %d\n", status);  		goto bail; @@ -2715,7 +2884,7 @@ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res)  	}  	status = ocfs2_test_suballoc_bit(osb, inode_alloc_inode, alloc_bh, -					 blkno, suballoc_bit, res); +					 group_blkno, blkno, suballoc_bit, res);  	if (status < 0)  		mlog(ML_ERROR, "test suballoc bit failed %d\n", status); | 
