diff options
Diffstat (limited to 'fs/ocfs2/file.c')
| -rw-r--r-- | fs/ocfs2/file.c | 134 | 
1 files changed, 90 insertions, 44 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 53939bf9d7d2..9876db52913a 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2098,53 +2098,89 @@ static int ocfs2_is_io_unaligned(struct inode *inode, size_t count, loff_t pos)  	return 0;  } -static int ocfs2_prepare_inode_for_refcount(struct inode *inode, -					    struct file *file, -					    loff_t pos, size_t count, -					    int *meta_level) +static int ocfs2_inode_lock_for_extent_tree(struct inode *inode, +					    struct buffer_head **di_bh, +					    int meta_level, +					    int overwrite_io, +					    int write_sem, +					    int wait)  { -	int ret; -	struct buffer_head *di_bh = NULL; -	u32 cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits; -	u32 clusters = -		ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos; +	int ret = 0; -	ret = ocfs2_inode_lock(inode, &di_bh, 1); -	if (ret) { -		mlog_errno(ret); +	if (wait) +		ret = ocfs2_inode_lock(inode, NULL, meta_level); +	else +		ret = ocfs2_try_inode_lock(inode, +			overwrite_io ? NULL : di_bh, meta_level); +	if (ret < 0)  		goto out; + +	if (wait) { +		if (write_sem) +			down_write(&OCFS2_I(inode)->ip_alloc_sem); +		else +			down_read(&OCFS2_I(inode)->ip_alloc_sem); +	} else { +		if (write_sem) +			ret = down_write_trylock(&OCFS2_I(inode)->ip_alloc_sem); +		else +			ret = down_read_trylock(&OCFS2_I(inode)->ip_alloc_sem); + +		if (!ret) { +			ret = -EAGAIN; +			goto out_unlock; +		}  	} -	*meta_level = 1; +	return ret; -	ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX); -	if (ret) -		mlog_errno(ret); +out_unlock: +	brelse(*di_bh); +	ocfs2_inode_unlock(inode, meta_level);  out: -	brelse(di_bh);  	return ret;  } +static void ocfs2_inode_unlock_for_extent_tree(struct inode *inode, +					       struct buffer_head **di_bh, +					       int meta_level, +					       int write_sem) +{ +	if (write_sem) +		up_write(&OCFS2_I(inode)->ip_alloc_sem); +	else +		up_read(&OCFS2_I(inode)->ip_alloc_sem); + +	brelse(*di_bh); +	*di_bh = NULL; + +	if (meta_level >= 0) +		ocfs2_inode_unlock(inode, meta_level); +} +  static int ocfs2_prepare_inode_for_write(struct file *file,  					 loff_t pos, size_t count, int wait)  {  	int ret = 0, meta_level = 0, overwrite_io = 0; +	int write_sem = 0;  	struct dentry *dentry = file->f_path.dentry;  	struct inode *inode = d_inode(dentry);  	struct buffer_head *di_bh = NULL; +	u32 cpos; +	u32 clusters;  	/*  	 * We start with a read level meta lock and only jump to an ex  	 * if we need to make modifications here.  	 */  	for(;;) { -		if (wait) -			ret = ocfs2_inode_lock(inode, NULL, meta_level); -		else -			ret = ocfs2_try_inode_lock(inode, -				overwrite_io ? NULL : &di_bh, meta_level); +		ret = ocfs2_inode_lock_for_extent_tree(inode, +						       &di_bh, +						       meta_level, +						       overwrite_io, +						       write_sem, +						       wait);  		if (ret < 0) { -			meta_level = -1;  			if (ret != -EAGAIN)  				mlog_errno(ret);  			goto out; @@ -2156,15 +2192,8 @@ static int ocfs2_prepare_inode_for_write(struct file *file,  		 */  		if (!wait && !overwrite_io) {  			overwrite_io = 1; -			if (!down_read_trylock(&OCFS2_I(inode)->ip_alloc_sem)) { -				ret = -EAGAIN; -				goto out_unlock; -			}  			ret = ocfs2_overwrite_io(inode, di_bh, pos, count); -			brelse(di_bh); -			di_bh = NULL; -			up_read(&OCFS2_I(inode)->ip_alloc_sem);  			if (ret < 0) {  				if (ret != -EAGAIN)  					mlog_errno(ret); @@ -2183,7 +2212,10 @@ static int ocfs2_prepare_inode_for_write(struct file *file,  		 * set inode->i_size at the end of a write. */  		if (should_remove_suid(dentry)) {  			if (meta_level == 0) { -				ocfs2_inode_unlock(inode, meta_level); +				ocfs2_inode_unlock_for_extent_tree(inode, +								   &di_bh, +								   meta_level, +								   write_sem);  				meta_level = 1;  				continue;  			} @@ -2197,18 +2229,32 @@ static int ocfs2_prepare_inode_for_write(struct file *file,  		ret = ocfs2_check_range_for_refcount(inode, pos, count);  		if (ret == 1) { -			ocfs2_inode_unlock(inode, meta_level); -			meta_level = -1; - -			ret = ocfs2_prepare_inode_for_refcount(inode, -							       file, -							       pos, -							       count, -							       &meta_level); +			ocfs2_inode_unlock_for_extent_tree(inode, +							   &di_bh, +							   meta_level, +							   write_sem); +			ret = ocfs2_inode_lock_for_extent_tree(inode, +							       &di_bh, +							       meta_level, +							       overwrite_io, +							       1, +							       wait); +			write_sem = 1; +			if (ret < 0) { +				if (ret != -EAGAIN) +					mlog_errno(ret); +				goto out; +			} + +			cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits; +			clusters = +				ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos; +			ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);  		}  		if (ret < 0) { -			mlog_errno(ret); +			if (ret != -EAGAIN) +				mlog_errno(ret);  			goto out_unlock;  		} @@ -2219,10 +2265,10 @@ out_unlock:  	trace_ocfs2_prepare_inode_for_write(OCFS2_I(inode)->ip_blkno,  					    pos, count, wait); -	brelse(di_bh); - -	if (meta_level >= 0) -		ocfs2_inode_unlock(inode, meta_level); +	ocfs2_inode_unlock_for_extent_tree(inode, +					   &di_bh, +					   meta_level, +					   write_sem);  out:  	return ret;  |