Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65ed39d6 authored by Mark Fasheh's avatar Mark Fasheh
Browse files

ocfs2: move nonsparse hole-filling into ocfs2_write_begin()



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.

Signed-off-by: default avatarMark Fasheh <mark.fasheh@oracle.com>
Reviewed-by: default avatarJoel Becker <joel.becker@oracle.com>
parent 92e91ce2
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -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;
}

@@ -1360,6 +1356,36 @@ static int ocfs2_populate_write_desc(struct inode *inode,
	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,
@@ -1381,6 +1407,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) {
+77 −140
Original line number Diff line number Diff line
@@ -779,25 +779,6 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
	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
@@ -889,25 +870,47 @@ static int ocfs2_zero_extend(struct inode *inode,
	return ret;
}

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;
		}
	}

	/*
 * 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.
	 * 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)
@@ -917,13 +920,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
@@ -938,35 +936,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);
	}

out_unlock:
	if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
@@ -1035,7 +1023,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);
@@ -1713,15 +1701,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);
@@ -1763,8 +1749,7 @@ 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;
		end = saved_pos + count;

		/*
		 * Skip the O_DIRECT checks if we don't need
@@ -1790,8 +1775,7 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
		 * caller will have to retake some cluster
		 * locks and initiate the io as buffered.
		 */
			ret = ocfs2_check_range_for_holes(inode, saved_pos,
							  count);
		ret = ocfs2_check_range_for_holes(inode, saved_pos, count);
		if (ret == 1) {
			*direct_io = 0;
			ret = 0;
@@ -1800,53 +1784,6 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
		break;
	}

		/*
		 * The rest of this loop is concerned with legacy file
		 * systems which don't support sparse files.
		 */

		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))
			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;
		}
		break;
	}

	if (appending)
		*ppos = saved_pos;

+2 −0
Original line number Diff line number Diff line
@@ -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,