Re: [PATCH v11 00/25] xfs: Delay Ready Attributes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Jul 20, 2020 at 05:15:41PM -0700, Allison Collins wrote:
> Hi all,
> 
> This set is a subset of a larger series for delayed attributes. Which is a
> subset of an even larger series, parent pointers. Delayed attributes allow
> attribute operations (set and remove) to be logged and committed in the same
> way that other delayed operations do. This allows more complex operations (like
> parent pointers) to be broken up into multiple smaller transactions. To do
> this, the existing attr operations must be modified to operate as either a
> delayed operation or a inline operation since older filesystems will not be
> able to use the new log entries.  This means that they cannot roll, commit, or
> finish transactions.  Instead, they return -EAGAIN to allow the calling
> function to handle the transaction. In this series, we focus on only the clean
> up and refactoring needed to accomplish this. We will introduce delayed attrs
> and parent pointers in a later set.
> 
> At the moment, I would like people to focus their review efforts on just this
> "delay ready" subseries, as I think that is a more conservative use of peoples
> review time.  I also think the set is a bit much to manage all at once, and we
> need to get the infrastructure ironed out before we focus too much anything
> that depends on it. But I do have the extended series for folks that want to
> see the bigger picture of where this is going.
> 
> To help organize the set, I've arranged the patches to make sort of mini sets.
> I thought it would help reviewers break down the reviewing some. For reviewing
> purposes, the set could be broken up into 4 different phases:
> 
> Error code filtering (patches 1-2):
> These two patches are all about finding and catching error codes that need to
> be sent back up to user space before starting delayed operations.  Errors that
> happen during a delayed operation are treated like internal errors that cause a
> shutdown.  But we wouldnt want that for example: when the user tries to rename
> a non existent attr.  So the idea is that we need to find all such conditions,
> and take care of them before starting a delayed operation.
>    xfs: Add xfs_has_attr and subroutines
>    xfs: Check for -ENOATTR or -EEXIST
> 
> Move transactions upwards (patches 3-12): 
> The goal of this subset is to try and move all the transaction specific code up
> the call stack much as possible.  The idea being that once we get them to the
> top, we can introduce the statemachine to handle the -EAGAIN logic where ever
> the transactions used to be.
>   xfs: Factor out new helper functions xfs_attr_rmtval_set
>   xfs: Pull up trans handling in xfs_attr3_leaf_flipflags
>   xfs: Split apart xfs_attr_leaf_addname
>   xfs: Refactor xfs_attr_try_sf_addname
>   xfs: Pull up trans roll from xfs_attr3_leaf_setflag
>   xfs: Factor out xfs_attr_rmtval_invalidate
>   xfs: Pull up trans roll in xfs_attr3_leaf_clearflag
>   xfs: Refactor xfs_attr_rmtval_remove
>   xfs: Pull up xfs_attr_rmtval_invalidate
>   xfs: Add helper function xfs_attr_node_shrink
> 
> Modularizing and cleanups (patches 13-22):
> Now that we have pulled the transactions up to where we need them, it's time to
> start breaking down the top level functions into new subfunctions. The goal
> being to work towards a top level function that deals mostly with the
> statemachine, and helpers for those states
>   xfs: Remove unneeded xfs_trans_roll_inode calls
>   xfs: Remove xfs_trans_roll in xfs_attr_node_removename
>   xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
>   xfs: Add helper function xfs_attr_leaf_mark_incomplete
>   xfs: Add remote block helper functions
>   xfs: Add helper function xfs_attr_node_removename_setup
>   xfs: Add helper function xfs_attr_node_removename_rmt
>   xfs: Simplify xfs_attr_leaf_addname
>   xfs: Simplify xfs_attr_node_addname
>   xfs: Lift -ENOSPC handler from xfs_attr_leaf_addname

I'm happy to see everything up to here merged.

> Introduce statemachine (patches 23-25):
> Now that we have re-arranged the code such that we can remove the transaction
> handling, we proceed to do so.  The behavior of the attr set/remove routines
> are now also compatible as a .finish_item callback
>   xfs: Add delay ready attr remove routines
>   xfs: Add delay ready attr set routines
>   xfs: Rename __xfs_attr_rmtval_remove

