[Ocfs2-devel] [PATCH 01/15] ocfs2: move nonsparse hole-filling into ocfs2_write_begin()

Joel Becker Joel.Becker at oracle.com
Thu Sep 20 11:22:19 PDT 2007


On Tue, Aug 28, 2007 at 05:13:23PM -0700, Mark Fasheh wrote:
> By doing this, we can remove any higher level logic which has to have
> knowledge of btree functionality - any callers of ocfs2_write_begin() can
> now expect it to do anything necessary to prepare the inode for new data.

I like the way extend_file gains smarts - it knows what it's doing.  I
also like the clean comments about how we prevent cluster and local
concurrent access.

Signed-off-by: Joel Becker <joel.becker at oracle.com>

> 
> Signed-off-by: Mark Fasheh <mark.fasheh at oracle.com>
> ---
>  fs/ocfs2/aops.c |   40 +++++++++-
>  fs/ocfs2/file.c |  217 ++++++++++++++++++++-----------------------------------
>  fs/ocfs2/file.h |    2 +
>  3 files changed, 115 insertions(+), 144 deletions(-)
> 
> diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
> index 460d440..97394c5 100644
> --- a/fs/ocfs2/aops.c
> +++ b/fs/ocfs2/aops.c
> @@ -301,12 +301,8 @@ int ocfs2_prepare_write_nolock(struct inode *inode, struct page *page,
>  {
>  	int ret;
>  
> -	down_read(&OCFS2_I(inode)->ip_alloc_sem);
> -
>  	ret = block_prepare_write(page, from, to, ocfs2_get_block);
>  
> -	up_read(&OCFS2_I(inode)->ip_alloc_sem);
> -
>  	return ret;
>  }
>  
> @@ -1353,6 +1349,36 @@ out:
>  	return ret;
>  }
>  
> +/*
> + * This function only does anything for file systems which can't
> + * handle sparse files.
> + *
> + * What we want to do here is fill in any hole between the current end
> + * of allocation and the end of our write. That way the rest of the
> + * write path can treat it as an non-allocating write, which has no
> + * special case code for sparse/nonsparse files.
> + */
> +static int ocfs2_expand_nonsparse_inode(struct inode *inode, loff_t pos,
> +					unsigned len,
> +					struct ocfs2_write_ctxt *wc)
> +{
> +	int ret;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +	loff_t newsize = pos + len;
> +
> +	if (ocfs2_sparse_alloc(osb))
> +		return 0;
> +
> +	if (newsize <= i_size_read(inode))
> +		return 0;
> +
> +	ret = ocfs2_extend_no_holes(inode, newsize, newsize - len);
> +	if (ret)
> +		mlog_errno(ret);
> +
> +	return ret;
> +}
> +
>  int ocfs2_write_begin_nolock(struct address_space *mapping,
>  			     loff_t pos, unsigned len, unsigned flags,
>  			     struct page **pagep, void **fsdata,
> @@ -1374,6 +1400,12 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
>  		return ret;
>  	}
>  
> +	ret = ocfs2_expand_nonsparse_inode(inode, pos, len, wc);
> +	if (ret) {
> +		mlog_errno(ret);
> +		goto out;
> +	}
> +
>  	ret = ocfs2_populate_write_desc(inode, wc, &clusters_to_alloc,
>  					&extents_to_split);
>  	if (ret) {
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index 4ffa715..429ebcb 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -780,25 +780,6 @@ leave:
>  	return status;
>  }
>  
> -static int ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
> -				   u32 clusters_to_add, int mark_unwritten)
> -{
> -	int ret;
> -
> -	/*
> -	 * The alloc sem blocks peope in read/write from reading our
> -	 * allocation until we're done changing it. We depend on
> -	 * i_mutex to block other extend/truncate calls while we're
> -	 * here.
> -	 */
> -	down_write(&OCFS2_I(inode)->ip_alloc_sem);
> -	ret = __ocfs2_extend_allocation(inode, logical_start, clusters_to_add,
> -					mark_unwritten);
> -	up_write(&OCFS2_I(inode)->ip_alloc_sem);
> -
> -	return ret;
> -}
> -
>  /* Some parts of this taken from generic_cont_expand, which turned out
>   * to be too fragile to do exactly what we need without us having to
>   * worry about recursive locking in ->prepare_write() and
> @@ -890,25 +871,47 @@ out:
>  	return ret;
>  }
>  
> -/* 
> - * A tail_to_skip value > 0 indicates that we're being called from
> - * ocfs2_file_aio_write(). This has the following implications:
> - *
> - * - we don't want to update i_size
> - * - di_bh will be NULL, which is fine because it's only used in the
> - *   case where we want to update i_size.
> - * - ocfs2_zero_extend() will then only be filling the hole created
> - *   between i_size and the start of the write.
> - */
> +int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, u64 zero_to)
> +{
> +	int ret;
> +	u32 clusters_to_add;
> +	struct ocfs2_inode_info *oi = OCFS2_I(inode);
> +
> +	clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size);
> +	if (clusters_to_add < oi->ip_clusters)
> +		clusters_to_add = 0;
> +	else
> +		clusters_to_add -= oi->ip_clusters;
> +
> +	if (clusters_to_add) {
> +		ret = __ocfs2_extend_allocation(inode, oi->ip_clusters,
> +						clusters_to_add, 0);
> +		if (ret) {
> +			mlog_errno(ret);
> +			goto out;
> +		}
> +	}
> +
> +	/*
> +	 * Call this even if we don't add any clusters to the tree. We
> +	 * still need to zero the area between the old i_size and the
> +	 * new i_size.
> +	 */
> +	ret = ocfs2_zero_extend(inode, zero_to);
> +	if (ret < 0)
> +		mlog_errno(ret);
> +
> +out:
> +	return ret;
> +}
> +
>  static int ocfs2_extend_file(struct inode *inode,
>  			     struct buffer_head *di_bh,
> -			     u64 new_i_size,
> -			     size_t tail_to_skip)
> +			     u64 new_i_size)
>  {
>  	int ret = 0;
> -	u32 clusters_to_add = 0;
>  
> -	BUG_ON(!tail_to_skip && !di_bh);
> +	BUG_ON(!di_bh);
>  
>  	/* setattr sometimes calls us like this. */
>  	if (new_i_size == 0)
> @@ -918,13 +921,8 @@ static int ocfs2_extend_file(struct inode *inode,
>    		goto out;
>  	BUG_ON(new_i_size < i_size_read(inode));
>  
> -	if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
> -		BUG_ON(tail_to_skip != 0);
> +	if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
>  		goto out_update_size;
> -	}
> -
> -	clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size) - 
> -		OCFS2_I(inode)->ip_clusters;
>  
>  	/* 
>  	 * protect the pages that ocfs2_zero_extend is going to be
> @@ -939,35 +937,25 @@ static int ocfs2_extend_file(struct inode *inode,
>  		goto out;
>  	}
>  
> -	if (clusters_to_add) {
> -		ret = ocfs2_extend_allocation(inode,
> -					      OCFS2_I(inode)->ip_clusters,
> -					      clusters_to_add, 0);
> -		if (ret < 0) {
> -			mlog_errno(ret);
> -			goto out_unlock;
> -		}
> -	}
> -
>  	/*
> -	 * Call this even if we don't add any clusters to the tree. We
> -	 * still need to zero the area between the old i_size and the
> -	 * new i_size.
> +	 * The alloc sem blocks people in read/write from reading our
> +	 * allocation until we're done changing it. We depend on
> +	 * i_mutex to block other extend/truncate calls while we're
> +	 * here.
>  	 */
> -	ret = ocfs2_zero_extend(inode, (u64)new_i_size - tail_to_skip);
> +	down_write(&OCFS2_I(inode)->ip_alloc_sem);
> +	ret = ocfs2_extend_no_holes(inode, new_i_size, new_i_size);
> +	up_write(&OCFS2_I(inode)->ip_alloc_sem);
> +
>  	if (ret < 0) {
>  		mlog_errno(ret);
>  		goto out_unlock;
>  	}
>  
>  out_update_size:
> -	if (!tail_to_skip) {
> -		/* We're being called from ocfs2_setattr() which wants
> -		 * us to update i_size */
> -		ret = ocfs2_simple_size_update(inode, di_bh, new_i_size);
> -		if (ret < 0)
> -			mlog_errno(ret);
> -	}
> +	ret = ocfs2_simple_size_update(inode, di_bh, new_i_size);
> +	if (ret < 0)
> +		mlog_errno(ret);
>  
>  out_unlock:
>  	if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
> @@ -1036,7 +1024,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
>  		if (i_size_read(inode) > attr->ia_size)
>  			status = ocfs2_truncate_file(inode, bh, attr->ia_size);
>  		else
> -			status = ocfs2_extend_file(inode, bh, attr->ia_size, 0);
> +			status = ocfs2_extend_file(inode, bh, attr->ia_size);
>  		if (status < 0) {
>  			if (status != -ENOSPC)
>  				mlog_errno(status);
> @@ -1714,15 +1702,13 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
>  					 int appending,
>  					 int *direct_io)
>  {
> -	int ret = 0, meta_level = appending;
> +	int ret = 0, meta_level = 0;
>  	struct inode *inode = dentry->d_inode;
> -	u32 clusters;
> -	loff_t newsize, saved_pos;
> +	loff_t saved_pos, end;
>  
>  	/* 
> -	 * We sample i_size under a read level meta lock to see if our write
> -	 * is extending the file, if it is we back off and get a write level
> -	 * meta lock.
> +	 * We start with a read level meta lock and only jump to an ex
> +	 * if we need to make modifications here.
>  	 */
>  	for(;;) {
>  		ret = ocfs2_meta_lock(inode, NULL, meta_level);
> @@ -1764,87 +1750,38 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
>  			saved_pos = *ppos;
>  		}
>  
> -		if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
> -			loff_t end = saved_pos + count;
> -
> -			/*
> -			 * Skip the O_DIRECT checks if we don't need
> -			 * them.
> -			 */
> -			if (!direct_io || !(*direct_io))
> -				break;
> -
> -			/*
> -			 * Allowing concurrent direct writes means
> -			 * i_size changes wouldn't be synchronized, so
> -			 * one node could wind up truncating another
> -			 * nodes writes.
> -			 */
> -			if (end > i_size_read(inode)) {
> -				*direct_io = 0;
> -				break;
> -			}
> +		end = saved_pos + count;
>  
> -			/*
> -			 * We don't fill holes during direct io, so
> -			 * check for them here. If any are found, the
> -			 * caller will have to retake some cluster
> -			 * locks and initiate the io as buffered.
> -			 */
> -			ret = ocfs2_check_range_for_holes(inode, saved_pos,
> -							  count);
> -			if (ret == 1) {
> -				*direct_io = 0;
> -				ret = 0;
> -			} else if (ret < 0)
> -				mlog_errno(ret);
> +		/*
> +		 * Skip the O_DIRECT checks if we don't need
> +		 * them.
> +		 */
> +		if (!direct_io || !(*direct_io))
>  			break;
> -		}
>  
>  		/*
> -		 * The rest of this loop is concerned with legacy file
> -		 * systems which don't support sparse files.
> +		 * Allowing concurrent direct writes means
> +		 * i_size changes wouldn't be synchronized, so
> +		 * one node could wind up truncating another
> +		 * nodes writes.
>  		 */
> -
> -		newsize = count + saved_pos;
> -
> -		mlog(0, "pos=%lld newsize=%lld cursize=%lld\n",
> -		     (long long) saved_pos, (long long) newsize,
> -		     (long long) i_size_read(inode));
> -
> -		/* No need for a higher level metadata lock if we're
> -		 * never going past i_size. */
> -		if (newsize <= i_size_read(inode))
> +		if (end > i_size_read(inode)) {
> +			*direct_io = 0;
>  			break;
> -
> -		if (meta_level == 0) {
> -			ocfs2_meta_unlock(inode, meta_level);
> -			meta_level = 1;
> -			continue;
>  		}
>  
> -		spin_lock(&OCFS2_I(inode)->ip_lock);
> -		clusters = ocfs2_clusters_for_bytes(inode->i_sb, newsize) -
> -			OCFS2_I(inode)->ip_clusters;
> -		spin_unlock(&OCFS2_I(inode)->ip_lock);
> -
> -		mlog(0, "Writing at EOF, may need more allocation: "
> -		     "i_size = %lld, newsize = %lld, need %u clusters\n",
> -		     (long long) i_size_read(inode), (long long) newsize,
> -		     clusters);
> -
> -		/* We only want to continue the rest of this loop if
> -		 * our extend will actually require more
> -		 * allocation. */
> -		if (!clusters)
> -			break;
> -
> -		ret = ocfs2_extend_file(inode, NULL, newsize, count);
> -		if (ret < 0) {
> -			if (ret != -ENOSPC)
> -				mlog_errno(ret);
> -			goto out_unlock;
> -		}
> +		/*
> +		 * We don't fill holes during direct io, so
> +		 * check for them here. If any are found, the
> +		 * caller will have to retake some cluster
> +		 * locks and initiate the io as buffered.
> +		 */
> +		ret = ocfs2_check_range_for_holes(inode, saved_pos, count);
> +		if (ret == 1) {
> +			*direct_io = 0;
> +			ret = 0;
> +		} else if (ret < 0)
> +			mlog_errno(ret);
>  		break;
>  	}
>  
> diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
> index 36fe27f..066f14a 100644
> --- a/fs/ocfs2/file.h
> +++ b/fs/ocfs2/file.h
> @@ -47,6 +47,8 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
>  			       struct ocfs2_alloc_context *data_ac,
>  			       struct ocfs2_alloc_context *meta_ac,
>  			       enum ocfs2_alloc_restarted *reason_ret);
> +int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
> +			  u64 zero_to);
>  int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
>  			  u32 clusters_to_add, u32 extents_to_split,
>  			  struct ocfs2_alloc_context **data_ac,
> -- 
> 1.5.0.6
> 
> 
> _______________________________________________
> Ocfs2-devel mailing list
> Ocfs2-devel at oss.oracle.com
> http://oss.oracle.com/mailman/listinfo/ocfs2-devel

-- 

"People with narrow minds usually have broad tongues."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127



More information about the Ocfs2-devel mailing list