However, I think these still need work. The state machine mechanism
needs more factoring so that there is a function per state/action
that moves the state forwards one step, rather than the current
setup where a single function might handle 3-5 different states by
jumping to different parts of the code.

I started thinking that this was largely just code re-arrangement,
but as I started to to do a bit of cleanup onit as an example, I
think there's some bugs in the code that might be leading to leaking
buffers and other such stuff. So I think this code really needs a
good cleanup and going over before it will be ready to merge.

I've attached an untested, uncompiled patch below to demonstrate
how I think we should start flattening this out. It takes
xfs_attr_set_iter() and flattens it into a switch statement which
calls fine grained functions. There are a couple of new states to
efficiently jump into the state machine based on initial attr fork
format, and xfs_attr_set_iter() goes away entirely.

Note that I haven't flattened the second level of function calls out
into a function per state, which is where it's like to see this end
up. i.e. we don't need to call through xfs_attr_leaf_addname() to
just get to another switch statement to jump to the code that flips
the flags.

The factoring and flattening process is the same, though: isolate
the code that needs to run to a single function, call it from the
state machine switch. That function then selects the next state to
run, whether a transaction roll is necessary, etc, and the main loop
then takes care of it from there.

Allison, if you want I can put together another couple of patches
like the one below that flatten it right out into fine grained
states and functions. I think having all the state machien
transitions in one spot makes the code much easier to understand
and, later, to simplify and optimise.

Cheers,

Dave.
-- 
Dave Chinner
david@xxxxxxxxxxxxx

---
 fs/xfs/libxfs/xfs_attr.c | 246 ++++++++++++++++++++++++++---------------------
 fs/xfs/libxfs/xfs_attr.h |   2 +
 2 files changed, 139 insertions(+), 109 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 88172459c8f4..8ebe09b7c26a 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -59,8 +59,6 @@ STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
 STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
 STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
 STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args, struct xfs_buf *bp);
-STATIC int xfs_attr_set_iter(struct xfs_delattr_context *dac,
-			     struct xfs_buf **leaf_bp);
 
 int
 xfs_inode_hasattr(
@@ -252,13 +250,7 @@ xfs_attr_set_shortform(
 	if (error)
 		return error;
 
-	/*
-	 * Prevent the leaf buffer from being unlocked so that a concurrent AIL
-	 * push cannot grab the half-baked leaf buffer and run into problems
-	 * with the write verifier.
-	 */
-	xfs_trans_bhold(args->trans, *leaf_bp);
-	return -EAGAIN;
+	return -ENOSPC;
 }
 
 /*
@@ -288,47 +280,35 @@ xfs_attr_trans_roll(
 	return xfs_trans_roll_inode(&args->trans, args->dp);
 }
 
-/*
- * Set the attribute specified in @args.
- */
-int
-xfs_attr_set_args(
-	struct xfs_da_args	*args)
+static int
+xfs_attr_das_set_sf(
+	struct xfs_delattr_context	*dac,
+	struct xfs_buf			**leaf_bp)
 {
-	struct xfs_buf			*leaf_bp = NULL;
-	int				error = 0;
-	struct xfs_delattr_context	dac = {
-		.da_args	= args,
-	};
-
-	do {
-		error = xfs_attr_set_iter(&dac, &leaf_bp);
-		if (error != -EAGAIN)
-			break;
 
-		error = xfs_attr_trans_roll(&dac);
-		if (error)
-			return error;
-
-		if (leaf_bp) {
-			xfs_trans_bjoin(args->trans, leaf_bp);
-			xfs_trans_bhold(args->trans, leaf_bp);
-		}
+	/*
+	 * If the attr was successfully set in shortform, no need to
+	 * continue.  Otherwise, is it converted from shortform to leaf
+	 * and -EAGAIN is returned.
+	 */
+	error = xfs_attr_set_shortform(args, leaf_bp);
+	if (error != ENOSPC)
+		return error;
 
-	} while (true);
+	dac->flags |= XFS_DAC_DEFER_FINISH;
+	dac->state = XFS_DAS_FIND_LBLK;
 
-	return error;
+	/*
+	 * Prevent the leaf buffer from being unlocked while we roll the
+	 * transaction so that a concurrent AIL push cannot grab the half-baked
+	 * leaf buffer and run into problems with the write verifier.
+	 */
+	xfs_trans_bhold(args->trans, *leaf_bp);
+	return -EAGAIN;
 }
 
-/*
- * Set the attribute specified in @args.
- * This routine is meant to function as a delayed operation, and may return
- * -EAGAIN when the transaction needs to be rolled.  Calling functions will need
- * to handle this, and recall the function until a successful error code is
- * returned.
- */
-STATIC int
-xfs_attr_set_iter(
+static int
+xfs_attr_das_find_leaf(
 	struct xfs_delattr_context	*dac,
 	struct xfs_buf			**leaf_bp)
 {
@@ -336,89 +316,137 @@ xfs_attr_set_iter(
 	struct xfs_inode		*dp = args->dp;
 	int				error = 0;
 
-	/* State machine switch */
-	switch (dac->dela_state) {
-	case XFS_DAS_FLIP_LFLAG:
-	case XFS_DAS_FOUND_LBLK:
-		goto das_leaf;
-	case XFS_DAS_FOUND_NBLK:
-	case XFS_DAS_FLIP_NFLAG:
-	case XFS_DAS_ALLOC_NODE:
-		goto das_node;
-	default:
-		break;
-	}
-
 	/*
-	 * If the attribute list is already in leaf format, jump straight to
-	 * leaf handling.  Otherwise, try to add the attribute to the shortform
-	 * list; if there's no room then convert the list to leaf format and try
-	 * again. No need to set state as we will be in leaf form when we come
-	 * back
+	 * After a shortform to leaf conversion, we don't need to hold the leaf
+	 * block we allocated anymore. Release the hold if we are passed a leaf
+	 * block and clear the pointer.
 	 */
-	if (xfs_attr_is_shortform(dp)) {
+	if (*leaf_bp) {
+		xfs_trans_bhold_release(args->trans, *leaf_bp);
+		*leaf_bp = NULL;
+	}
 
+	error = xfs_attr_leaf_try_add(args, leaf_bp);
+	switch (error) {
+	case -ENOSPC:
 		/*
-		 * If the attr was successfully set in shortform, no need to
-		 * continue.  Otherwise, is it converted from shortform to leaf
-		 * and -EAGAIN is returned.
+		 * Promote the attribute list to the Btree format.
 		 */
-		error = xfs_attr_set_shortform(args, leaf_bp);
-		if (error == -EAGAIN)
-			dac->flags |= XFS_DAC_DEFER_FINISH;
+		error = xfs_attr3_leaf_to_node(args);
+		if (error)
+			return error;
 
+		/*
+		 * Finish any deferred work items and roll the
+		 * transaction once more.  The goal here is to call
+		 * node_addname with the inode  and transaction in the
+		 * same state (inode locked and joined, transaction
+		 * clean) no matter how we got to this step.
+		 */
+		dac->flags |= XFS_DAC_DEFER_FINISH;
+		return -EAGAIN;
+	case 0:
+		dac->dela_state = XFS_DAS_FOUND_LBLK;
+		return -EAGAIN;
+	default:
 		return error;
 	}
+}
+
+static int
+xfs_attr_das_set_leaf(
+	struct xfs_delattr_context	*dac)
+{
+	int				error = 0;
+
+	error = xfs_attr_leaf_addname(dac);
+	if (error == -ENOSPC) {
+		/*
+		 * No need to set state.  We will be in node form when
+		 * we are recalled
+		 */
+		return -EAGAIN;
+	}
+	return error;
+}
+
+static int
+xfs_attr_das_set_node(
+	struct xfs_delattr_context	*dac)
+{
+	return  xfs_attr_node_addname(dac);
+}
+
+/*
+ * Set the attribute specified in @args.
+ */
+int
+xfs_attr_set_args(
+	struct xfs_da_args	*args)
+{
+	struct xfs_buf			*leaf_bp = NULL;
+	int				error = 0;
+	struct xfs_delattr_context	dac = {
+		.da_args	= args,
+	};
 
 	/*
-	 * After a shortform to leaf conversion, we need to hold the leaf and
-	 * cycle out the transaction.  When we get back, we need to release
-	 * the leaf.
+	 * Initial condition setup is based on detected form of
+	 * the attribute tree.
 	 */
-	if (*leaf_bp != NULL) {
-		xfs_trans_bhold_release(args->trans, *leaf_bp);
-		*leaf_bp = NULL;
+	if (xfs_attr_is_shortform(dp)) {
+		dac->dela_state = XFS_DAS_FIND_SF;
+	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+		dac->dela_state = XFS_DAS_FIND_LBLK;
+	} else {
+		dac->dela_state = XFS_DAS_FIND_NBLK;
 	}
 
-	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-		error = xfs_attr_leaf_try_add(args, *leaf_bp);
-		switch (error) {
-		case -ENOSPC:
-			/*
-			 * Promote the attribute list to the Btree format.
-			 */
-			error = xfs_attr3_leaf_to_node(args);
-			if (error)
-				return error;
+	do {
+		/*
+		 * XXX: Need two returns here:
+		 *	- state change occurred, no trans roll required
+		 *	- state change occurred, trans roll required.
+		 */
+		switch (dac->dela_state) {
+		case XFS_DAS_FIND_SF:
+			error = xfs_attr_das_set_sf(dac, &leaf_bp);
+			break;
+		case XFS_DAS_FIND_LBLK:
+			/* XXX: should we really be getting leaf_bp back here? */
+			error = xfs_attr_das_find_leaf(dac, &leaf_bp);
+			break;
 
-			/*
-			 * Finish any deferred work items and roll the
-			 * transaction once more.  The goal here is to call
-			 * node_addname with the inode  and transaction in the
-			 * same state (inode locked and joined, transaction
-			 * clean) no matter how we got to this step.
-			 */
-			dac->flags |= XFS_DAC_DEFER_FINISH;
-			return -EAGAIN;
-		case 0:
-			dac->dela_state = XFS_DAS_FOUND_LBLK;
-			return -EAGAIN;
+		case XFS_DAS_FLIP_LFLAG:
+		case XFS_DAS_FOUND_LBLK:
+			error = xfs_attr_das_set_leaf(dac);
+			break;
+
+		case XFS_DAS_FOUND_NBLK:
+		case XFS_DAS_FLIP_NFLAG:
+		case XFS_DAS_ALLOC_NODE:
+			error = xfs_attr_das_set_node(dac);
+			break;
 		default:
+			error = xfs_attr_das_set_sf(dac, &leaf_bp);
+			break;
+		}
+		if (error != -EAGAIN)
+			break;
+
+		error = xfs_attr_trans_roll(&dac);
+		if (error)
 			return error;
+
+		if (leaf_bp) {
+			xfs_trans_bjoin(args->trans, leaf_bp);
+			xfs_trans_bhold(args->trans, leaf_bp);
 		}
-das_leaf:
-		error = xfs_attr_leaf_addname(dac);
-		if (error == -ENOSPC)
-			/*
-			 * No need to set state.  We will be in node form when
-			 * we are recalled
-			 */
-			return -EAGAIN;
 
-		return error;
-	}
-das_node:
-	error = xfs_attr_node_addname(dac);
+	} while (true);
+
+	/* XXX: shouldn't this drop the hold on leaf_bp here? */
+
 	return error;
 }
 
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 4f6bba87fd42..9f80b937c22a 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -232,6 +232,8 @@ struct xfs_attr_list_context {
 enum xfs_delattr_state {
 				      /* Zero is uninitalized */
 	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
+	XFS_DAS_FIND_SF,
+	XFS_DAS_FIND_LBLK,
 	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
 	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
 	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux