All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 00/20] xfs: Delay Ready Attributes
@ 2020-04-03 22:12 Allison Collins
  2020-04-03 22:12 ` [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines Allison Collins
                   ` (19 more replies)
  0 siblings, 20 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

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-10): 
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: Add helper function __xfs_attr_rmtval_remove

Modularizing and cleanups (patches 11-18):
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: Add helper function xfs_attr_node_shrink
   xfs: Removed unneeded xfs_trans_roll_inode calls
   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

Introduce statemachine (patches 18-20):
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


Changes since v7:
Theres a lot of activity that goes around with each set, so to help people
recall the discussion I've outlined the changes for each patch, which are new,
and which are unchanged:

xfs: Add xfs_has_attr and subroutines
  Added extra free in the case of statep == NULL
  Minor error handling syntax nits
  Rebase adjustments
 
xfs: Check for -ENOATTR or -EEXIST
  Extra error handling in the case of (error != -ENOATTR && error != -EEXIST)
  Rebase adjustments

xfs: Factor out new helper functions xfs_attr_rmtval_set
  No change

xfs: Pull up trans handling in xfs_attr3_leaf_flipflags
  Commit message amended based on review commentary

xfs: Split apart xfs_attr_leaf_addname
  Commit message amended based on review commentary
  brealse on error moved into xfs_attr_leaf_addname helper
  line wrap adjust
  Investigated error handling logic suggestion:
     let go for reasons of later statemachine impacts

xfs: Refactor xfs_attr_try_sf_addname
   Commit message typo

xfs: Pull up out trans roll from xfs_attr3_leaf_setflag
   Commit message amended based on review nits

xfs: Factor out xfs_attr_rmtval_invalidate
   no change

xfs: Pull up trans roll in xfs_attr3_leaf_clearflag
  Commit message amended based on review commentary

xfs: Add helper function __xfs_attr_rmtval_remove
   renamed xfs_attr_rmtval_unmap to __xfs_attr_rmtval_remove
   remove stale comment

xfs: Add helper function xfs_attr_node_shrink
   rebase to lower position in the set

xfs: Removed unneeded xfs_trans_roll_inode calls
   new

xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
   new

xfs: Add helper function xfs_attr_leaf_mark_incomplete
   rebased to lower position in the set

xfs: Add remote block helper functions
  rename xfs_attr_store_rmt_blk to xfs_attr_save_rmt_blk
  rebase adjustments

xfs: Add helper function xfs_attr_node_removename_setup
   new

xfs: Add helper function xfs_attr_node_removename_rmt
   rename xfs_attr_rmtval_unmap to _xfs_attr_rmtval_remove

xfs: Add delay ready attr remove routines
   Simplify xfs_attr_remove_args loop now that trailing transaction rolls are gone
      (removed in patch 12)
   Subroutines adjusted to accept a struct xfs_delattr_context containing an xfs_da_args
      instead of an xfs_da_args containing a struct xfs_delattr_context
   Extra transaction conversion logic added for helpers added in the new patches
   gotos associated with state logic amended to have the das_* prefix
   XFS_DAC_DEFER_FINISH renamed to XFS_DAC_DEFER_FINISH
   enum xfs_delattr_state ammended to start at 1 instead of 0
   extra includes removed due to xfs_delattr_context and an xfs_da_args flip
   rebase adjustments

xfs: Add delay ready attr set routines
   Simplify xfs_attr_remove_args loop now that trailing transaction rolls are gone
       (removed in patch 12)
   Subroutines adjusted to accept a struct xfs_delattr_context containing an xfs_da_args
      instead of an xfs_da_args containing a struct xfs_delattr_context
   Extra transaction conversion logic added for helpers added in the new patches
   gotos associated with state logic amended to have the das_* prefix
   XFS_DAC_DEFER_FINISH renamed to XFS_DAC_DEFER_FINISH
   Extra XFS_DAS_RM_LBLK state added in the case of attr rename
   Remove now unused xfs_attr_rmtval_remove added to this patch instead of next patch
   rebase adjustments

xfs: Rename __xfs_attr_rmtval_remove
   New



This series can be viewed on github here:
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v8

As well as the extended delayed attribute and parent pointer series:
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v8_extended

And the test cases:
https://github.com/allisonhenderson/xfs_work/tree/pptr_xfstests

In order to run the test cases, you will need have the corresponding xfsprogs
changes as well.  Which can be found here:
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v8
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v8_extended

To run the xfs attributes tests run:
check -g attr

To run as delayed attributes run:
export MKFS_OPTIONS="-n delattr"
check -g attr

To run parent pointer tests:
check -g parent


I've also made the corresponding updates to the user space side as well, and ported anything
they need to seat correctly.

Questions, comment and feedback appreciated! 

Thanks all!
Allison



Allison Collins (20):
  xfs: Add xfs_has_attr and subroutines
  xfs: Check for -ENOATTR or -EEXIST
  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: Add helper function __xfs_attr_rmtval_remove
  xfs: Add helper function xfs_attr_node_shrink
  xfs: Removed unneeded xfs_trans_roll_inode calls
  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: Add delay ready attr remove routines
  xfs: Add delay ready attr set routines
  xfs: Rename __xfs_attr_rmtval_remove

 fs/xfs/libxfs/xfs_attr.c        | 1052 ++++++++++++++++++++++++++++-----------
 fs/xfs/libxfs/xfs_attr.h        |   55 ++
 fs/xfs/libxfs/xfs_attr_leaf.c   |  115 +++--
 fs/xfs/libxfs/xfs_attr_leaf.h   |    3 +
 fs/xfs/libxfs/xfs_attr_remote.c |  257 +++++++---
 fs/xfs/libxfs/xfs_attr_remote.h |    7 +-
 fs/xfs/xfs_attr_inactive.c      |    1 +
 fs/xfs/xfs_trace.h              |    1 -
 8 files changed, 1067 insertions(+), 424 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-06 14:31   ` Brian Foster
  2020-04-03 22:12 ` [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST Allison Collins
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch adds a new functions to check for the existence of an
attribute. Subroutines are also added to handle the cases of leaf
blocks, nodes or shortform. Common code that appears in existing attr
add and remove functions have been factored out to help reduce the
appearance of duplicated code.  We will need these routines later for
delayed attributes since delayed operations cannot return error codes.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c      | 181 ++++++++++++++++++++++++++++--------------
 fs/xfs/libxfs/xfs_attr.h      |   1 +
 fs/xfs/libxfs/xfs_attr_leaf.c |  97 +++++++++++++++-------
 fs/xfs/libxfs/xfs_attr_leaf.h |   3 +
 4 files changed, 191 insertions(+), 91 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index e4fe3dc..2a0d3d3 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -46,6 +46,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
 STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
 STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
 STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
 
 /*
  * Internal routines when attribute list is more than one block.
@@ -53,6 +54,8 @@ STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
 STATIC int xfs_attr_node_get(xfs_da_args_t *args);
 STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
 STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
+				 struct xfs_da_state **state);
 STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
 STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
 
@@ -261,6 +264,37 @@ xfs_attr_set_args(
 }
 
 /*
+ * Return EEXIST if attr is found, or ENOATTR if not
+ */
+int
+xfs_has_attr(
+	struct xfs_da_args      *args)
+{
+	struct xfs_inode	*dp = args->dp;
+	struct xfs_buf		*bp = NULL;
+	int			error;
+
+	if (!xfs_inode_hasattr(dp))
+		return -ENOATTR;
+
+	if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
+		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
+		return xfs_attr_sf_findname(args, NULL, NULL);
+	}
+
+	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+		error = xfs_attr_leaf_hasname(args, &bp);
+
+		if (bp)
+			xfs_trans_brelse(args->trans, bp);
+
+		return error;
+	}
+
+	return xfs_attr_node_hasname(args, NULL);
+}
+
+/*
  * Remove the attribute specified in @args.
  */
 int
@@ -469,26 +503,19 @@ STATIC int
 xfs_attr_leaf_addname(
 	struct xfs_da_args	*args)
 {
-	struct xfs_inode	*dp;
 	struct xfs_buf		*bp;
 	int			retval, error, forkoff;
+	struct xfs_inode	*dp = args->dp;
 
 	trace_xfs_attr_leaf_addname(args);
 
 	/*
-	 * Read the (only) block in the attribute list in.
-	 */
-	dp = args->dp;
-	args->blkno = 0;
-	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
-	if (error)
-		return error;
-
-	/*
 	 * Look up the given attribute in the leaf block.  Figure out if
 	 * the given flags produce an error or call for an atomic rename.
 	 */
-	retval = xfs_attr3_leaf_lookup_int(bp, args);
+	retval = xfs_attr_leaf_hasname(args, &bp);
+	if (retval != -ENOATTR && retval != -EEXIST)
+		return retval;
 	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
 		goto out_brelse;
 	if (retval == -EEXIST) {
@@ -640,6 +667,27 @@ xfs_attr_leaf_addname(
 }
 
 /*
+ * Return EEXIST if attr is found, or ENOATTR if not
+ */
+STATIC int
+xfs_attr_leaf_hasname(
+	struct xfs_da_args      *args,
+	struct xfs_buf		**bp)
+{
+	int                     error = 0;
+
+	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, bp);
+	if (error)
+		return error;
+
+	error = xfs_attr3_leaf_lookup_int(*bp, args);
+	if (error != -ENOATTR && error != -EEXIST)
+		xfs_trans_brelse(args->trans, *bp);
+
+	return error;
+}
+
+/*
  * Remove a name from the leaf attribute list structure
  *
  * This leaf block cannot have a "remote" value, we only call this routine
@@ -659,16 +707,14 @@ xfs_attr_leaf_removename(
 	 * Remove the attribute.
 	 */
 	dp = args->dp;
-	args->blkno = 0;
-	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
-	if (error)
-		return error;
 
-	error = xfs_attr3_leaf_lookup_int(bp, args);
+	error = xfs_attr_leaf_hasname(args, &bp);
+
 	if (error == -ENOATTR) {
 		xfs_trans_brelse(args->trans, bp);
 		return error;
-	}
+	} else if (error != -EEXIST)
+		return error;
 
 	xfs_attr3_leaf_remove(bp, args);
 
@@ -703,21 +749,57 @@ xfs_attr_leaf_get(xfs_da_args_t *args)
 
 	trace_xfs_attr_leaf_get(args);
 
-	args->blkno = 0;
-	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
-	if (error)
-		return error;
+	error = xfs_attr_leaf_hasname(args, &bp);
 
-	error = xfs_attr3_leaf_lookup_int(bp, args);
 	if (error != -EEXIST)  {
 		xfs_trans_brelse(args->trans, bp);
 		return error;
-	}
+	} else if (error != -EEXIST)
+		return error;
+
+
 	error = xfs_attr3_leaf_getvalue(bp, args);
 	xfs_trans_brelse(args->trans, bp);
 	return error;
 }
 
+/*
+ * Return EEXIST if attr is found, or ENOATTR if not
+ * statep: If not null is set to point at the found state.  Caller will
+ *         be responsible for freeing the state in this case.
+ */
+STATIC int
+xfs_attr_node_hasname(
+	struct xfs_da_args	*args,
+	struct xfs_da_state	**statep)
+{
+	struct xfs_da_state	*state;
+	int			retval, error;
+
+	state = xfs_da_state_alloc();
+	state->args = args;
+	state->mp = args->dp->i_mount;
+
+	if (statep != NULL)
+		*statep = NULL;
+
+	/*
+	 * Search to see if name exists, and get back a pointer to it.
+	 */
+	error = xfs_da3_node_lookup_int(state, &retval);
+	if (error == 0) {
+		if (statep != NULL)
+			*statep = state;
+		else
+			xfs_da_state_free(state);
+		return retval;
+	}
+
+	xfs_da_state_free(state);
+
+	return error;
+}
+
 /*========================================================================
  * External routines when attribute list size > geo->blksize
  *========================================================================*/
@@ -750,17 +832,14 @@ xfs_attr_node_addname(
 	dp = args->dp;
 	mp = dp->i_mount;
 restart:
-	state = xfs_da_state_alloc();
-	state->args = args;
-	state->mp = mp;
-
 	/*
 	 * Search to see if name already exists, and get back a pointer
 	 * to where it should go.
 	 */
-	error = xfs_da3_node_lookup_int(state, &retval);
-	if (error)
+	retval = xfs_attr_node_hasname(args, &state);
+	if (retval != -ENOATTR && retval != -EEXIST)
 		goto out;
+
 	blk = &state->path.blk[ state->path.active-1 ];
 	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
@@ -965,29 +1044,15 @@ xfs_attr_node_removename(
 {
 	struct xfs_da_state	*state;
 	struct xfs_da_state_blk	*blk;
-	struct xfs_inode	*dp;
 	struct xfs_buf		*bp;
 	int			retval, error, forkoff;
+	struct xfs_inode	*dp = args->dp;
 
 	trace_xfs_attr_node_removename(args);
 
-	/*
-	 * Tie a string around our finger to remind us where we are.
-	 */
-	dp = args->dp;
-	state = xfs_da_state_alloc();
-	state->args = args;
-	state->mp = dp->i_mount;
-
-	/*
-	 * Search to see if name exists, and get back a pointer to it.
-	 */
-	error = xfs_da3_node_lookup_int(state, &retval);
-	if (error || (retval != -EEXIST)) {
-		if (error == 0)
-			error = retval;
+	error = xfs_attr_node_hasname(args, &state);
+	if (error != -EEXIST)
 		goto out;
-	}
 
 	/*
 	 * If there is an out-of-line value, de-allocate the blocks.
@@ -1082,7 +1147,8 @@ xfs_attr_node_removename(
 	error = 0;
 
 out:
-	xfs_da_state_free(state);
+	if (state)
+		xfs_da_state_free(state);
 	return error;
 }
 
@@ -1202,31 +1268,23 @@ xfs_attr_node_get(xfs_da_args_t *args)
 {
 	xfs_da_state_t *state;
 	xfs_da_state_blk_t *blk;
-	int error, retval;
+	int error;
 	int i;
 
 	trace_xfs_attr_node_get(args);
 
-	state = xfs_da_state_alloc();
-	state->args = args;
-	state->mp = args->dp->i_mount;
-
 	/*
 	 * Search to see if name exists, and get back a pointer to it.
 	 */
-	error = xfs_da3_node_lookup_int(state, &retval);
-	if (error) {
-		retval = error;
-		goto out_release;
-	}
-	if (retval != -EEXIST)
+	error = xfs_attr_node_hasname(args, &state);
+	if (error != -EEXIST)
 		goto out_release;
 
 	/*
 	 * Get the value, local or "remote"
 	 */
 	blk = &state->path.blk[state->path.active - 1];
-	retval = xfs_attr3_leaf_getvalue(blk->bp, args);
+	error = xfs_attr3_leaf_getvalue(blk->bp, args);
 
 	/*
 	 * If not in a transaction, we have to release all the buffers.
@@ -1237,8 +1295,9 @@ xfs_attr_node_get(xfs_da_args_t *args)
 		state->path.blk[i].bp = NULL;
 	}
 
-	xfs_da_state_free(state);
-	return retval;
+	if (state)
+		xfs_da_state_free(state);
+	return error;
 }
 
 /* Returns true if the attribute entry name is valid. */
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 0d2d059..66575b8 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -89,6 +89,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
 int xfs_attr_get(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
 int xfs_attr_set_args(struct xfs_da_args *args);
+int xfs_has_attr(struct xfs_da_args *args);
 int xfs_attr_remove_args(struct xfs_da_args *args);
 bool xfs_attr_namecheck(const void *name, size_t length);
 
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 863444e..9f39e7a 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -664,18 +664,63 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
 }
 
 /*
+ * Return -EEXIST if attr is found, or -ENOATTR if not
+ * args:  args containing attribute name and namelen
+ * sfep:  If not null, pointer will be set to the last attr entry found on
+	  -EEXIST.  On -ENOATTR pointer is left at the last entry in the list
+ * basep: If not null, pointer is set to the byte offset of the entry in the
+ *	  list on -EEXIST.  On -ENOATTR, pointer is left at the byte offset of
+ *	  the last entry in the list
+ */
+int
+xfs_attr_sf_findname(
+	struct xfs_da_args	 *args,
+	struct xfs_attr_sf_entry **sfep,
+	unsigned int		 *basep)
+{
+	struct xfs_attr_shortform *sf;
+	struct xfs_attr_sf_entry *sfe;
+	unsigned int		base = sizeof(struct xfs_attr_sf_hdr);
+	int			size = 0;
+	int			end;
+	int			i;
+
+	sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data;
+	sfe = &sf->list[0];
+	end = sf->hdr.count;
+	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
+			     base += size, i++) {
+		size = XFS_ATTR_SF_ENTSIZE(sfe);
+		if (!xfs_attr_match(args, sfe->namelen, sfe->nameval,
+				    sfe->flags))
+			continue;
+		break;
+	}
+
+	if (sfep != NULL)
+		*sfep = sfe;
+
+	if (basep != NULL)
+		*basep = base;
+
+	if (i == end)
+		return -ENOATTR;
+	return -EEXIST;
+}
+
+/*
  * Add a name/value pair to the shortform attribute list.
  * Overflow from the inode has already been checked for.
  */
 void
-xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
+xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff)
 {
-	xfs_attr_shortform_t *sf;
-	xfs_attr_sf_entry_t *sfe;
-	int i, offset, size;
-	xfs_mount_t *mp;
-	xfs_inode_t *dp;
-	struct xfs_ifork *ifp;
+	struct xfs_attr_shortform	*sf;
+	struct xfs_attr_sf_entry	*sfe;
+	int				offset, size, error;
+	struct xfs_mount		*mp;
+	struct xfs_inode		*dp;
+	struct xfs_ifork		*ifp;
 
 	trace_xfs_attr_sf_add(args);
 
@@ -686,11 +731,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
 	ifp = dp->i_afp;
 	ASSERT(ifp->if_flags & XFS_IFINLINE);
 	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
-	sfe = &sf->list[0];
-	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
-		ASSERT(!xfs_attr_match(args, sfe->namelen, sfe->nameval,
-			sfe->flags));
-	}
+	error = xfs_attr_sf_findname(args, &sfe, NULL);
+	ASSERT(error != -EEXIST);
 
 	offset = (char *)sfe - (char *)sf;
 	size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
@@ -733,31 +775,26 @@ xfs_attr_fork_remove(
  * Remove an attribute from the shortform attribute list structure.
  */
 int
-xfs_attr_shortform_remove(xfs_da_args_t *args)
+xfs_attr_shortform_remove(struct xfs_da_args *args)
 {
-	xfs_attr_shortform_t *sf;
-	xfs_attr_sf_entry_t *sfe;
-	int base, size=0, end, totsize, i;
-	xfs_mount_t *mp;
-	xfs_inode_t *dp;
+	struct xfs_attr_shortform	*sf;
+	struct xfs_attr_sf_entry	*sfe;
+	int				size = 0, end, totsize;
+	unsigned int			base;
+	struct xfs_mount		*mp;
+	struct xfs_inode		*dp;
+	int				error;
 
 	trace_xfs_attr_sf_remove(args);
 
 	dp = args->dp;
 	mp = dp->i_mount;
-	base = sizeof(xfs_attr_sf_hdr_t);
 	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
-	sfe = &sf->list[0];
-	end = sf->hdr.count;
-	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
-					base += size, i++) {
-		size = XFS_ATTR_SF_ENTSIZE(sfe);
-		if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
-				sfe->flags))
-			break;
-	}
-	if (i == end)
-		return -ENOATTR;
+
+	error = xfs_attr_sf_findname(args, &sfe, &base);
+	if (error != -EEXIST)
+		return error;
+	size = XFS_ATTR_SF_ENTSIZE(sfe);
 
 	/*
 	 * Fix up the attribute fork data, covering the hole
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h
index 6dd2d93..88ec042 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.h
+++ b/fs/xfs/libxfs/xfs_attr_leaf.h
@@ -52,6 +52,9 @@ int	xfs_attr_shortform_getvalue(struct xfs_da_args *args);
 int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
 			struct xfs_buf **leaf_bp);
 int	xfs_attr_shortform_remove(struct xfs_da_args *args);
+int	xfs_attr_sf_findname(struct xfs_da_args *args,
+			     struct xfs_attr_sf_entry **sfep,
+			     unsigned int *basep);
 int	xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
 int	xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
 xfs_failaddr_t xfs_attr_shortform_verify(struct xfs_inode *ip);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
  2020-04-03 22:12 ` [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-06 14:31   ` Brian Foster
  2020-04-15  6:43   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 03/20] xfs: Factor out new helper functions xfs_attr_rmtval_set Allison Collins
                   ` (17 subsequent siblings)
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Delayed operations cannot return error codes.  So we must check for
these conditions first before starting set or remove operations

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 2a0d3d3..f7e289e 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -404,6 +404,17 @@ xfs_attr_set(
 				args->total, 0, quota_flags);
 		if (error)
 			goto out_trans_cancel;
+
+		error = xfs_has_attr(args);
+		if (error == -EEXIST && (args->attr_flags & XATTR_CREATE))
+			goto out_trans_cancel;
+
+		if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
+			goto out_trans_cancel;
+
+		if (error != -ENOATTR && error != -EEXIST)
+			goto out_trans_cancel;
+
 		error = xfs_attr_set_args(args);
 		if (error)
 			goto out_trans_cancel;
@@ -411,6 +422,10 @@ xfs_attr_set(
 		if (!args->trans)
 			goto out_unlock;
 	} else {
+		error = xfs_has_attr(args);
+		if (error != -EEXIST)
+			goto out_trans_cancel;
+
 		error = xfs_attr_remove_args(args);
 		if (error)
 			goto out_trans_cancel;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 03/20] xfs: Factor out new helper functions xfs_attr_rmtval_set
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
  2020-04-03 22:12 ` [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines Allison Collins
  2020-04-03 22:12 ` [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 04/20] xfs: Pull up trans handling in xfs_attr3_leaf_flipflags Allison Collins
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Break xfs_attr_rmtval_set into two helper functions
xfs_attr_rmt_find_hole and xfs_attr_rmtval_set_value.
xfs_attr_rmtval_set rolls the transaction between the helpers, but
delayed operations cannot.  We will use the helpers later when
constructing new delayed attribute routines.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr_remote.c | 149 +++++++++++++++++++++++++---------------
 1 file changed, 92 insertions(+), 57 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 01ad7f3..f825eed 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -440,32 +440,23 @@ xfs_attr_rmtval_get(
 }
 
 /*
- * Write the value associated with an attribute into the out-of-line buffer
- * that we have defined for it.
+ * Find a "hole" in the attribute address space large enough for us to drop the
+ * new attribute's value into
  */
-int
-xfs_attr_rmtval_set(
+STATIC int
+xfs_attr_rmt_find_hole(
 	struct xfs_da_args	*args)
 {
 	struct xfs_inode	*dp = args->dp;
 	struct xfs_mount	*mp = dp->i_mount;
-	struct xfs_bmbt_irec	map;
-	xfs_dablk_t		lblkno;
-	xfs_fileoff_t		lfileoff = 0;
-	uint8_t			*src = args->value;
-	int			blkcnt;
-	int			valuelen;
-	int			nmap;
 	int			error;
-	int			offset = 0;
-
-	trace_xfs_attr_rmtval_set(args);
+	int			blkcnt;
+	xfs_fileoff_t		lfileoff = 0;
 
 	/*
-	 * Find a "hole" in the attribute address space large enough for
-	 * us to drop the new attribute's value into. Because CRC enable
-	 * attributes have headers, we can't just do a straight byte to FSB
-	 * conversion and have to take the header space into account.
+	 * Because CRC enable attributes have headers, we can't just do a
+	 * straight byte to FSB conversion and have to take the header space
+	 * into account.
 	 */
 	blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
 	error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
@@ -473,48 +464,26 @@ xfs_attr_rmtval_set(
 	if (error)
 		return error;
 
-	args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff;
+	args->rmtblkno = (xfs_dablk_t)lfileoff;
 	args->rmtblkcnt = blkcnt;
 
-	/*
-	 * Roll through the "value", allocating blocks on disk as required.
-	 */
-	while (blkcnt > 0) {
-		/*
-		 * Allocate a single extent, up to the size of the value.
-		 *
-		 * Note that we have to consider this a data allocation as we
-		 * write the remote attribute without logging the contents.
-		 * Hence we must ensure that we aren't using blocks that are on
-		 * the busy list so that we don't overwrite blocks which have
-		 * recently been freed but their transactions are not yet
-		 * committed to disk. If we overwrite the contents of a busy
-		 * extent and then crash then the block may not contain the
-		 * correct metadata after log recovery occurs.
-		 */
-		nmap = 1;
-		error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno,
-				  blkcnt, XFS_BMAPI_ATTRFORK, args->total, &map,
-				  &nmap);
-		if (error)
-			return error;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
-
-		ASSERT(nmap == 1);
-		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
-		       (map.br_startblock != HOLESTARTBLOCK));
-		lblkno += map.br_blockcount;
-		blkcnt -= map.br_blockcount;
+	return 0;
+}
 
-		/*
-		 * Start the next trans in the chain.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
-			return error;
-	}
+STATIC int
+xfs_attr_rmtval_set_value(
+	struct xfs_da_args	*args)
+{
+	struct xfs_inode	*dp = args->dp;
+	struct xfs_mount	*mp = dp->i_mount;
+	struct xfs_bmbt_irec	map;
+	xfs_dablk_t		lblkno;
+	uint8_t			*src = args->value;
+	int			blkcnt;
+	int			valuelen;
+	int			nmap;
+	int			error;
+	int			offset = 0;
 
 	/*
 	 * Roll through the "value", copying the attribute value to the
@@ -595,6 +564,72 @@ xfs_attr_rmtval_stale(
 }
 
 /*
+ * Write the value associated with an attribute into the out-of-line buffer
+ * that we have defined for it.
+ */
+int
+xfs_attr_rmtval_set(
+	struct xfs_da_args	*args)
+{
+	struct xfs_inode	*dp = args->dp;
+	struct xfs_bmbt_irec	map;
+	xfs_dablk_t		lblkno;
+	int			blkcnt;
+	int			nmap;
+	int			error;
+
+	trace_xfs_attr_rmtval_set(args);
+
+	error = xfs_attr_rmt_find_hole(args);
+	if (error)
+		return error;
+
+	blkcnt = args->rmtblkcnt;
+	lblkno = (xfs_dablk_t)args->rmtblkno;
+	/*
+	 * Roll through the "value", allocating blocks on disk as required.
+	 */
+	while (blkcnt > 0) {
+		/*
+		 * Allocate a single extent, up to the size of the value.
+		 *
+		 * Note that we have to consider this a data allocation as we
+		 * write the remote attribute without logging the contents.
+		 * Hence we must ensure that we aren't using blocks that are on
+		 * the busy list so that we don't overwrite blocks which have
+		 * recently been freed but their transactions are not yet
+		 * committed to disk. If we overwrite the contents of a busy
+		 * extent and then crash then the block may not contain the
+		 * correct metadata after log recovery occurs.
+		 */
+		nmap = 1;
+		error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno,
+				  blkcnt, XFS_BMAPI_ATTRFORK, args->total, &map,
+				  &nmap);
+		if (error)
+			return error;
+		error = xfs_defer_finish(&args->trans);
+		if (error)
+			return error;
+
+		ASSERT(nmap == 1);
+		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+		       (map.br_startblock != HOLESTARTBLOCK));
+		lblkno += map.br_blockcount;
+		blkcnt -= map.br_blockcount;
+
+		/*
+		 * Start the next trans in the chain.
+		 */
+		error = xfs_trans_roll_inode(&args->trans, dp);
+		if (error)
+			return error;
+	}
+
+	return xfs_attr_rmtval_set_value(args);
+}
+
+/*
  * Remove the value associated with an attribute by deleting the
  * out-of-line buffer that it is stored on.
  */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 04/20] xfs: Pull up trans handling in xfs_attr3_leaf_flipflags
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (2 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 03/20] xfs: Factor out new helper functions xfs_attr_rmtval_set Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 05/20] xfs: Split apart xfs_attr_leaf_addname Allison Collins
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Since delayed operations cannot roll transactions, pull up the
transaction handling into the calling function

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c      | 14 ++++++++++++++
 fs/xfs/libxfs/xfs_attr_leaf.c |  7 +------
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f7e289e..d9a4dc8 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -624,6 +624,13 @@ xfs_attr_leaf_addname(
 		error = xfs_attr3_leaf_flipflags(args);
 		if (error)
 			return error;
+		/*
+		 * Commit the flag value change and start the next trans in
+		 * series.
+		 */
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		if (error)
+			return error;
 
 		/*
 		 * Dismantle the "old" attribute/value pair by removing
@@ -971,6 +978,13 @@ xfs_attr_node_addname(
 		error = xfs_attr3_leaf_flipflags(args);
 		if (error)
 			goto out;
+		/*
+		 * Commit the flag value change and start the next trans in
+		 * series
+		 */
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		if (error)
+			goto out;
 
 		/*
 		 * Dismantle the "old" attribute/value pair by removing
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 9f39e7a..ab87bae 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -2953,10 +2953,5 @@ xfs_attr3_leaf_flipflags(
 			 XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt)));
 	}
 
-	/*
-	 * Commit the flag value change and start the next trans in series.
-	 */
-	error = xfs_trans_roll_inode(&args->trans, args->dp);
-
-	return error;
+	return 0;
 }
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 05/20] xfs: Split apart xfs_attr_leaf_addname
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (3 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 04/20] xfs: Pull up trans handling in xfs_attr3_leaf_flipflags Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 06/20] xfs: Refactor xfs_attr_try_sf_addname Allison Collins
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Split out new helper function xfs_attr_leaf_try_add from
xfs_attr_leaf_addname. Because new delayed attribute routines cannot
roll transactions, we split off the parts of xfs_attr_leaf_addname that
we can use, and move the commit into the calling function.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c | 94 ++++++++++++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 34 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index d9a4dc8..45e6251 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -256,10 +256,30 @@ xfs_attr_set_args(
 		}
 	}
 
-	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
+	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
 		error = xfs_attr_leaf_addname(args);
-	else
-		error = xfs_attr_node_addname(args);
+		if (error != -ENOSPC)
+			return error;
+
+		/*
+		 * Commit that transaction so that the node_addname()
+		 * call can manage its own transactions.
+		 */
+		error = xfs_defer_finish(&args->trans);
+		if (error)
+			return error;
+
+		/*
+		 * Commit the current trans (including the inode) and
+		 * start a new one.
+		 */
+		error = xfs_trans_roll_inode(&args->trans, dp);
+		if (error)
+			return error;
+
+	}
+
+	error = xfs_attr_node_addname(args);
 	return error;
 }
 
@@ -509,20 +529,21 @@ xfs_attr_shortform_addname(xfs_da_args_t *args)
  *========================================================================*/
 
 /*
- * Add a name to the leaf attribute list structure
+ * Tries to add an attribute to an inode in leaf form
  *
- * This leaf block cannot have a "remote" value, we only call this routine
- * if bmap_one_block() says there is only one block (ie: no remote blks).
+ * This function is meant to execute as part of a delayed operation and leaves
+ * the transaction handling to the caller.  On success the attribute is added
+ * and the inode and transaction are left dirty.  If there is not enough space,
+ * the attr data is converted to node format and -ENOSPC is returned. Caller is
+ * responsible for handling the dirty inode and transaction or adding the attr
+ * in node format.
  */
 STATIC int
-xfs_attr_leaf_addname(
-	struct xfs_da_args	*args)
+xfs_attr_leaf_try_add(
+	struct xfs_da_args	*args,
+	struct xfs_buf		*bp)
 {
-	struct xfs_buf		*bp;
-	int			retval, error, forkoff;
-	struct xfs_inode	*dp = args->dp;
-
-	trace_xfs_attr_leaf_addname(args);
+	int			retval, error;
 
 	/*
 	 * Look up the given attribute in the leaf block.  Figure out if
@@ -564,31 +585,39 @@ xfs_attr_leaf_addname(
 	retval = xfs_attr3_leaf_add(bp, args);
 	if (retval == -ENOSPC) {
 		/*
-		 * Promote the attribute list to the Btree format, then
-		 * Commit that transaction so that the node_addname() call
-		 * can manage its own transactions.
+		 * Promote the attribute list to the Btree format. Unless an
+		 * error occurs, retain the -ENOSPC retval
 		 */
 		error = xfs_attr3_leaf_to_node(args);
 		if (error)
 			return error;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
+	}
+	return retval;
+out_brelse:
+	xfs_trans_brelse(args->trans, bp);
+	return retval;
+}
 
-		/*
-		 * Commit the current trans (including the inode) and start
-		 * a new one.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
-			return error;
 
-		/*
-		 * Fob the whole rest of the problem off on the Btree code.
-		 */
-		error = xfs_attr_node_addname(args);
+/*
+ * Add a name to the leaf attribute list structure
+ *
+ * This leaf block cannot have a "remote" value, we only call this routine
+ * if bmap_one_block() says there is only one block (ie: no remote blks).
+ */
+STATIC int
+xfs_attr_leaf_addname(
+	struct xfs_da_args	*args)
+{
+	int			error, forkoff;
+	struct xfs_buf		*bp = NULL;
+	struct xfs_inode	*dp = args->dp;
+
+	trace_xfs_attr_leaf_addname(args);
+
+	error = xfs_attr_leaf_try_add(args, bp);
+	if (error)
 		return error;
-	}
 
 	/*
 	 * Commit the transaction that added the attr name so that
@@ -683,9 +712,6 @@ xfs_attr_leaf_addname(
 		error = xfs_attr3_leaf_clearflag(args);
 	}
 	return error;
-out_brelse:
-	xfs_trans_brelse(args->trans, bp);
-	return retval;
 }
 
 /*
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 06/20] xfs: Refactor xfs_attr_try_sf_addname
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (4 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 05/20] xfs: Split apart xfs_attr_leaf_addname Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 07/20] xfs: Pull up trans roll from xfs_attr3_leaf_setflag Allison Collins
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

To help pre-simplify xfs_attr_set_args, we need to hoist transaction
handling up, while modularizing the adjacent code down into helpers. In
this patch, hoist the commit in xfs_attr_try_sf_addname up into the
calling function, and also pull the attr list creation down.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 45e6251..f8c7582 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -178,8 +178,13 @@ xfs_attr_try_sf_addname(
 	struct xfs_da_args	*args)
 {
 
-	struct xfs_mount	*mp = dp->i_mount;
-	int			error, error2;
+	int			error;
+
+	/*
+	 * Build initial attribute list (if required).
+	 */
+	if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
+		xfs_attr_shortform_create(args);
 
 	error = xfs_attr_shortform_addname(args);
 	if (error == -ENOSPC)
@@ -192,12 +197,10 @@ xfs_attr_try_sf_addname(
 	if (!error && !(args->op_flags & XFS_DA_OP_NOTIME))
 		xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
 
-	if (mp->m_flags & XFS_MOUNT_WSYNC)
+	if (dp->i_mount->m_flags & XFS_MOUNT_WSYNC)
 		xfs_trans_set_sync(args->trans);
 
-	error2 = xfs_trans_commit(args->trans);
-	args->trans = NULL;
-	return error ? error : error2;
+	return error;
 }
 
 /*
@@ -209,7 +212,7 @@ xfs_attr_set_args(
 {
 	struct xfs_inode	*dp = args->dp;
 	struct xfs_buf          *leaf_bp = NULL;
-	int			error;
+	int			error, error2 = 0;
 
 	/*
 	 * If the attribute list is non-existent or a shortform list,
@@ -220,17 +223,14 @@ xfs_attr_set_args(
 	     dp->i_d.di_anextents == 0)) {
 
 		/*
-		 * Build initial attribute list (if required).
-		 */
-		if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
-			xfs_attr_shortform_create(args);
-
-		/*
 		 * Try to add the attr to the attribute list in the inode.
 		 */
 		error = xfs_attr_try_sf_addname(dp, args);
-		if (error != -ENOSPC)
-			return error;
+		if (error != -ENOSPC) {
+			error2 = xfs_trans_commit(args->trans);
+			args->trans = NULL;
+			return error ? error : error2;
+		}
 
 		/*
 		 * It won't fit in the shortform, transform to a leaf block.
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 07/20] xfs: Pull up trans roll from xfs_attr3_leaf_setflag
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (5 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 06/20] xfs: Refactor xfs_attr_try_sf_addname Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 08/20] xfs: Factor out xfs_attr_rmtval_invalidate Allison Collins
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

New delayed allocation routines cannot be handling transactions so
pull them up into the calling functions

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c      | 5 +++++
 fs/xfs/libxfs/xfs_attr_leaf.c | 5 +----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f8c7582..ec5890a 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1134,6 +1134,11 @@ xfs_attr_node_removename(
 		error = xfs_attr3_leaf_setflag(args);
 		if (error)
 			goto out;
+
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		if (error)
+			goto out;
+
 		error = xfs_attr_rmtval_remove(args);
 		if (error)
 			goto out;
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index ab87bae..b588f8e 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -2835,10 +2835,7 @@ xfs_attr3_leaf_setflag(
 			 XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
 	}
 
-	/*
-	 * Commit the flag value change and start the next trans in series.
-	 */
-	return xfs_trans_roll_inode(&args->trans, args->dp);
+	return 0;
 }
 
 /*
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 08/20] xfs: Factor out xfs_attr_rmtval_invalidate
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (6 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 07/20] xfs: Pull up trans roll from xfs_attr3_leaf_setflag Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 09/20] xfs: Pull up trans roll in xfs_attr3_leaf_clearflag Allison Collins
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Because new delayed attribute routines cannot roll transactions, we
carve off the parts of xfs_attr_rmtval_remove that we can use.  This
will help to reduce repetitive code later when we introduce delayed
attributes.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr_remote.c | 26 +++++++++++++++++++++-----
 fs/xfs/libxfs/xfs_attr_remote.h |  2 +-
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index f825eed..4d51969 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -634,15 +634,12 @@ xfs_attr_rmtval_set(
  * out-of-line buffer that it is stored on.
  */
 int
-xfs_attr_rmtval_remove(
+xfs_attr_rmtval_invalidate(
 	struct xfs_da_args	*args)
 {
 	xfs_dablk_t		lblkno;
 	int			blkcnt;
 	int			error;
-	int			done;
-
-	trace_xfs_attr_rmtval_remove(args);
 
 	/*
 	 * Roll through the "value", invalidating the attribute value's blocks.
@@ -670,13 +667,32 @@ xfs_attr_rmtval_remove(
 		lblkno += map.br_blockcount;
 		blkcnt -= map.br_blockcount;
 	}
+	return 0;
+}
 
+/*
+ * Remove the value associated with an attribute by deleting the
+ * out-of-line buffer that it is stored on.
+ */
+int
+xfs_attr_rmtval_remove(
+	struct xfs_da_args      *args)
+{
+	xfs_dablk_t		lblkno;
+	int			blkcnt;
+	int			error = 0;
+	int			done = 0;
+
+	trace_xfs_attr_rmtval_remove(args);
+
+	error = xfs_attr_rmtval_invalidate(args);
+	if (error)
+		return error;
 	/*
 	 * Keep de-allocating extents until the remote-value region is gone.
 	 */
 	lblkno = args->rmtblkno;
 	blkcnt = args->rmtblkcnt;
-	done = 0;
 	while (!done) {
 		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
 				    XFS_BMAPI_ATTRFORK, 1, &done);
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index 6fb4572..eff5f95 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -13,5 +13,5 @@ int xfs_attr_rmtval_set(struct xfs_da_args *args);
 int xfs_attr_rmtval_remove(struct xfs_da_args *args);
 int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
 		xfs_buf_flags_t incore_flags);
-
+int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
 #endif /* __XFS_ATTR_REMOTE_H__ */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 09/20] xfs: Pull up trans roll in xfs_attr3_leaf_clearflag
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (7 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 08/20] xfs: Factor out xfs_attr_rmtval_invalidate Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-03 22:12 ` [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove Allison Collins
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

New delayed allocation routines cannot be handling transactions so
pull them out into the calling functions

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c      | 16 ++++++++++++++++
 fs/xfs/libxfs/xfs_attr_leaf.c |  5 +----
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index ec5890a..db5a99c 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -710,6 +710,14 @@ xfs_attr_leaf_addname(
 		 * Added a "remote" value, just clear the incomplete flag.
 		 */
 		error = xfs_attr3_leaf_clearflag(args);
+		if (error)
+			return error;
+
+		/*
+		 * Commit the flag value change and start the next trans in
+		 * series.
+		 */
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
 	}
 	return error;
 }
@@ -1075,6 +1083,14 @@ xfs_attr_node_addname(
 		error = xfs_attr3_leaf_clearflag(args);
 		if (error)
 			goto out;
+
+		 /*
+		  * Commit the flag value change and start the next trans in
+		  * series.
+		  */
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		if (error)
+			goto out;
 	}
 	retval = error = 0;
 
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index b588f8e..f55402b 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -2784,10 +2784,7 @@ xfs_attr3_leaf_clearflag(
 			 XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
 	}
 
-	/*
-	 * Commit the flag value change and start the next trans in series.
-	 */
-	return xfs_trans_roll_inode(&args->trans, args->dp);
+	return 0;
 }
 
 /*
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (8 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 09/20] xfs: Pull up trans roll in xfs_attr3_leaf_clearflag Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-07 14:16   ` Brian Foster
  2020-04-29 23:05   ` Darrick J. Wong
  2020-04-03 22:12 ` [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink Allison Collins
                   ` (9 subsequent siblings)
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This function is similar to xfs_attr_rmtval_remove, but adapted to
return EAGAIN for new transactions. We will use this later when we
introduce delayed attributes.  This function will eventually replace
xfs_attr_rmtval_remove

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr_remote.c | 25 +++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_attr_remote.h |  1 +
 2 files changed, 26 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 4d51969..fd4be9d 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -711,3 +711,28 @@ xfs_attr_rmtval_remove(
 	}
 	return 0;
 }
+
+/*
+ * Remove the value associated with an attribute by deleting the out-of-line
+ * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
+ * transaction and recall the function
+ */
+int
+__xfs_attr_rmtval_remove(
+	struct xfs_da_args	*args)
+{
+	int	error, done;
+
+	/*
+	 * Unmap value blocks for this attr.
+	 */
+	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
+			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
+	if (error)
+		return error;
+
+	if (!done)
+		return -EAGAIN;
+
+	return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index eff5f95..ee3337b 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -14,4 +14,5 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args);
 int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
 		xfs_buf_flags_t incore_flags);
 int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
+int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
 #endif /* __XFS_ATTR_REMOTE_H__ */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (9 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-07 14:17   ` Brian Foster
  2020-04-15 10:16   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls Allison Collins
                   ` (8 subsequent siblings)
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch adds a new helper function xfs_attr_node_shrink used to
shrink an attr name into an inode if it is small enough.  This helps to
modularize the greater calling function xfs_attr_node_removename.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 67 ++++++++++++++++++++++++++++++------------------
 1 file changed, 42 insertions(+), 25 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index db5a99c..27a9bb5 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1103,6 +1103,45 @@ xfs_attr_node_addname(
 }
 
 /*
+ * Shrink an attribute from leaf to shortform
+ */
+STATIC int
+xfs_attr_node_shrink(
+	struct xfs_da_args	*args,
+	struct xfs_da_state     *state)
+{
+	struct xfs_inode	*dp = args->dp;
+	int			error, forkoff;
+	struct xfs_buf		*bp;
+
+	/*
+	 * Have to get rid of the copy of this dabuf in the state.
+	 */
+	ASSERT(state->path.active == 1);
+	ASSERT(state->path.blk[0].bp);
+	state->path.blk[0].bp = NULL;
+
+	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
+	if (error)
+		return error;
+
+	forkoff = xfs_attr_shortform_allfit(bp, dp);
+	if (forkoff) {
+		error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
+		/* bp is gone due to xfs_da_shrink_inode */
+		if (error)
+			return error;
+
+		error = xfs_defer_finish(&args->trans);
+		if (error)
+			return error;
+	} else
+		xfs_trans_brelse(args->trans, bp);
+
+	return 0;
+}
+
+/*
  * Remove a name from a B-tree attribute list.
  *
  * This will involve walking down the Btree, and may involve joining
@@ -1115,8 +1154,7 @@ xfs_attr_node_removename(
 {
 	struct xfs_da_state	*state;
 	struct xfs_da_state_blk	*blk;
-	struct xfs_buf		*bp;
-	int			retval, error, forkoff;
+	int			retval, error;
 	struct xfs_inode	*dp = args->dp;
 
 	trace_xfs_attr_node_removename(args);
@@ -1197,31 +1235,10 @@ xfs_attr_node_removename(
 	/*
 	 * If the result is small enough, push it all into the inode.
 	 */
-	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-		/*
-		 * Have to get rid of the copy of this dabuf in the state.
-		 */
-		ASSERT(state->path.active == 1);
-		ASSERT(state->path.blk[0].bp);
-		state->path.blk[0].bp = NULL;
-
-		error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
-		if (error)
-			goto out;
+	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
+		error = xfs_attr_node_shrink(args, state);
 
-		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
-			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
-			/* bp is gone due to xfs_da_shrink_inode */
-			if (error)
-				goto out;
-			error = xfs_defer_finish(&args->trans);
-			if (error)
-				goto out;
-		} else
-			xfs_trans_brelse(args->trans, bp);
-	}
 	error = 0;
-
 out:
 	if (state)
 		xfs_da_state_free(state);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (10 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-07 14:17   ` Brian Foster
  2020-04-03 22:12 ` [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform Allison Collins
                   ` (7 subsequent siblings)
  19 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Some calls to xfs_trans_roll_inode in the *_addname routines are not
needed. If they are the last operations executed in these functions, and
no further changes are made, then higher level routines will roll or
commit the tranactions. The xfs_trans_roll in _removename is also not
needed because invalidating blocks is not an incore change.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 30 ------------------------------
 1 file changed, 30 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 27a9bb5..4225a94 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -700,11 +700,6 @@ xfs_attr_leaf_addname(
 				return error;
 		}
 
-		/*
-		 * Commit the remove and start the next trans in series.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-
 	} else if (args->rmtblkno > 0) {
 		/*
 		 * Added a "remote" value, just clear the incomplete flag.
@@ -712,12 +707,6 @@ xfs_attr_leaf_addname(
 		error = xfs_attr3_leaf_clearflag(args);
 		if (error)
 			return error;
-
-		/*
-		 * Commit the flag value change and start the next trans in
-		 * series.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
 	}
 	return error;
 }
@@ -1069,13 +1058,6 @@ xfs_attr_node_addname(
 				goto out;
 		}
 
-		/*
-		 * Commit and start the next trans in the chain.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
-			goto out;
-
 	} else if (args->rmtblkno > 0) {
 		/*
 		 * Added a "remote" value, just clear the incomplete flag.
@@ -1083,14 +1065,6 @@ xfs_attr_node_addname(
 		error = xfs_attr3_leaf_clearflag(args);
 		if (error)
 			goto out;
-
-		 /*
-		  * Commit the flag value change and start the next trans in
-		  * series.
-		  */
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
-		if (error)
-			goto out;
 	}
 	retval = error = 0;
 
@@ -1189,10 +1163,6 @@ xfs_attr_node_removename(
 		if (error)
 			goto out;
 
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
-		if (error)
-			goto out;
-
 		error = xfs_attr_rmtval_remove(args);
 		if (error)
 			goto out;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (11 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-07 15:23   ` Brian Foster
  2020-04-20  5:30   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete Allison Collins
                   ` (6 subsequent siblings)
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

In this patch, we hoist code from xfs_attr_set_args into two new helpers
xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
to simplify xfs_attr_set_args when we get into delayed attrs later.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 107 +++++++++++++++++++++++++++++++----------------
 1 file changed, 72 insertions(+), 35 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 4225a94..ba26ffe 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
 }
 
 /*
+ * Check to see if the attr should be upgraded from non-existent or shortform to
+ * single-leaf-block attribute list.
+ */
+static inline bool
+xfs_attr_is_shortform(
+	struct xfs_inode    *ip)
+{
+	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
+	      (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+	      ip->i_d.di_anextents == 0);
+}
+
+/*
+ * Attempts to set an attr in shortform, or converts the tree to leaf form if
+ * there is not enough room.  If the attr is set, the transaction is committed
+ * and set to NULL.
+ */
+STATIC int
+xfs_attr_set_shortform(
+	struct xfs_da_args	*args,
+	struct xfs_buf		**leaf_bp)
+{
+	struct xfs_inode	*dp = args->dp;
+	int			error, error2 = 0;
+
+	/*
+	 * Try to add the attr to the attribute list in the inode.
+	 */
+	error = xfs_attr_try_sf_addname(dp, args);
+	if (error != -ENOSPC) {
+		error2 = xfs_trans_commit(args->trans);
+		args->trans = NULL;
+		return error ? error : error2;
+	}
+	/*
+	 * It won't fit in the shortform, transform to a leaf block.  GROT:
+	 * another possible req'mt for a double-split btree op.
+	 */
+	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
+	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. Once we're done rolling the transaction we
+	 * can release the hold and add the attr to the leaf.
+	 */
+	xfs_trans_bhold(args->trans, *leaf_bp);
+	error = xfs_defer_finish(&args->trans);
+	xfs_trans_bhold_release(args->trans, *leaf_bp);
+	if (error) {
+		xfs_trans_brelse(args->trans, *leaf_bp);
+		return error;
+	}
+
+	return 0;
+}
+
+/*
  * Set the attribute specified in @args.
  */
 int
@@ -212,48 +272,25 @@ xfs_attr_set_args(
 {
 	struct xfs_inode	*dp = args->dp;
 	struct xfs_buf          *leaf_bp = NULL;
-	int			error, error2 = 0;
+	int			error = 0;
 
 	/*
-	 * If the attribute list is non-existent or a shortform list,
-	 * upgrade it to a single-leaf-block attribute list.
+	 * 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.
 	 */
-	if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
-	    (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
-	     dp->i_d.di_anextents == 0)) {
-
-		/*
-		 * Try to add the attr to the attribute list in the inode.
-		 */
-		error = xfs_attr_try_sf_addname(dp, args);
-		if (error != -ENOSPC) {
-			error2 = xfs_trans_commit(args->trans);
-			args->trans = NULL;
-			return error ? error : error2;
-		}
-
-		/*
-		 * It won't fit in the shortform, transform to a leaf block.
-		 * GROT: another possible req'mt for a double-split btree op.
-		 */
-		error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
-		if (error)
-			return error;
+	if (xfs_attr_is_shortform(dp)) {
 
 		/*
-		 * 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.
-		 * Once we're done rolling the transaction we can release
-		 * the hold and add the attr to the leaf.
+		 * If the attr was successfully set in shortform, the
+		 * transaction is committed and set to NULL.  Otherwise, is it
+		 * converted from shortform to leaf, and the transaction is
+		 * retained.
 		 */
-		xfs_trans_bhold(args->trans, leaf_bp);
-		error = xfs_defer_finish(&args->trans);
-		xfs_trans_bhold_release(args->trans, leaf_bp);
-		if (error) {
-			xfs_trans_brelse(args->trans, leaf_bp);
+		error = xfs_attr_set_shortform(args, &leaf_bp);
+		if (error || !args->trans)
 			return error;
-		}
 	}
 
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (12 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-07 15:23   ` Brian Foster
  2020-04-03 22:12 ` [PATCH v8 15/20] xfs: Add remote block helper functions Allison Collins
                   ` (5 subsequent siblings)
  19 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch helps to simplify xfs_attr_node_removename by modularizing
the code around the transactions into helper functions.  This will make
the function easier to follow when we introduce delayed attributes.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c | 45 +++++++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index ba26ffe..8d7a5db 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1153,6 +1153,36 @@ xfs_attr_node_shrink(
 }
 
 /*
+ * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers
+ * for later deletion of the entry.
+ */
+STATIC int
+xfs_attr_leaf_mark_incomplete(
+	struct xfs_da_args	*args,
+	struct xfs_da_state	*state)
+{
+	int error;
+
+	/*
+	 * Fill in disk block numbers in the state structure
+	 * so that we can get the buffers back after we commit
+	 * several transactions in the following calls.
+	 */
+	error = xfs_attr_fillstate(state);
+	if (error)
+		return error;
+
+	/*
+	 * Mark the attribute as INCOMPLETE
+	 */
+	error = xfs_attr3_leaf_setflag(args);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+/*
  * Remove a name from a B-tree attribute list.
  *
  * This will involve walking down the Btree, and may involve joining
@@ -1183,20 +1213,7 @@ xfs_attr_node_removename(
 	ASSERT(blk->bp != NULL);
 	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 	if (args->rmtblkno > 0) {
-		/*
-		 * Fill in disk block numbers in the state structure
-		 * so that we can get the buffers back after we commit
-		 * several transactions in the following calls.
-		 */
-		error = xfs_attr_fillstate(state);
-		if (error)
-			goto out;
-
-		/*
-		 * Mark the attribute as INCOMPLETE, then bunmapi() the
-		 * remote value.
-		 */
-		error = xfs_attr3_leaf_setflag(args);
+		error = xfs_attr_leaf_mark_incomplete(args, state);
 		if (error)
 			goto out;
 
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 15/20] xfs: Add remote block helper functions
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (13 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-08 12:09   ` Brian Foster
  2020-04-03 22:12 ` [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup Allison Collins
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch adds two new helper functions xfs_attr_store_rmt_blk and
xfs_attr_restore_rmt_blk. These two helpers assist to remove redundant
code associated with storing and retrieving remote blocks during the
attr set operations.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c | 50 +++++++++++++++++++++++++++++-------------------
 1 file changed, 30 insertions(+), 20 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 8d7a5db..f70b4f2 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -565,6 +565,30 @@ xfs_attr_shortform_addname(xfs_da_args_t *args)
  * External routines when attribute list is one block
  *========================================================================*/
 
+/* Store info about a remote block */
+STATIC void
+xfs_attr_save_rmt_blk(
+	struct xfs_da_args	*args)
+{
+	args->blkno2 = args->blkno;
+	args->index2 = args->index;
+	args->rmtblkno2 = args->rmtblkno;
+	args->rmtblkcnt2 = args->rmtblkcnt;
+	args->rmtvaluelen2 = args->rmtvaluelen;
+}
+
+/* Set stored info about a remote block */
+STATIC void
+xfs_attr_restore_rmt_blk(
+	struct xfs_da_args	*args)
+{
+	args->blkno = args->blkno2;
+	args->index = args->index2;
+	args->rmtblkno = args->rmtblkno2;
+	args->rmtblkcnt = args->rmtblkcnt2;
+	args->rmtvaluelen = args->rmtvaluelen2;
+}
+
 /*
  * Tries to add an attribute to an inode in leaf form
  *
@@ -599,11 +623,7 @@ xfs_attr_leaf_try_add(
 
 		/* save the attribute state for later removal*/
 		args->op_flags |= XFS_DA_OP_RENAME;	/* an atomic rename */
-		args->blkno2 = args->blkno;		/* set 2nd entry info*/
-		args->index2 = args->index;
-		args->rmtblkno2 = args->rmtblkno;
-		args->rmtblkcnt2 = args->rmtblkcnt;
-		args->rmtvaluelen2 = args->rmtvaluelen;
+		xfs_attr_save_rmt_blk(args);
 
 		/*
 		 * clear the remote attr state now that it is saved so that the
@@ -702,11 +722,8 @@ xfs_attr_leaf_addname(
 		 * Dismantle the "old" attribute/value pair by removing
 		 * a "remote" value (if it exists).
 		 */
-		args->index = args->index2;
-		args->blkno = args->blkno2;
-		args->rmtblkno = args->rmtblkno2;
-		args->rmtblkcnt = args->rmtblkcnt2;
-		args->rmtvaluelen = args->rmtvaluelen2;
+		xfs_attr_restore_rmt_blk(args);
+
 		if (args->rmtblkno) {
 			error = xfs_attr_rmtval_remove(args);
 			if (error)
@@ -934,11 +951,7 @@ xfs_attr_node_addname(
 
 		/* save the attribute state for later removal*/
 		args->op_flags |= XFS_DA_OP_RENAME;	/* atomic rename op */
-		args->blkno2 = args->blkno;		/* set 2nd entry info*/
-		args->index2 = args->index;
-		args->rmtblkno2 = args->rmtblkno;
-		args->rmtblkcnt2 = args->rmtblkcnt;
-		args->rmtvaluelen2 = args->rmtvaluelen;
+		xfs_attr_save_rmt_blk(args);
 
 		/*
 		 * clear the remote attr state now that it is saved so that the
@@ -1050,11 +1063,8 @@ xfs_attr_node_addname(
 		 * Dismantle the "old" attribute/value pair by removing
 		 * a "remote" value (if it exists).
 		 */
-		args->index = args->index2;
-		args->blkno = args->blkno2;
-		args->rmtblkno = args->rmtblkno2;
-		args->rmtblkcnt = args->rmtblkcnt2;
-		args->rmtvaluelen = args->rmtvaluelen2;
+		xfs_attr_restore_rmt_blk(args);
+
 		if (args->rmtblkno) {
 			error = xfs_attr_rmtval_remove(args);
 			if (error)
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (14 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 15/20] xfs: Add remote block helper functions Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-08 12:09   ` Brian Foster
  2020-04-20  6:25   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt Allison Collins
                   ` (3 subsequent siblings)
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch adds a new helper function xfs_attr_node_removename_setup.
This will help modularize xfs_attr_node_removename when we add delay
ready attributes later.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 40 +++++++++++++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f70b4f2..3c33dc5 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1193,6 +1193,35 @@ xfs_attr_leaf_mark_incomplete(
 }
 
 /*
+ * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
+ * the blocks are valid.  Any remote blocks will be marked incomplete.
+ */
+STATIC
+int xfs_attr_node_removename_setup(
+	struct xfs_da_args	*args,
+	struct xfs_da_state	**state)
+{
+	int			error;
+	struct xfs_da_state_blk	*blk;
+
+	error = xfs_attr_node_hasname(args, state);
+	if (error != -EEXIST)
+		return error;
+
+	blk = &(*state)->path.blk[(*state)->path.active - 1];
+	ASSERT(blk->bp != NULL);
+	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+
+	if (args->rmtblkno > 0) {
+		error = xfs_attr_leaf_mark_incomplete(args, *state);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+/*
  * Remove a name from a B-tree attribute list.
  *
  * This will involve walking down the Btree, and may involve joining
@@ -1210,8 +1239,8 @@ xfs_attr_node_removename(
 
 	trace_xfs_attr_node_removename(args);
 
-	error = xfs_attr_node_hasname(args, &state);
-	if (error != -EEXIST)
+	error = xfs_attr_node_removename_setup(args, &state);
+	if (error)
 		goto out;
 
 	/*
@@ -1219,14 +1248,7 @@ xfs_attr_node_removename(
 	 * This is done before we remove the attribute so that we don't
 	 * overflow the maximum size of a transaction and/or hit a deadlock.
 	 */
-	blk = &state->path.blk[ state->path.active-1 ];
-	ASSERT(blk->bp != NULL);
-	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 	if (args->rmtblkno > 0) {
-		error = xfs_attr_leaf_mark_incomplete(args, state);
-		if (error)
-			goto out;
-
 		error = xfs_attr_rmtval_remove(args);
 		if (error)
 			goto out;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (15 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-08 12:10   ` Brian Foster
  2020-04-20  6:38   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 18/20] xfs: Add delay ready attr remove routines Allison Collins
                   ` (2 subsequent siblings)
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch adds another new helper function
xfs_attr_node_removename_rmt. This will also help modularize
xfs_attr_node_removename when we add delay ready attributes later.

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 3c33dc5..d735570 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1221,6 +1221,28 @@ int xfs_attr_node_removename_setup(
 	return 0;
 }
 
+STATIC int
+xfs_attr_node_removename_rmt (
+	struct xfs_da_args	*args,
+	struct xfs_da_state	*state)
+{
+	int			error = 0;
+
+	error = xfs_attr_rmtval_remove(args);
+	if (error)
+		return error;
+
+	/*
+	 * Refill the state structure with buffers, the prior calls
+	 * released our buffers.
+	 */
+	error = xfs_attr_refillstate(state);
+	if (error)
+		return error;
+
+	return 0;
+}
+
 /*
  * Remove a name from a B-tree attribute list.
  *
@@ -1249,15 +1271,7 @@ xfs_attr_node_removename(
 	 * overflow the maximum size of a transaction and/or hit a deadlock.
 	 */
 	if (args->rmtblkno > 0) {
-		error = xfs_attr_rmtval_remove(args);
-		if (error)
-			goto out;
-
-		/*
-		 * Refill the state structure with buffers, the prior calls
-		 * released our buffers.
-		 */
-		error = xfs_attr_refillstate(state);
+		error = xfs_attr_node_removename_rmt(args, state);
 		if (error)
 			goto out;
 	}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (16 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-13 12:30   ` Brian Foster
  2020-04-20  9:06   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 19/20] xfs: Add delay ready attr set routines Allison Collins
  2020-04-03 22:12 ` [PATCH v8 20/20] xfs: Rename __xfs_attr_rmtval_remove Allison Collins
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch modifies the attr remove routines to be delay ready. This
means they no longer roll or commit transactions, but instead return
-EAGAIN to have the calling routine roll and refresh the transaction. In
this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
uses a sort of state machine like switch to keep track of where it was
when EAGAIN was returned. xfs_attr_node_removename has also been
modified to use the switch, and a new version of xfs_attr_remove_args
consists of a simple loop to refresh the transaction until the operation
is completed.

Calls to xfs_attr_rmtval_remove are replaced with the delay ready
counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
helper) and then __xfs_attr_rmtval_remove. We will rename
__xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
done.

This patch also adds a new struct xfs_delattr_context, which we will use
to keep track of the current state of an attribute operation. The new
xfs_delattr_state enum is used to track various operations that are in
progress so that we know not to repeat them, and resume where we left
off before EAGAIN was returned to cycle out the transaction. Other
members take the place of local variables that need to retain their
values across multiple function recalls.

Below is a state machine diagram for attr remove operations. The
XFS_DAS_* states indicate places where the function would return
-EAGAIN, and then immediately resume from after being recalled by the
calling function.  States marked as a "subroutine state" indicate that
they belong to a subroutine, and so the calling function needs to pass
them back to that subroutine to allow it to finish where it left off.
But they otherwise do not have a role in the calling function other than
just passing through.

 xfs_attr_remove_iter()
         XFS_DAS_RM_SHRINK     ─┐
         (subroutine state)     │
                                │
         XFS_DAS_RMTVAL_REMOVE ─┤
         (subroutine state)     │
                                └─>xfs_attr_node_removename()
                                                 │
                                                 v
                                         need to remove
                                   ┌─n──  rmt blocks?
                                   │             │
                                   │             y
                                   │             │
                                   │             v
                                   │  ┌─>XFS_DAS_RMTVAL_REMOVE
                                   │  │          │
                                   │  │          v
                                   │  └──y── more blks
                                   │         to remove?
                                   │             │
                                   │             n
                                   │             │
                                   │             v
                                   │         need to
                                   └─────> shrink tree? ─n─┐
                                                 │         │
                                                 y         │
                                                 │         │
                                                 v         │
                                         XFS_DAS_RM_SHRINK │
                                                 │         │
                                                 v         │
                                                done <─────┘

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
 fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
 2 files changed, 168 insertions(+), 38 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index d735570..f700976 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
  */
 STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
 STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
-STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
 
 /*
@@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
  */
 STATIC int xfs_attr_node_get(xfs_da_args_t *args);
 STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
-STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
 				 struct xfs_da_state **state);
 STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
 STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
 
+STATIC void
+xfs_delattr_context_init(
+	struct xfs_delattr_context	*dac,
+	struct xfs_da_args		*args)
+{
+	memset(dac, 0, sizeof(struct xfs_delattr_context));
+	dac->da_args = args;
+}
+
 int
 xfs_inode_hasattr(
 	struct xfs_inode	*ip)
@@ -356,20 +365,66 @@ xfs_has_attr(
  */
 int
 xfs_attr_remove_args(
-	struct xfs_da_args      *args)
+	struct xfs_da_args	*args)
 {
+	int			error = 0;
+	struct			xfs_delattr_context dac;
+
+	xfs_delattr_context_init(&dac, args);
+
+	do {
+		error = xfs_attr_remove_iter(&dac);
+		if (error != -EAGAIN)
+			break;
+
+		if (dac.flags & XFS_DAC_DEFER_FINISH) {
+			dac.flags &= ~XFS_DAC_DEFER_FINISH;
+			error = xfs_defer_finish(&args->trans);
+			if (error)
+				break;
+		}
+
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		if (error)
+			break;
+	} while (true);
+
+	return error;
+}
+
+/*
+ * Remove the attribute specified in @args.
+ *
+ * This function may return -EAGAIN to signal that the transaction needs to be
+ * rolled.  Callers should continue calling this function until they receive a
+ * return value other than -EAGAIN.
+ */
+int
+xfs_attr_remove_iter(
+	struct xfs_delattr_context *dac)
+{
+	struct xfs_da_args	*args = dac->da_args;
 	struct xfs_inode	*dp = args->dp;
 	int			error;
 
+	/* State machine switch */
+	switch (dac->dela_state) {
+	case XFS_DAS_RM_SHRINK:
+	case XFS_DAS_RMTVAL_REMOVE:
+		return xfs_attr_node_removename(dac);
+	default:
+		break;
+	}
+
 	if (!xfs_inode_hasattr(dp)) {
 		error = -ENOATTR;
 	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
 		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
 		error = xfs_attr_shortform_remove(args);
 	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-		error = xfs_attr_leaf_removename(args);
+		error = xfs_attr_leaf_removename(dac);
 	} else {
-		error = xfs_attr_node_removename(args);
+		error = xfs_attr_node_removename(dac);
 	}
 
 	return error;
@@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
  */
 STATIC int
 xfs_attr_leaf_removename(
-	struct xfs_da_args	*args)
+	struct xfs_delattr_context	*dac)
 {
-	struct xfs_inode	*dp;
-	struct xfs_buf		*bp;
-	int			error, forkoff;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_inode		*dp;
+	struct xfs_buf			*bp;
+	int				error, forkoff;
 
 	trace_xfs_attr_leaf_removename(args);
 
@@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
 		/* bp is gone due to xfs_da_shrink_inode */
 		if (error)
 			return error;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
+
+		dac->flags |= XFS_DAC_DEFER_FINISH;
 	}
 	return 0;
 }
@@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
  */
 STATIC int
 xfs_attr_node_shrink(
-	struct xfs_da_args	*args,
-	struct xfs_da_state     *state)
+	struct xfs_delattr_context	*dac,
+	struct xfs_da_state		*state)
 {
-	struct xfs_inode	*dp = args->dp;
-	int			error, forkoff;
-	struct xfs_buf		*bp;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_inode		*dp = args->dp;
+	int				error, forkoff;
+	struct xfs_buf			*bp;
 
 	/*
 	 * Have to get rid of the copy of this dabuf in the state.
@@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
 		if (error)
 			return error;
 
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
+		dac->flags |= XFS_DAC_DEFER_FINISH;
 	} else
 		xfs_trans_brelse(args->trans, bp);
 
@@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
 
 /*
  * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
- * the blocks are valid.  Any remote blocks will be marked incomplete.
+ * the blocks are valid.  Any remote blocks will be marked incomplete and
+ * invalidated.
  */
 STATIC
 int xfs_attr_node_removename_setup(
-	struct xfs_da_args	*args,
-	struct xfs_da_state	**state)
+	struct xfs_delattr_context	*dac,
+	struct xfs_da_state		**state)
 {
+	struct xfs_da_args	*args = dac->da_args;
 	int			error;
 	struct xfs_da_state_blk	*blk;
 
@@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
 	ASSERT(blk->bp != NULL);
 	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 
+	/*
+	 * Store blk and state in the context incase we need to cycle out the
+	 * transaction
+	 */
+	dac->blk = blk;
+	dac->da_state = *state;
+
 	if (args->rmtblkno > 0) {
 		error = xfs_attr_leaf_mark_incomplete(args, *state);
 		if (error)
 			return error;
+
+		error = xfs_attr_rmtval_invalidate(args);
+		if (error)
+			return error;
 	}
 
 	return 0;
@@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
 {
 	int			error = 0;
 
-	error = xfs_attr_rmtval_remove(args);
+	/*
+	 * May return -EAGAIN to request that the caller recall this function
+	 */
+	error = __xfs_attr_rmtval_remove(args);
 	if (error)
 		return error;
 
@@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
  * This will involve walking down the Btree, and may involve joining
  * leaf nodes and even joining intermediate nodes up to and including
  * the root node (a special case of an intermediate node).
+ *
+ * This routine is meant to function as either an inline or 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_node_removename(
-	struct xfs_da_args	*args)
+	struct xfs_delattr_context	*dac)
 {
+	struct xfs_da_args	*args = dac->da_args;
 	struct xfs_da_state	*state;
 	struct xfs_da_state_blk	*blk;
 	int			retval, error;
 	struct xfs_inode	*dp = args->dp;
 
 	trace_xfs_attr_node_removename(args);
+	state = dac->da_state;
+	blk = dac->blk;
+
+	/* State machine switch */
+	switch (dac->dela_state) {
+	case XFS_DAS_RMTVAL_REMOVE:
+		goto das_rmtval_remove;
+	case XFS_DAS_RM_SHRINK:
+		goto das_rm_shrink;
+	default:
+		break;
+	}
 
-	error = xfs_attr_node_removename_setup(args, &state);
+	error = xfs_attr_node_removename_setup(dac, &state);
 	if (error)
 		goto out;
 
@@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
 	 * This is done before we remove the attribute so that we don't
 	 * overflow the maximum size of a transaction and/or hit a deadlock.
 	 */
+
+das_rmtval_remove:
+
 	if (args->rmtblkno > 0) {
 		error = xfs_attr_node_removename_rmt(args, state);
-		if (error)
-			goto out;
+		if (error) {
+			if (error == -EAGAIN)
+				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
+			return error;
+		}
 	}
 
 	/*
@@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
 		error = xfs_da3_join(state);
 		if (error)
 			goto out;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			goto out;
-		/*
-		 * Commit the Btree join operation and start a new trans.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
-			goto out;
+
+		dac->flags |= XFS_DAC_DEFER_FINISH;
+		dac->dela_state = XFS_DAS_RM_SHRINK;
+		return -EAGAIN;
 	}
 
+das_rm_shrink:
+	dac->dela_state = XFS_DAS_RM_SHRINK;
+
 	/*
 	 * If the result is small enough, push it all into the inode.
 	 */
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
-		error = xfs_attr_node_shrink(args, state);
+		error = xfs_attr_node_shrink(dac, state);
 
 	error = 0;
 out:
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 66575b8..0e8ae1a 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -74,6 +74,43 @@ struct xfs_attr_list_context {
 };
 
 
+/*
+ * ========================================================================
+ * Structure used to pass context around among the delayed routines.
+ * ========================================================================
+ */
+
+/*
+ * Enum values for xfs_delattr_context.da_state
+ *
+ * These values are used by delayed attribute operations to keep track  of where
+ * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
+ * calling function to roll the transaction, and then recall the subroutine to
+ * finish the operation.  The enum is then used by the subroutine to jump back
+ * to where it was and resume executing where it left off.
+ */
+enum xfs_delattr_state {
+				      /* Zero is uninitalized */
+	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
+	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
+};
+
+/*
+ * Defines for xfs_delattr_context.flags
+ */
+#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
+
+/*
+ * Context used for keeping track of delayed attribute operations
+ */
+struct xfs_delattr_context {
+	struct xfs_da_args      *da_args;
+	struct xfs_da_state     *da_state;
+	struct xfs_da_state_blk *blk;
+	unsigned int            flags;
+	enum xfs_delattr_state  dela_state;
+};
+
 /*========================================================================
  * Function prototypes for the kernel.
  *========================================================================*/
@@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
 int xfs_attr_set_args(struct xfs_da_args *args);
 int xfs_has_attr(struct xfs_da_args *args);
 int xfs_attr_remove_args(struct xfs_da_args *args);
+int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
 bool xfs_attr_namecheck(const void *name, size_t length);
 
 #endif	/* __XFS_ATTR_H__ */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (17 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 18/20] xfs: Add delay ready attr remove routines Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  2020-04-13 13:40   ` Brian Foster
  2020-04-20 11:45   ` Chandan Rajendra
  2020-04-03 22:12 ` [PATCH v8 20/20] xfs: Rename __xfs_attr_rmtval_remove Allison Collins
  19 siblings, 2 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

This patch modifies the attr set routines to be delay ready. This means
they no longer roll or commit transactions, but instead return -EAGAIN
to have the calling routine roll and refresh the transaction.  In this
series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
state machine like switch to keep track of where it was when EAGAIN was
returned.

Two new helper functions have been added: xfs_attr_rmtval_set_init and
xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
xfs_attr_rmtval_set, but they store the current block in the delay attr
context to allow the caller to roll the transaction between allocations.
This helps to simplify and consolidate code used by
xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
now become a simple loop to refresh the transaction until the operation
is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
removed.

Below is a state machine diagram for attr set operations. The XFS_DAS_*
states indicate places where the function would return -EAGAIN, and then
immediately resume from after being recalled by the calling function.
States marked as a "subroutine state" indicate that they belong to a
subroutine, and so the calling function needs to pass them back to that
subroutine to allow it to finish where it left off.  But they otherwise
do not have a role in the calling function other than just passing
through.

 xfs_attr_set_iter()
                 │
                 v
           need to upgrade
          from sf to leaf? ──n─┐
                 │             │
                 y             │
                 │             │
                 V             │
          XFS_DAS_ADD_LEAF     │
                 │             │
                 v             │
  ┌──────n── fork has   <──────┘
  │         only 1 blk?
  │              │
  │              y
  │              │
  │              v
  │     xfs_attr_leaf_try_add()
  │              │
  │              v
  │          had enough
  ├──────n──   space?
  │              │
  │              y
  │              │
  │              v
  │      XFS_DAS_FOUND_LBLK  ──┐
  │                            │
  │      XFS_DAS_FLIP_LFLAG  ──┤
  │      (subroutine state)    │
  │                            │
  │      XFS_DAS_ALLOC_LEAF  ──┤
  │      (subroutine state)    │
  │                            └─>xfs_attr_leaf_addname()
  │                                              │
  │                                              v
  │                                ┌─────n──  need to
  │                                │        alloc blks?
  │                                │             │
  │                                │             y
  │                                │             │
  │                                │             v
  │                                │  ┌─>XFS_DAS_ALLOC_LEAF
  │                                │  │          │
  │                                │  │          v
  │                                │  └──y── need to alloc
  │                                │         more blocks?
  │                                │             │
  │                                │             n
  │                                │             │
  │                                │             v
  │                                │          was this
  │                                └────────> a rename? ──n─┐
  │                                              │          │
  │                                              y          │
  │                                              │          │
  │                                              v          │
  │                                        flip incomplete  │
  │                                            flag         │
  │                                              │          │
  │                                              v          │
  │                                      XFS_DAS_FLIP_LFLAG │
  │                                              │          │
  │                                              v          │
  │                                            remove       │
  │                        XFS_DAS_RM_LBLK ─> old name      │
  │                                 ^            │          │
  │                                 │            v          │
  │                                 └──────y── more to      │
  │                                            remove       │
  │                                              │          │
  │                                              n          │
  │                                              │          │
  │                                              v          │
  │                                             done <──────┘
  └────> XFS_DAS_LEAF_TO_NODE ─┐
                               │
         XFS_DAS_FOUND_NBLK  ──┤
         (subroutine state)    │
                               │
         XFS_DAS_ALLOC_NODE  ──┤
         (subroutine state)    │
                               │
         XFS_DAS_FLIP_NFLAG  ──┤
         (subroutine state)    │
                               │
                               └─>xfs_attr_node_addname()
                                                 │
                                                 v
                                         find space to store
                                        attr. Split if needed
                                                 │
                                                 v
                                         XFS_DAS_FOUND_NBLK
                                                 │
                                                 v
                                   ┌─────n──  need to
                                   │        alloc blks?
                                   │             │
                                   │             y
                                   │             │
                                   │             v
                                   │  ┌─>XFS_DAS_ALLOC_NODE
                                   │  │          │
                                   │  │          v
                                   │  └──y── need to alloc
                                   │         more blocks?
                                   │             │
                                   │             n
                                   │             │
                                   │             v
                                   │          was this
                                   └────────> a rename? ──n─┐
                                                 │          │
                                                 y          │
                                                 │          │
                                                 v          │
                                           flip incomplete  │
                                               flag         │
                                                 │          │
                                                 v          │
                                         XFS_DAS_FLIP_NFLAG │
                                                 │          │
                                                 v          │
                                               remove       │
                           XFS_DAS_RM_NBLK ─> old name      │
                                    ^            │          │
                                    │            v          │
                                    └──────y── more to      │
                                               remove       │
                                                 │          │
                                                 n          │
                                                 │          │
                                                 v          │
                                                done <──────┘

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
 fs/xfs/libxfs/xfs_attr.h        |  16 ++
 fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
 fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
 fs/xfs/libxfs/xfs_attr_remote.h |   4 +
 fs/xfs/xfs_attr_inactive.c      |   1 +
 fs/xfs/xfs_trace.h              |   1 -
 7 files changed, 351 insertions(+), 167 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f700976..c160b7a 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
  * Internal routines when attribute list is one block.
  */
 STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
-STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
 
@@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
  * Internal routines when attribute list is more than one block.
  */
 STATIC int xfs_attr_node_get(xfs_da_args_t *args);
-STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
+STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
 				 struct xfs_da_state **state);
 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 void
 xfs_delattr_context_init(
@@ -227,8 +228,11 @@ xfs_attr_is_shortform(
 
 /*
  * Attempts to set an attr in shortform, or converts the tree to leaf form if
- * there is not enough room.  If the attr is set, the transaction is committed
- * and set to NULL.
+ * there is not enough room.  This function is meant to operate as a helper
+ * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
+ * that the calling function should roll the transaction, and then proceed to
+ * add the attr in leaf form.  This subroutine does not expect to be recalled
+ * again like the other delayed attr routines do.
  */
 STATIC int
 xfs_attr_set_shortform(
@@ -236,16 +240,16 @@ xfs_attr_set_shortform(
 	struct xfs_buf		**leaf_bp)
 {
 	struct xfs_inode	*dp = args->dp;
-	int			error, error2 = 0;
+	int			error = 0;
 
 	/*
 	 * Try to add the attr to the attribute list in the inode.
 	 */
 	error = xfs_attr_try_sf_addname(dp, args);
+
+	/* Should only be 0, -EEXIST or ENOSPC */
 	if (error != -ENOSPC) {
-		error2 = xfs_trans_commit(args->trans);
-		args->trans = NULL;
-		return error ? error : error2;
+		return error;
 	}
 	/*
 	 * It won't fit in the shortform, transform to a leaf block.  GROT:
@@ -258,18 +262,10 @@ xfs_attr_set_shortform(
 	/*
 	 * 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. Once we're done rolling the transaction we
-	 * can release the hold and add the attr to the leaf.
+	 * with the write verifier.
 	 */
 	xfs_trans_bhold(args->trans, *leaf_bp);
-	error = xfs_defer_finish(&args->trans);
-	xfs_trans_bhold_release(args->trans, *leaf_bp);
-	if (error) {
-		xfs_trans_brelse(args->trans, *leaf_bp);
-		return error;
-	}
-
-	return 0;
+	return -EAGAIN;
 }
 
 /*
@@ -279,9 +275,83 @@ int
 xfs_attr_set_args(
 	struct xfs_da_args	*args)
 {
-	struct xfs_inode	*dp = args->dp;
-	struct xfs_buf          *leaf_bp = NULL;
-	int			error = 0;
+	struct xfs_buf			*leaf_bp = NULL;
+	int				error = 0;
+	struct xfs_delattr_context	dac;
+
+	xfs_delattr_context_init(&dac, args);
+
+	do {
+		error = xfs_attr_set_iter(&dac, &leaf_bp);
+		if (error != -EAGAIN)
+			break;
+
+		if (dac.flags & XFS_DAC_DEFER_FINISH) {
+			dac.flags &= ~XFS_DAC_DEFER_FINISH;
+			error = xfs_defer_finish(&args->trans);
+			if (error)
+				break;
+		}
+
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		if (error)
+			break;
+
+		if (leaf_bp) {
+			xfs_trans_bjoin(args->trans, leaf_bp);
+			xfs_trans_bhold(args->trans, leaf_bp);
+		}
+
+	} while (true);
+
+	return error;
+}
+
+/*
+ * 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.
+ */
+int
+xfs_attr_set_iter(
+	struct xfs_delattr_context	*dac,
+	struct xfs_buf			**leaf_bp)
+{
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_inode		*dp = args->dp;
+	int				error = 0;
+	int				sf_size;
+
+	/* State machine switch */
+	switch (dac->dela_state) {
+	case XFS_DAS_ADD_LEAF:
+		goto das_add_leaf;
+	case XFS_DAS_ALLOC_LEAF:
+	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:
+	case XFS_DAS_LEAF_TO_NODE:
+		goto das_node;
+	default:
+		break;
+	}
+
+	/*
+	 * New inodes may not have an attribute fork yet. So set the attribute
+	 * fork appropriately
+	 */
+	if (XFS_IFORK_Q((args->dp)) == 0) {
+		sf_size = sizeof(struct xfs_attr_sf_hdr) +
+		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
+		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
+		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
+		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
+	}
 
 	/*
 	 * If the attribute list is already in leaf format, jump straight to
@@ -292,40 +362,53 @@ xfs_attr_set_args(
 	if (xfs_attr_is_shortform(dp)) {
 
 		/*
-		 * If the attr was successfully set in shortform, the
-		 * transaction is committed and set to NULL.  Otherwise, is it
-		 * converted from shortform to leaf, and the transaction is
-		 * retained.
+		 * 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 || !args->trans)
-			return error;
+		error = xfs_attr_set_shortform(args, leaf_bp);
+		if (error == -EAGAIN) {
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			dac->dela_state = XFS_DAS_ADD_LEAF;
+		}
+		return error;
 	}
 
-	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-		error = xfs_attr_leaf_addname(args);
-		if (error != -ENOSPC)
-			return error;
+das_add_leaf:
 
-		/*
-		 * Commit that transaction so that the node_addname()
-		 * call can manage its own transactions.
-		 */
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
+	/*
+	 * After a shortform to leaf conversion, we need to hold the leaf and
+	 * cylce out the transaction.  When we get back, we need to release
+	 * the leaf.
+	 */
+	if (*leaf_bp != NULL) {
+		xfs_trans_brelse(args->trans, *leaf_bp);
+		*leaf_bp = NULL;
+	}
 
-		/*
-		 * Commit the current trans (including the inode) and
-		 * start a new one.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
+	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+		error = xfs_attr_leaf_try_add(args, *leaf_bp);
+		switch (error) {
+		case -ENOSPC:
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
+			return -EAGAIN;
+		case 0:
+			dac->dela_state = XFS_DAS_FOUND_LBLK;
+			return -EAGAIN;
+		default:
 			return error;
-
+		}
+das_leaf:
+		error = xfs_attr_leaf_addname(dac);
+		if (error == -ENOSPC) {
+			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
+			return -EAGAIN;
+		}
+		return error;
 	}
-
-	error = xfs_attr_node_addname(args);
+das_node:
+	error = xfs_attr_node_addname(dac);
 	return error;
 }
 
@@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
  *
  * This leaf block cannot have a "remote" value, we only call this routine
  * if bmap_one_block() says there is only one block (ie: no remote blks).
+ *
+ * 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_leaf_addname(
-	struct xfs_da_args	*args)
+	struct xfs_delattr_context	*dac)
 {
-	int			error, forkoff;
-	struct xfs_buf		*bp = NULL;
-	struct xfs_inode	*dp = args->dp;
-
-	trace_xfs_attr_leaf_addname(args);
-
-	error = xfs_attr_leaf_try_add(args, bp);
-	if (error)
-		return error;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_buf			*bp = NULL;
+	int				error, forkoff;
+	struct xfs_inode		*dp = args->dp;
 
-	/*
-	 * Commit the transaction that added the attr name so that
-	 * later routines can manage their own transactions.
-	 */
-	error = xfs_trans_roll_inode(&args->trans, dp);
-	if (error)
-		return error;
+	/* State machine switch */
+	switch (dac->dela_state) {
+	case XFS_DAS_FLIP_LFLAG:
+		goto das_flip_flag;
+	case XFS_DAS_ALLOC_LEAF:
+		goto das_alloc_leaf;
+	case XFS_DAS_RM_LBLK:
+		goto das_rm_lblk;
+	default:
+		break;
+	}
 
 	/*
 	 * If there was an out-of-line value, allocate the blocks we
@@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
 	 * maximum size of a transaction and/or hit a deadlock.
 	 */
 	if (args->rmtblkno > 0) {
-		error = xfs_attr_rmtval_set(args);
+
+		/* Open coded xfs_attr_rmtval_set without trans handling */
+		error = xfs_attr_rmtval_set_init(dac);
+		if (error)
+			return error;
+
+		/*
+		 * Roll through the "value", allocating blocks on disk as
+		 * required.
+		 */
+das_alloc_leaf:
+		while (dac->blkcnt > 0) {
+			error = xfs_attr_rmtval_set_blk(dac);
+			if (error)
+				return error;
+
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			dac->dela_state = XFS_DAS_ALLOC_LEAF;
+			return -EAGAIN;
+		}
+
+		error = xfs_attr_rmtval_set_value(args);
 		if (error)
 			return error;
 	}
@@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
 		error = xfs_attr3_leaf_flipflags(args);
 		if (error)
 			return error;
-		/*
-		 * Commit the flag value change and start the next trans in
-		 * series.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
-		if (error)
-			return error;
-
+		dac->dela_state = XFS_DAS_FLIP_LFLAG;
+		return -EAGAIN;
+das_flip_flag:
 		/*
 		 * Dismantle the "old" attribute/value pair by removing
 		 * a "remote" value (if it exists).
 		 */
 		xfs_attr_restore_rmt_blk(args);
 
+		xfs_attr_rmtval_invalidate(args);
+das_rm_lblk:
 		if (args->rmtblkno) {
-			error = xfs_attr_rmtval_remove(args);
+			error = __xfs_attr_rmtval_remove(args);
+
+			if (error == -EAGAIN) {
+				dac->dela_state = XFS_DAS_RM_LBLK;
+				return -EAGAIN;
+			}
+
 			if (error)
 				return error;
 		}
@@ -799,15 +910,11 @@ xfs_attr_leaf_addname(
 		/*
 		 * If the result is small enough, shrink it all into the inode.
 		 */
-		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
+		forkoff = xfs_attr_shortform_allfit(bp, dp);
+		if (forkoff)
 			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
-			/* bp is gone due to xfs_da_shrink_inode */
-			if (error)
-				return error;
-			error = xfs_defer_finish(&args->trans);
-			if (error)
-				return error;
-		}
+
+		dac->flags |= XFS_DAC_DEFER_FINISH;
 
 	} else if (args->rmtblkno > 0) {
 		/*
@@ -967,16 +1074,23 @@ xfs_attr_node_hasname(
  *
  * "Remote" attribute values confuse the issue and atomic rename operations
  * add a whole extra layer of confusion on top of that.
+ *
+ * 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_node_addname(
-	struct xfs_da_args	*args)
+	struct xfs_delattr_context	*dac)
 {
-	struct xfs_da_state	*state;
-	struct xfs_da_state_blk	*blk;
-	struct xfs_inode	*dp;
-	struct xfs_mount	*mp;
-	int			retval, error;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_state		*state = NULL;
+	struct xfs_da_state_blk		*blk;
+	struct xfs_inode		*dp;
+	struct xfs_mount		*mp;
+	int				retval = 0;
+	int				error = 0;
 
 	trace_xfs_attr_node_addname(args);
 
@@ -985,7 +1099,21 @@ xfs_attr_node_addname(
 	 */
 	dp = args->dp;
 	mp = dp->i_mount;
-restart:
+
+	/* State machine switch */
+	switch (dac->dela_state) {
+	case XFS_DAS_FLIP_NFLAG:
+		goto das_flip_flag;
+	case XFS_DAS_FOUND_NBLK:
+		goto das_found_nblk;
+	case XFS_DAS_ALLOC_NODE:
+		goto das_alloc_node;
+	case XFS_DAS_RM_NBLK:
+		goto das_rm_nblk;
+	default:
+		break;
+	}
+
 	/*
 	 * Search to see if name already exists, and get back a pointer
 	 * to where it should go.
@@ -1031,19 +1159,13 @@ xfs_attr_node_addname(
 			error = xfs_attr3_leaf_to_node(args);
 			if (error)
 				goto out;
-			error = xfs_defer_finish(&args->trans);
-			if (error)
-				goto out;
 
 			/*
-			 * Commit the node conversion and start the next
-			 * trans in the chain.
+			 * Restart routine from the top.  No need to set  the
+			 * state
 			 */
-			error = xfs_trans_roll_inode(&args->trans, dp);
-			if (error)
-				goto out;
-
-			goto restart;
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			return -EAGAIN;
 		}
 
 		/*
@@ -1055,9 +1177,7 @@ xfs_attr_node_addname(
 		error = xfs_da3_split(state);
 		if (error)
 			goto out;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			goto out;
+		dac->flags |= XFS_DAC_DEFER_FINISH;
 	} else {
 		/*
 		 * Addition succeeded, update Btree hashvals.
@@ -1072,13 +1192,9 @@ xfs_attr_node_addname(
 	xfs_da_state_free(state);
 	state = NULL;
 
-	/*
-	 * Commit the leaf addition or btree split and start the next
-	 * trans in the chain.
-	 */
-	error = xfs_trans_roll_inode(&args->trans, dp);
-	if (error)
-		goto out;
+	dac->dela_state = XFS_DAS_FOUND_NBLK;
+	return -EAGAIN;
+das_found_nblk:
 
 	/*
 	 * If there was an out-of-line value, allocate the blocks we
@@ -1087,7 +1203,27 @@ xfs_attr_node_addname(
 	 * maximum size of a transaction and/or hit a deadlock.
 	 */
 	if (args->rmtblkno > 0) {
-		error = xfs_attr_rmtval_set(args);
+		/* Open coded xfs_attr_rmtval_set without trans handling */
+		error = xfs_attr_rmtval_set_init(dac);
+		if (error)
+			return error;
+
+		/*
+		 * Roll through the "value", allocating blocks on disk as
+		 * required.
+		 */
+das_alloc_node:
+		while (dac->blkcnt > 0) {
+			error = xfs_attr_rmtval_set_blk(dac);
+			if (error)
+				return error;
+
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			dac->dela_state = XFS_DAS_ALLOC_NODE;
+			return -EAGAIN;
+		}
+
+		error = xfs_attr_rmtval_set_value(args);
 		if (error)
 			return error;
 	}
@@ -1110,18 +1246,26 @@ xfs_attr_node_addname(
 		 * Commit the flag value change and start the next trans in
 		 * series
 		 */
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
-		if (error)
-			goto out;
-
+		dac->dela_state = XFS_DAS_FLIP_NFLAG;
+		return -EAGAIN;
+das_flip_flag:
 		/*
 		 * Dismantle the "old" attribute/value pair by removing
 		 * a "remote" value (if it exists).
 		 */
 		xfs_attr_restore_rmt_blk(args);
 
+		xfs_attr_rmtval_invalidate(args);
+
+das_rm_nblk:
 		if (args->rmtblkno) {
-			error = xfs_attr_rmtval_remove(args);
+			error = __xfs_attr_rmtval_remove(args);
+
+			if (error == -EAGAIN) {
+				dac->dela_state = XFS_DAS_RM_NBLK;
+				return -EAGAIN;
+			}
+
 			if (error)
 				return error;
 		}
@@ -1139,7 +1283,6 @@ xfs_attr_node_addname(
 		error = xfs_da3_node_lookup_int(state, &retval);
 		if (error)
 			goto out;
-
 		/*
 		 * Remove the name and update the hashvals in the tree.
 		 */
@@ -1147,7 +1290,6 @@ xfs_attr_node_addname(
 		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 		error = xfs_attr3_leaf_remove(blk->bp, args);
 		xfs_da3_fixhashpath(state, &state->path);
-
 		/*
 		 * Check to see if the tree needs to be collapsed.
 		 */
@@ -1155,11 +1297,9 @@ xfs_attr_node_addname(
 			error = xfs_da3_join(state);
 			if (error)
 				goto out;
-			error = xfs_defer_finish(&args->trans);
-			if (error)
-				goto out;
-		}
 
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+		}
 	} else if (args->rmtblkno > 0) {
 		/*
 		 * Added a "remote" value, just clear the incomplete flag.
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 0e8ae1a..67af9d1 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -93,6 +93,16 @@ enum xfs_delattr_state {
 				      /* Zero is uninitalized */
 	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
 	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
+	XFS_DAS_ADD_LEAF,	      /* We are adding a leaf attr */
+	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
+	XFS_DAS_LEAF_TO_NODE,	      /* Converted leaf to node */
+	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
+	XFS_DAS_ALLOC_LEAF,	      /* We are allocating leaf blocks */
+	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
+	XFS_DAS_RM_LBLK,	      /* A rename is removing leaf blocks */
+	XFS_DAS_ALLOC_NODE,	      /* We are allocating node blocks */
+	XFS_DAS_FLIP_NFLAG,	      /* Flipped node INCOMPLETE attr flag */
+	XFS_DAS_RM_NBLK,	      /* A rename is removing node blocks */
 };
 
 /*
@@ -105,8 +115,13 @@ enum xfs_delattr_state {
  */
 struct xfs_delattr_context {
 	struct xfs_da_args      *da_args;
+	struct xfs_bmbt_irec	map;
+	struct xfs_buf		*leaf_bp;
+	xfs_fileoff_t		lfileoff;
 	struct xfs_da_state     *da_state;
 	struct xfs_da_state_blk *blk;
+	xfs_dablk_t		lblkno;
+	int			blkcnt;
 	unsigned int            flags;
 	enum xfs_delattr_state  dela_state;
 };
@@ -126,6 +141,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
 int xfs_attr_get(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
 int xfs_attr_set_args(struct xfs_da_args *args);
+int xfs_attr_set_iter(struct xfs_delattr_context *dac, struct xfs_buf **leaf_bp);
 int xfs_has_attr(struct xfs_da_args *args);
 int xfs_attr_remove_args(struct xfs_da_args *args);
 int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index f55402b..4d15f45 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -19,6 +19,7 @@
 #include "xfs_bmap_btree.h"
 #include "xfs_bmap.h"
 #include "xfs_attr_sf.h"
+#include "xfs_attr.h"
 #include "xfs_attr_remote.h"
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index fd4be9d..9607fd2 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -443,7 +443,7 @@ xfs_attr_rmtval_get(
  * Find a "hole" in the attribute address space large enough for us to drop the
  * new attribute's value into
  */
-STATIC int
+int
 xfs_attr_rmt_find_hole(
 	struct xfs_da_args	*args)
 {
@@ -470,7 +470,7 @@ xfs_attr_rmt_find_hole(
 	return 0;
 }
 
-STATIC int
+int
 xfs_attr_rmtval_set_value(
 	struct xfs_da_args	*args)
 {
@@ -630,6 +630,71 @@ xfs_attr_rmtval_set(
 }
 
 /*
+ * Find a hole for the attr and store it in the delayed attr context.  This
+ * initializes the context to roll through allocating an attr extent for a
+ * delayed attr operation
+ */
+int
+xfs_attr_rmtval_set_init(
+	struct xfs_delattr_context	*dac)
+{
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_bmbt_irec		*map = &dac->map;
+	int error;
+
+	dac->lblkno = 0;
+	dac->lfileoff = 0;
+	dac->blkcnt = 0;
+	args->rmtblkcnt = 0;
+	args->rmtblkno = 0;
+	memset(map, 0, sizeof(struct xfs_bmbt_irec));
+
+	error = xfs_attr_rmt_find_hole(args);
+	if (error)
+		return error;
+
+	dac->blkcnt = args->rmtblkcnt;
+	dac->lblkno = args->rmtblkno;
+
+	return error;
+}
+
+/*
+ * Write one block of the value associated with an attribute into the
+ * out-of-line buffer that we have defined for it. This is similar to a subset
+ * of xfs_attr_rmtval_set, but records the current block to the delayed attr
+ * context, and leaves transaction handling to the caller.
+ */
+int
+xfs_attr_rmtval_set_blk(
+	struct xfs_delattr_context	*dac)
+{
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_inode		*dp = args->dp;
+	struct xfs_bmbt_irec		*map = &dac->map;
+	int nmap;
+	int error;
+
+	nmap = 1;
+	error = xfs_bmapi_write(args->trans, dp,
+		  (xfs_fileoff_t)dac->lblkno,
+		  dac->blkcnt, XFS_BMAPI_ATTRFORK,
+		  args->total, map, &nmap);
+	if (error)
+		return error;
+
+	ASSERT(nmap == 1);
+	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
+	       (map->br_startblock != HOLESTARTBLOCK));
+
+	/* roll attribute extent map forwards */
+	dac->lblkno += map->br_blockcount;
+	dac->blkcnt -= map->br_blockcount;
+
+	return 0;
+}
+
+/*
  * Remove the value associated with an attribute by deleting the
  * out-of-line buffer that it is stored on.
  */
@@ -671,48 +736,6 @@ xfs_attr_rmtval_invalidate(
 }
 
 /*
- * Remove the value associated with an attribute by deleting the
- * out-of-line buffer that it is stored on.
- */
-int
-xfs_attr_rmtval_remove(
-	struct xfs_da_args      *args)
-{
-	xfs_dablk_t		lblkno;
-	int			blkcnt;
-	int			error = 0;
-	int			done = 0;
-
-	trace_xfs_attr_rmtval_remove(args);
-
-	error = xfs_attr_rmtval_invalidate(args);
-	if (error)
-		return error;
-	/*
-	 * Keep de-allocating extents until the remote-value region is gone.
-	 */
-	lblkno = args->rmtblkno;
-	blkcnt = args->rmtblkcnt;
-	while (!done) {
-		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
-				    XFS_BMAPI_ATTRFORK, 1, &done);
-		if (error)
-			return error;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
-
-		/*
-		 * Close out trans and start the next one in the chain.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
-		if (error)
-			return error;
-	}
-	return 0;
-}
-
-/*
  * Remove the value associated with an attribute by deleting the out-of-line
  * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
  * transaction and recall the function
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index ee3337b..482dff9 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -15,4 +15,8 @@ int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
 		xfs_buf_flags_t incore_flags);
 int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
 int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
+int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
+int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
+int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
+int xfs_attr_rmtval_set_init(struct xfs_delattr_context *dac);
 #endif /* __XFS_ATTR_REMOTE_H__ */
diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
index c42f90e..3e8cec5 100644
--- a/fs/xfs/xfs_attr_inactive.c
+++ b/fs/xfs/xfs_attr_inactive.c
@@ -15,6 +15,7 @@
 #include "xfs_da_format.h"
 #include "xfs_da_btree.h"
 #include "xfs_inode.h"
+#include "xfs_attr.h"
 #include "xfs_attr_remote.h"
 #include "xfs_trans.h"
 #include "xfs_bmap.h"
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index a4323a6..26dc8bf 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1784,7 +1784,6 @@ DEFINE_ATTR_EVENT(xfs_attr_refillstate);
 
 DEFINE_ATTR_EVENT(xfs_attr_rmtval_get);
 DEFINE_ATTR_EVENT(xfs_attr_rmtval_set);
-DEFINE_ATTR_EVENT(xfs_attr_rmtval_remove);
 
 #define DEFINE_DA_EVENT(name) \
 DEFINE_EVENT(xfs_da_class, name, \
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v8 20/20] xfs: Rename __xfs_attr_rmtval_remove
  2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
                   ` (18 preceding siblings ...)
  2020-04-03 22:12 ` [PATCH v8 19/20] xfs: Add delay ready attr set routines Allison Collins
@ 2020-04-03 22:12 ` Allison Collins
  19 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-03 22:12 UTC (permalink / raw)
  To: linux-xfs

Now that xfs_attr_rmtval_remove is gone, rename __xfs_attr_rmtval_remove
to xfs_attr_rmtval_remove

Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c        | 6 +++---
 fs/xfs/libxfs/xfs_attr_remote.c | 2 +-
 fs/xfs/libxfs/xfs_attr_remote.h | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index c160b7a..8a89394 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -885,7 +885,7 @@ xfs_attr_leaf_addname(
 		xfs_attr_rmtval_invalidate(args);
 das_rm_lblk:
 		if (args->rmtblkno) {
-			error = __xfs_attr_rmtval_remove(args);
+			error = xfs_attr_rmtval_remove(args);
 
 			if (error == -EAGAIN) {
 				dac->dela_state = XFS_DAS_RM_LBLK;
@@ -1259,7 +1259,7 @@ xfs_attr_node_addname(
 
 das_rm_nblk:
 		if (args->rmtblkno) {
-			error = __xfs_attr_rmtval_remove(args);
+			error = xfs_attr_rmtval_remove(args);
 
 			if (error == -EAGAIN) {
 				dac->dela_state = XFS_DAS_RM_NBLK;
@@ -1438,7 +1438,7 @@ xfs_attr_node_removename_rmt (
 	/*
 	 * May return -EAGAIN to request that the caller recall this function
 	 */
-	error = __xfs_attr_rmtval_remove(args);
+	error = xfs_attr_rmtval_remove(args);
 	if (error)
 		return error;
 
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 9607fd2..3cfe4d3 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -741,7 +741,7 @@ xfs_attr_rmtval_invalidate(
  * transaction and recall the function
  */
 int
-__xfs_attr_rmtval_remove(
+xfs_attr_rmtval_remove(
 	struct xfs_da_args	*args)
 {
 	int	error, done;
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index 482dff9..f39ef53 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -14,7 +14,7 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args);
 int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
 		xfs_buf_flags_t incore_flags);
 int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
-int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
+int xfs_attr_rmtval_remove(struct xfs_da_args *args);
 int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
 int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
 int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines
  2020-04-03 22:12 ` [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines Allison Collins
@ 2020-04-06 14:31   ` Brian Foster
  2020-04-06 22:09     ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-06 14:31 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:10PM -0700, Allison Collins wrote:
> This patch adds a new functions to check for the existence of an
> attribute. Subroutines are also added to handle the cases of leaf
> blocks, nodes or shortform. Common code that appears in existing attr
> add and remove functions have been factored out to help reduce the
> appearance of duplicated code.  We will need these routines later for
> delayed attributes since delayed operations cannot return error codes.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c      | 181 ++++++++++++++++++++++++++++--------------
>  fs/xfs/libxfs/xfs_attr.h      |   1 +
>  fs/xfs/libxfs/xfs_attr_leaf.c |  97 +++++++++++++++-------
>  fs/xfs/libxfs/xfs_attr_leaf.h |   3 +
>  4 files changed, 191 insertions(+), 91 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index e4fe3dc..2a0d3d3 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -703,21 +749,57 @@ xfs_attr_leaf_get(xfs_da_args_t *args)
>  
>  	trace_xfs_attr_leaf_get(args);
>  
> -	args->blkno = 0;
> -	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
> -	if (error)
> -		return error;
> +	error = xfs_attr_leaf_hasname(args, &bp);
>  
> -	error = xfs_attr3_leaf_lookup_int(bp, args);
>  	if (error != -EEXIST)  {
>  		xfs_trans_brelse(args->trans, bp);
>  		return error;
> -	}
> +	} else if (error != -EEXIST)
> +		return error;
> +

The if/else above looks busted (the same check for both branches).

> +
>  	error = xfs_attr3_leaf_getvalue(bp, args);
>  	xfs_trans_brelse(args->trans, bp);
>  	return error;
>  }
>  
> +/*
> + * Return EEXIST if attr is found, or ENOATTR if not
> + * statep: If not null is set to point at the found state.  Caller will
> + *         be responsible for freeing the state in this case.
> + */
> +STATIC int
> +xfs_attr_node_hasname(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	**statep)
> +{
> +	struct xfs_da_state	*state;
> +	int			retval, error;
> +
> +	state = xfs_da_state_alloc();
> +	state->args = args;
> +	state->mp = args->dp->i_mount;
> +
> +	if (statep != NULL)
> +		*statep = NULL;
> +
> +	/*
> +	 * Search to see if name exists, and get back a pointer to it.
> +	 */
> +	error = xfs_da3_node_lookup_int(state, &retval);
> +	if (error == 0) {
> +		if (statep != NULL)
> +			*statep = state;
> +		else
> +			xfs_da_state_free(state);
> +		return retval;
> +	}
> +
> +	xfs_da_state_free(state);
> +

Inverting the logic is a bit more clear IMO:

	...
	error = xfs_da3_node_lookup_int(state, &retval);
	if (error) {
		...
		return error;
	}

	if (statep)
		...
	else
		...
	return retval;
}

With those things addressed:

Reviewed-by: Brian Foster <bfoster@redhat.com>

> +	return error;
> +}
> +
>  /*========================================================================
>   * External routines when attribute list size > geo->blksize
>   *========================================================================*/
> @@ -750,17 +832,14 @@ xfs_attr_node_addname(
>  	dp = args->dp;
>  	mp = dp->i_mount;
>  restart:
> -	state = xfs_da_state_alloc();
> -	state->args = args;
> -	state->mp = mp;
> -
>  	/*
>  	 * Search to see if name already exists, and get back a pointer
>  	 * to where it should go.
>  	 */
> -	error = xfs_da3_node_lookup_int(state, &retval);
> -	if (error)
> +	retval = xfs_attr_node_hasname(args, &state);
> +	if (retval != -ENOATTR && retval != -EEXIST)
>  		goto out;
> +
>  	blk = &state->path.blk[ state->path.active-1 ];
>  	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
> @@ -965,29 +1044,15 @@ xfs_attr_node_removename(
>  {
>  	struct xfs_da_state	*state;
>  	struct xfs_da_state_blk	*blk;
> -	struct xfs_inode	*dp;
>  	struct xfs_buf		*bp;
>  	int			retval, error, forkoff;
> +	struct xfs_inode	*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
>  
> -	/*
> -	 * Tie a string around our finger to remind us where we are.
> -	 */
> -	dp = args->dp;
> -	state = xfs_da_state_alloc();
> -	state->args = args;
> -	state->mp = dp->i_mount;
> -
> -	/*
> -	 * Search to see if name exists, and get back a pointer to it.
> -	 */
> -	error = xfs_da3_node_lookup_int(state, &retval);
> -	if (error || (retval != -EEXIST)) {
> -		if (error == 0)
> -			error = retval;
> +	error = xfs_attr_node_hasname(args, &state);
> +	if (error != -EEXIST)
>  		goto out;
> -	}
>  
>  	/*
>  	 * If there is an out-of-line value, de-allocate the blocks.
> @@ -1082,7 +1147,8 @@ xfs_attr_node_removename(
>  	error = 0;
>  
>  out:
> -	xfs_da_state_free(state);
> +	if (state)
> +		xfs_da_state_free(state);
>  	return error;
>  }
>  
> @@ -1202,31 +1268,23 @@ xfs_attr_node_get(xfs_da_args_t *args)
>  {
>  	xfs_da_state_t *state;
>  	xfs_da_state_blk_t *blk;
> -	int error, retval;
> +	int error;
>  	int i;
>  
>  	trace_xfs_attr_node_get(args);
>  
> -	state = xfs_da_state_alloc();
> -	state->args = args;
> -	state->mp = args->dp->i_mount;
> -
>  	/*
>  	 * Search to see if name exists, and get back a pointer to it.
>  	 */
> -	error = xfs_da3_node_lookup_int(state, &retval);
> -	if (error) {
> -		retval = error;
> -		goto out_release;
> -	}
> -	if (retval != -EEXIST)
> +	error = xfs_attr_node_hasname(args, &state);
> +	if (error != -EEXIST)
>  		goto out_release;
>  
>  	/*
>  	 * Get the value, local or "remote"
>  	 */
>  	blk = &state->path.blk[state->path.active - 1];
> -	retval = xfs_attr3_leaf_getvalue(blk->bp, args);
> +	error = xfs_attr3_leaf_getvalue(blk->bp, args);
>  
>  	/*
>  	 * If not in a transaction, we have to release all the buffers.
> @@ -1237,8 +1295,9 @@ xfs_attr_node_get(xfs_da_args_t *args)
>  		state->path.blk[i].bp = NULL;
>  	}
>  
> -	xfs_da_state_free(state);
> -	return retval;
> +	if (state)
> +		xfs_da_state_free(state);
> +	return error;
>  }
>  
>  /* Returns true if the attribute entry name is valid. */
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 0d2d059..66575b8 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -89,6 +89,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>  int xfs_attr_get(struct xfs_da_args *args);
>  int xfs_attr_set(struct xfs_da_args *args);
>  int xfs_attr_set_args(struct xfs_da_args *args);
> +int xfs_has_attr(struct xfs_da_args *args);
>  int xfs_attr_remove_args(struct xfs_da_args *args);
>  bool xfs_attr_namecheck(const void *name, size_t length);
>  
> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> index 863444e..9f39e7a 100644
> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> @@ -664,18 +664,63 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
>  }
>  
>  /*
> + * Return -EEXIST if attr is found, or -ENOATTR if not
> + * args:  args containing attribute name and namelen
> + * sfep:  If not null, pointer will be set to the last attr entry found on
> +	  -EEXIST.  On -ENOATTR pointer is left at the last entry in the list
> + * basep: If not null, pointer is set to the byte offset of the entry in the
> + *	  list on -EEXIST.  On -ENOATTR, pointer is left at the byte offset of
> + *	  the last entry in the list
> + */
> +int
> +xfs_attr_sf_findname(
> +	struct xfs_da_args	 *args,
> +	struct xfs_attr_sf_entry **sfep,
> +	unsigned int		 *basep)
> +{
> +	struct xfs_attr_shortform *sf;
> +	struct xfs_attr_sf_entry *sfe;
> +	unsigned int		base = sizeof(struct xfs_attr_sf_hdr);
> +	int			size = 0;
> +	int			end;
> +	int			i;
> +
> +	sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data;
> +	sfe = &sf->list[0];
> +	end = sf->hdr.count;
> +	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
> +			     base += size, i++) {
> +		size = XFS_ATTR_SF_ENTSIZE(sfe);
> +		if (!xfs_attr_match(args, sfe->namelen, sfe->nameval,
> +				    sfe->flags))
> +			continue;
> +		break;
> +	}
> +
> +	if (sfep != NULL)
> +		*sfep = sfe;
> +
> +	if (basep != NULL)
> +		*basep = base;
> +
> +	if (i == end)
> +		return -ENOATTR;
> +	return -EEXIST;
> +}
> +
> +/*
>   * Add a name/value pair to the shortform attribute list.
>   * Overflow from the inode has already been checked for.
>   */
>  void
> -xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
> +xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff)
>  {
> -	xfs_attr_shortform_t *sf;
> -	xfs_attr_sf_entry_t *sfe;
> -	int i, offset, size;
> -	xfs_mount_t *mp;
> -	xfs_inode_t *dp;
> -	struct xfs_ifork *ifp;
> +	struct xfs_attr_shortform	*sf;
> +	struct xfs_attr_sf_entry	*sfe;
> +	int				offset, size, error;
> +	struct xfs_mount		*mp;
> +	struct xfs_inode		*dp;
> +	struct xfs_ifork		*ifp;
>  
>  	trace_xfs_attr_sf_add(args);
>  
> @@ -686,11 +731,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
>  	ifp = dp->i_afp;
>  	ASSERT(ifp->if_flags & XFS_IFINLINE);
>  	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
> -	sfe = &sf->list[0];
> -	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
> -		ASSERT(!xfs_attr_match(args, sfe->namelen, sfe->nameval,
> -			sfe->flags));
> -	}
> +	error = xfs_attr_sf_findname(args, &sfe, NULL);
> +	ASSERT(error != -EEXIST);
>  
>  	offset = (char *)sfe - (char *)sf;
>  	size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
> @@ -733,31 +775,26 @@ xfs_attr_fork_remove(
>   * Remove an attribute from the shortform attribute list structure.
>   */
>  int
> -xfs_attr_shortform_remove(xfs_da_args_t *args)
> +xfs_attr_shortform_remove(struct xfs_da_args *args)
>  {
> -	xfs_attr_shortform_t *sf;
> -	xfs_attr_sf_entry_t *sfe;
> -	int base, size=0, end, totsize, i;
> -	xfs_mount_t *mp;
> -	xfs_inode_t *dp;
> +	struct xfs_attr_shortform	*sf;
> +	struct xfs_attr_sf_entry	*sfe;
> +	int				size = 0, end, totsize;
> +	unsigned int			base;
> +	struct xfs_mount		*mp;
> +	struct xfs_inode		*dp;
> +	int				error;
>  
>  	trace_xfs_attr_sf_remove(args);
>  
>  	dp = args->dp;
>  	mp = dp->i_mount;
> -	base = sizeof(xfs_attr_sf_hdr_t);
>  	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
> -	sfe = &sf->list[0];
> -	end = sf->hdr.count;
> -	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
> -					base += size, i++) {
> -		size = XFS_ATTR_SF_ENTSIZE(sfe);
> -		if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
> -				sfe->flags))
> -			break;
> -	}
> -	if (i == end)
> -		return -ENOATTR;
> +
> +	error = xfs_attr_sf_findname(args, &sfe, &base);
> +	if (error != -EEXIST)
> +		return error;
> +	size = XFS_ATTR_SF_ENTSIZE(sfe);
>  
>  	/*
>  	 * Fix up the attribute fork data, covering the hole
> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h
> index 6dd2d93..88ec042 100644
> --- a/fs/xfs/libxfs/xfs_attr_leaf.h
> +++ b/fs/xfs/libxfs/xfs_attr_leaf.h
> @@ -52,6 +52,9 @@ int	xfs_attr_shortform_getvalue(struct xfs_da_args *args);
>  int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
>  			struct xfs_buf **leaf_bp);
>  int	xfs_attr_shortform_remove(struct xfs_da_args *args);
> +int	xfs_attr_sf_findname(struct xfs_da_args *args,
> +			     struct xfs_attr_sf_entry **sfep,
> +			     unsigned int *basep);
>  int	xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
>  int	xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
>  xfs_failaddr_t xfs_attr_shortform_verify(struct xfs_inode *ip);
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST
  2020-04-03 22:12 ` [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST Allison Collins
@ 2020-04-06 14:31   ` Brian Foster
  2020-04-06 23:14     ` Allison Collins
  2020-04-15  6:43   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-06 14:31 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:11PM -0700, Allison Collins wrote:
> Delayed operations cannot return error codes.  So we must check for
> these conditions first before starting set or remove operations
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 2a0d3d3..f7e289e 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -404,6 +404,17 @@ xfs_attr_set(
>  				args->total, 0, quota_flags);
>  		if (error)
>  			goto out_trans_cancel;
> +
> +		error = xfs_has_attr(args);
> +		if (error == -EEXIST && (args->attr_flags & XATTR_CREATE))
> +			goto out_trans_cancel;
> +
> +		if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
> +			goto out_trans_cancel;
> +
> +		if (error != -ENOATTR && error != -EEXIST)
> +			goto out_trans_cancel;

I'd kill off the whitespace between the above error checks. Otherwise
looks good to me:

Reviewed-by: Brian Foster <bfoster@redhat.com>

> +
>  		error = xfs_attr_set_args(args);
>  		if (error)
>  			goto out_trans_cancel;
> @@ -411,6 +422,10 @@ xfs_attr_set(
>  		if (!args->trans)
>  			goto out_unlock;
>  	} else {
> +		error = xfs_has_attr(args);
> +		if (error != -EEXIST)
> +			goto out_trans_cancel;
> +
>  		error = xfs_attr_remove_args(args);
>  		if (error)
>  			goto out_trans_cancel;
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines
  2020-04-06 14:31   ` Brian Foster
@ 2020-04-06 22:09     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-06 22:09 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/6/20 7:31 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:10PM -0700, Allison Collins wrote:
>> This patch adds a new functions to check for the existence of an
>> attribute. Subroutines are also added to handle the cases of leaf
>> blocks, nodes or shortform. Common code that appears in existing attr
>> add and remove functions have been factored out to help reduce the
>> appearance of duplicated code.  We will need these routines later for
>> delayed attributes since delayed operations cannot return error codes.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c      | 181 ++++++++++++++++++++++++++++--------------
>>   fs/xfs/libxfs/xfs_attr.h      |   1 +
>>   fs/xfs/libxfs/xfs_attr_leaf.c |  97 +++++++++++++++-------
>>   fs/xfs/libxfs/xfs_attr_leaf.h |   3 +
>>   4 files changed, 191 insertions(+), 91 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index e4fe3dc..2a0d3d3 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -703,21 +749,57 @@ xfs_attr_leaf_get(xfs_da_args_t *args)
>>   
>>   	trace_xfs_attr_leaf_get(args);
>>   
>> -	args->blkno = 0;
>> -	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp);
>> -	if (error)
>> -		return error;
>> +	error = xfs_attr_leaf_hasname(args, &bp);
>>   
>> -	error = xfs_attr3_leaf_lookup_int(bp, args);
>>   	if (error != -EEXIST)  {
>>   		xfs_trans_brelse(args->trans, bp);
>>   		return error;
>> -	}
>> +	} else if (error != -EEXIST)
>> +		return error;
>> +
> 
> The if/else above looks busted (the same check for both branches).
Sorry, that one should have been ENOATTR for this branch.  Will fix.

> 
>> +
>>   	error = xfs_attr3_leaf_getvalue(bp, args);
>>   	xfs_trans_brelse(args->trans, bp);
>>   	return error;
>>   }
>>   
>> +/*
>> + * Return EEXIST if attr is found, or ENOATTR if not
>> + * statep: If not null is set to point at the found state.  Caller will
>> + *         be responsible for freeing the state in this case.
>> + */
>> +STATIC int
>> +xfs_attr_node_hasname(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	**statep)
>> +{
>> +	struct xfs_da_state	*state;
>> +	int			retval, error;
>> +
>> +	state = xfs_da_state_alloc();
>> +	state->args = args;
>> +	state->mp = args->dp->i_mount;
>> +
>> +	if (statep != NULL)
>> +		*statep = NULL;
>> +
>> +	/*
>> +	 * Search to see if name exists, and get back a pointer to it.
>> +	 */
>> +	error = xfs_da3_node_lookup_int(state, &retval);
>> +	if (error == 0) {
>> +		if (statep != NULL)
>> +			*statep = state;
>> +		else
>> +			xfs_da_state_free(state);
>> +		return retval;
>> +	}
>> +
>> +	xfs_da_state_free(state);
>> +
> 
> Inverting the logic is a bit more clear IMO:
> 
> 	...
> 	error = xfs_da3_node_lookup_int(state, &retval);
> 	if (error) {
> 		...
> 		return error;
> 	}
> 
> 	if (statep)
> 		...
> 	else
> 		...
> 	return retval;
> }
> 
Sure, will flip.  Thanks for the reviews!

Allison

> With those things addressed:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> 
>> +	return error;
>> +}
>> +
>>   /*========================================================================
>>    * External routines when attribute list size > geo->blksize
>>    *========================================================================*/
>> @@ -750,17 +832,14 @@ xfs_attr_node_addname(
>>   	dp = args->dp;
>>   	mp = dp->i_mount;
>>   restart:
>> -	state = xfs_da_state_alloc();
>> -	state->args = args;
>> -	state->mp = mp;
>> -
>>   	/*
>>   	 * Search to see if name already exists, and get back a pointer
>>   	 * to where it should go.
>>   	 */
>> -	error = xfs_da3_node_lookup_int(state, &retval);
>> -	if (error)
>> +	retval = xfs_attr_node_hasname(args, &state);
>> +	if (retval != -ENOATTR && retval != -EEXIST)
>>   		goto out;
>> +
>>   	blk = &state->path.blk[ state->path.active-1 ];
>>   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>> @@ -965,29 +1044,15 @@ xfs_attr_node_removename(
>>   {
>>   	struct xfs_da_state	*state;
>>   	struct xfs_da_state_blk	*blk;
>> -	struct xfs_inode	*dp;
>>   	struct xfs_buf		*bp;
>>   	int			retval, error, forkoff;
>> +	struct xfs_inode	*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>>   
>> -	/*
>> -	 * Tie a string around our finger to remind us where we are.
>> -	 */
>> -	dp = args->dp;
>> -	state = xfs_da_state_alloc();
>> -	state->args = args;
>> -	state->mp = dp->i_mount;
>> -
>> -	/*
>> -	 * Search to see if name exists, and get back a pointer to it.
>> -	 */
>> -	error = xfs_da3_node_lookup_int(state, &retval);
>> -	if (error || (retval != -EEXIST)) {
>> -		if (error == 0)
>> -			error = retval;
>> +	error = xfs_attr_node_hasname(args, &state);
>> +	if (error != -EEXIST)
>>   		goto out;
>> -	}
>>   
>>   	/*
>>   	 * If there is an out-of-line value, de-allocate the blocks.
>> @@ -1082,7 +1147,8 @@ xfs_attr_node_removename(
>>   	error = 0;
>>   
>>   out:
>> -	xfs_da_state_free(state);
>> +	if (state)
>> +		xfs_da_state_free(state);
>>   	return error;
>>   }
>>   
>> @@ -1202,31 +1268,23 @@ xfs_attr_node_get(xfs_da_args_t *args)
>>   {
>>   	xfs_da_state_t *state;
>>   	xfs_da_state_blk_t *blk;
>> -	int error, retval;
>> +	int error;
>>   	int i;
>>   
>>   	trace_xfs_attr_node_get(args);
>>   
>> -	state = xfs_da_state_alloc();
>> -	state->args = args;
>> -	state->mp = args->dp->i_mount;
>> -
>>   	/*
>>   	 * Search to see if name exists, and get back a pointer to it.
>>   	 */
>> -	error = xfs_da3_node_lookup_int(state, &retval);
>> -	if (error) {
>> -		retval = error;
>> -		goto out_release;
>> -	}
>> -	if (retval != -EEXIST)
>> +	error = xfs_attr_node_hasname(args, &state);
>> +	if (error != -EEXIST)
>>   		goto out_release;
>>   
>>   	/*
>>   	 * Get the value, local or "remote"
>>   	 */
>>   	blk = &state->path.blk[state->path.active - 1];
>> -	retval = xfs_attr3_leaf_getvalue(blk->bp, args);
>> +	error = xfs_attr3_leaf_getvalue(blk->bp, args);
>>   
>>   	/*
>>   	 * If not in a transaction, we have to release all the buffers.
>> @@ -1237,8 +1295,9 @@ xfs_attr_node_get(xfs_da_args_t *args)
>>   		state->path.blk[i].bp = NULL;
>>   	}
>>   
>> -	xfs_da_state_free(state);
>> -	return retval;
>> +	if (state)
>> +		xfs_da_state_free(state);
>> +	return error;
>>   }
>>   
>>   /* Returns true if the attribute entry name is valid. */
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 0d2d059..66575b8 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -89,6 +89,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>>   int xfs_attr_get(struct xfs_da_args *args);
>>   int xfs_attr_set(struct xfs_da_args *args);
>>   int xfs_attr_set_args(struct xfs_da_args *args);
>> +int xfs_has_attr(struct xfs_da_args *args);
>>   int xfs_attr_remove_args(struct xfs_da_args *args);
>>   bool xfs_attr_namecheck(const void *name, size_t length);
>>   
>> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
>> index 863444e..9f39e7a 100644
>> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
>> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
>> @@ -664,18 +664,63 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
>>   }
>>   
>>   /*
>> + * Return -EEXIST if attr is found, or -ENOATTR if not
>> + * args:  args containing attribute name and namelen
>> + * sfep:  If not null, pointer will be set to the last attr entry found on
>> +	  -EEXIST.  On -ENOATTR pointer is left at the last entry in the list
>> + * basep: If not null, pointer is set to the byte offset of the entry in the
>> + *	  list on -EEXIST.  On -ENOATTR, pointer is left at the byte offset of
>> + *	  the last entry in the list
>> + */
>> +int
>> +xfs_attr_sf_findname(
>> +	struct xfs_da_args	 *args,
>> +	struct xfs_attr_sf_entry **sfep,
>> +	unsigned int		 *basep)
>> +{
>> +	struct xfs_attr_shortform *sf;
>> +	struct xfs_attr_sf_entry *sfe;
>> +	unsigned int		base = sizeof(struct xfs_attr_sf_hdr);
>> +	int			size = 0;
>> +	int			end;
>> +	int			i;
>> +
>> +	sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data;
>> +	sfe = &sf->list[0];
>> +	end = sf->hdr.count;
>> +	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
>> +			     base += size, i++) {
>> +		size = XFS_ATTR_SF_ENTSIZE(sfe);
>> +		if (!xfs_attr_match(args, sfe->namelen, sfe->nameval,
>> +				    sfe->flags))
>> +			continue;
>> +		break;
>> +	}
>> +
>> +	if (sfep != NULL)
>> +		*sfep = sfe;
>> +
>> +	if (basep != NULL)
>> +		*basep = base;
>> +
>> +	if (i == end)
>> +		return -ENOATTR;
>> +	return -EEXIST;
>> +}
>> +
>> +/*
>>    * Add a name/value pair to the shortform attribute list.
>>    * Overflow from the inode has already been checked for.
>>    */
>>   void
>> -xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
>> +xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff)
>>   {
>> -	xfs_attr_shortform_t *sf;
>> -	xfs_attr_sf_entry_t *sfe;
>> -	int i, offset, size;
>> -	xfs_mount_t *mp;
>> -	xfs_inode_t *dp;
>> -	struct xfs_ifork *ifp;
>> +	struct xfs_attr_shortform	*sf;
>> +	struct xfs_attr_sf_entry	*sfe;
>> +	int				offset, size, error;
>> +	struct xfs_mount		*mp;
>> +	struct xfs_inode		*dp;
>> +	struct xfs_ifork		*ifp;
>>   
>>   	trace_xfs_attr_sf_add(args);
>>   
>> @@ -686,11 +731,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
>>   	ifp = dp->i_afp;
>>   	ASSERT(ifp->if_flags & XFS_IFINLINE);
>>   	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
>> -	sfe = &sf->list[0];
>> -	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
>> -		ASSERT(!xfs_attr_match(args, sfe->namelen, sfe->nameval,
>> -			sfe->flags));
>> -	}
>> +	error = xfs_attr_sf_findname(args, &sfe, NULL);
>> +	ASSERT(error != -EEXIST);
>>   
>>   	offset = (char *)sfe - (char *)sf;
>>   	size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
>> @@ -733,31 +775,26 @@ xfs_attr_fork_remove(
>>    * Remove an attribute from the shortform attribute list structure.
>>    */
>>   int
>> -xfs_attr_shortform_remove(xfs_da_args_t *args)
>> +xfs_attr_shortform_remove(struct xfs_da_args *args)
>>   {
>> -	xfs_attr_shortform_t *sf;
>> -	xfs_attr_sf_entry_t *sfe;
>> -	int base, size=0, end, totsize, i;
>> -	xfs_mount_t *mp;
>> -	xfs_inode_t *dp;
>> +	struct xfs_attr_shortform	*sf;
>> +	struct xfs_attr_sf_entry	*sfe;
>> +	int				size = 0, end, totsize;
>> +	unsigned int			base;
>> +	struct xfs_mount		*mp;
>> +	struct xfs_inode		*dp;
>> +	int				error;
>>   
>>   	trace_xfs_attr_sf_remove(args);
>>   
>>   	dp = args->dp;
>>   	mp = dp->i_mount;
>> -	base = sizeof(xfs_attr_sf_hdr_t);
>>   	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
>> -	sfe = &sf->list[0];
>> -	end = sf->hdr.count;
>> -	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
>> -					base += size, i++) {
>> -		size = XFS_ATTR_SF_ENTSIZE(sfe);
>> -		if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
>> -				sfe->flags))
>> -			break;
>> -	}
>> -	if (i == end)
>> -		return -ENOATTR;
>> +
>> +	error = xfs_attr_sf_findname(args, &sfe, &base);
>> +	if (error != -EEXIST)
>> +		return error;
>> +	size = XFS_ATTR_SF_ENTSIZE(sfe);
>>   
>>   	/*
>>   	 * Fix up the attribute fork data, covering the hole
>> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h
>> index 6dd2d93..88ec042 100644
>> --- a/fs/xfs/libxfs/xfs_attr_leaf.h
>> +++ b/fs/xfs/libxfs/xfs_attr_leaf.h
>> @@ -52,6 +52,9 @@ int	xfs_attr_shortform_getvalue(struct xfs_da_args *args);
>>   int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
>>   			struct xfs_buf **leaf_bp);
>>   int	xfs_attr_shortform_remove(struct xfs_da_args *args);
>> +int	xfs_attr_sf_findname(struct xfs_da_args *args,
>> +			     struct xfs_attr_sf_entry **sfep,
>> +			     unsigned int *basep);
>>   int	xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
>>   int	xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
>>   xfs_failaddr_t xfs_attr_shortform_verify(struct xfs_inode *ip);
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST
  2020-04-06 14:31   ` Brian Foster
@ 2020-04-06 23:14     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-06 23:14 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/6/20 7:31 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:11PM -0700, Allison Collins wrote:
>> Delayed operations cannot return error codes.  So we must check for
>> these conditions first before starting set or remove operations
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 15 +++++++++++++++
>>   1 file changed, 15 insertions(+)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 2a0d3d3..f7e289e 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -404,6 +404,17 @@ xfs_attr_set(
>>   				args->total, 0, quota_flags);
>>   		if (error)
>>   			goto out_trans_cancel;
>> +
>> +		error = xfs_has_attr(args);
>> +		if (error == -EEXIST && (args->attr_flags & XATTR_CREATE))
>> +			goto out_trans_cancel;
>> +
>> +		if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>> +			goto out_trans_cancel;
>> +
>> +		if (error != -ENOATTR && error != -EEXIST)
>> +			goto out_trans_cancel;
> 
> I'd kill off the whitespace between the above error checks. Otherwise
> looks good to me:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Alrighty, will do.  Thanks!

Allison
> 
>> +
>>   		error = xfs_attr_set_args(args);
>>   		if (error)
>>   			goto out_trans_cancel;
>> @@ -411,6 +422,10 @@ xfs_attr_set(
>>   		if (!args->trans)
>>   			goto out_unlock;
>>   	} else {
>> +		error = xfs_has_attr(args);
>> +		if (error != -EEXIST)
>> +			goto out_trans_cancel;
>> +
>>   		error = xfs_attr_remove_args(args);
>>   		if (error)
>>   			goto out_trans_cancel;
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove
  2020-04-03 22:12 ` [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove Allison Collins
@ 2020-04-07 14:16   ` Brian Foster
  2020-04-07 22:19     ` Allison Collins
  2020-04-29 23:05   ` Darrick J. Wong
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-07 14:16 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:19PM -0700, Allison Collins wrote:
> This function is similar to xfs_attr_rmtval_remove, but adapted to
> return EAGAIN for new transactions. We will use this later when we
> introduce delayed attributes.  This function will eventually replace
> xfs_attr_rmtval_remove
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
> ---
>  fs/xfs/libxfs/xfs_attr_remote.c | 25 +++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_attr_remote.h |  1 +
>  2 files changed, 26 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index 4d51969..fd4be9d 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -711,3 +711,28 @@ xfs_attr_rmtval_remove(
>  	}
>  	return 0;
>  }
> +
> +/*
> + * Remove the value associated with an attribute by deleting the out-of-line
> + * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
> + * transaction and recall the function
> + */
> +int
> +__xfs_attr_rmtval_remove(
> +	struct xfs_da_args	*args)
> +{
> +	int	error, done;
> +
> +	/*
> +	 * Unmap value blocks for this attr.
> +	 */
> +	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
> +			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
> +	if (error)
> +		return error;
> +
> +	if (!done)
> +		return -EAGAIN;
> +
> +	return 0;
> +}

We should let xfs_attr_rmtval_remove() call this function and do the
roll based on -EAGAIN, then eliminate the higher level function later if
it becomes unused.

Brian

> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> index eff5f95..ee3337b 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.h
> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> @@ -14,4 +14,5 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args);
>  int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
>  		xfs_buf_flags_t incore_flags);
>  int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
> +int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
>  #endif /* __XFS_ATTR_REMOTE_H__ */
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink
  2020-04-03 22:12 ` [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink Allison Collins
@ 2020-04-07 14:17   ` Brian Foster
  2020-04-07 21:52     ` Allison Collins
  2020-04-15 10:16   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-07 14:17 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:20PM -0700, Allison Collins wrote:
> This patch adds a new helper function xfs_attr_node_shrink used to
> shrink an attr name into an inode if it is small enough.  This helps to
> modularize the greater calling function xfs_attr_node_removename.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 67 ++++++++++++++++++++++++++++++------------------
>  1 file changed, 42 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index db5a99c..27a9bb5 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -1197,31 +1235,10 @@ xfs_attr_node_removename(
>  	/*
>  	 * If the result is small enough, push it all into the inode.
>  	 */
> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		/*
> -		 * Have to get rid of the copy of this dabuf in the state.
> -		 */
> -		ASSERT(state->path.active == 1);
> -		ASSERT(state->path.blk[0].bp);
> -		state->path.blk[0].bp = NULL;
> -
> -		error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
> -		if (error)
> -			goto out;
> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> +		error = xfs_attr_node_shrink(args, state);
>  
> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
> -			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
> -			/* bp is gone due to xfs_da_shrink_inode */
> -			if (error)
> -				goto out;
> -			error = xfs_defer_finish(&args->trans);
> -			if (error)
> -				goto out;
> -		} else
> -			xfs_trans_brelse(args->trans, bp);
> -	}
>  	error = 0;

Looks like the error handling is busted here..? We used to goto out, now
we always reset error to 0.

Brian

> -
>  out:
>  	if (state)
>  		xfs_da_state_free(state);
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls
  2020-04-03 22:12 ` [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls Allison Collins
@ 2020-04-07 14:17   ` Brian Foster
  2020-04-07 21:53     ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-07 14:17 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:21PM -0700, Allison Collins wrote:
> Some calls to xfs_trans_roll_inode in the *_addname routines are not
> needed. If they are the last operations executed in these functions, and
> no further changes are made, then higher level routines will roll or
> commit the tranactions. The xfs_trans_roll in _removename is also not
> needed because invalidating blocks is not an incore change.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 30 ------------------------------
>  1 file changed, 30 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 27a9bb5..4225a94 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -700,11 +700,6 @@ xfs_attr_leaf_addname(
>  				return error;
>  		}
>  
> -		/*
> -		 * Commit the remove and start the next trans in series.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -

Ok, so it looks like the only caller immediately rolls again.

>  	} else if (args->rmtblkno > 0) {
>  		/*
>  		 * Added a "remote" value, just clear the incomplete flag.
> @@ -712,12 +707,6 @@ xfs_attr_leaf_addname(
>  		error = xfs_attr3_leaf_clearflag(args);
>  		if (error)
>  			return error;
> -
> -		/*
> -		 * Commit the flag value change and start the next trans in
> -		 * series.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>  	}

Same logic applies here. Makes sense.

>  	return error;
>  }
> @@ -1069,13 +1058,6 @@ xfs_attr_node_addname(
>  				goto out;
>  		}
>  
> -		/*
> -		 * Commit and start the next trans in the chain.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> -			goto out;
> -

From here, we log the inode and commit, so that seems safe.

BTW, doesn't that mean the defer_finish() just above (after the
da3_join()) is unnecessary as well?

>  	} else if (args->rmtblkno > 0) {
>  		/*
>  		 * Added a "remote" value, just clear the incomplete flag.
> @@ -1083,14 +1065,6 @@ xfs_attr_node_addname(
>  		error = xfs_attr3_leaf_clearflag(args);
>  		if (error)
>  			goto out;
> -
> -		 /*
> -		  * Commit the flag value change and start the next trans in
> -		  * series.
> -		  */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -		if (error)
> -			goto out;
>  	}
>  	retval = error = 0;
>  
> @@ -1189,10 +1163,6 @@ xfs_attr_node_removename(
>  		if (error)
>  			goto out;
>  
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -		if (error)
> -			goto out;
> -

Hmm, not sure I follow this one. Don't we want to commit the incomplete
flag before proceeding? Or are we just saying it can be combined with
the first bunmap since that's going to roll anyways..?

BTW, the commit log refers to the invalidation as "not incore," which I
think is opposite. :P xfs_attr_rmtval_invalidate() is incore-only in the
sense that it doesn't seem to use the transaction. Is that what you
mean?

Brian

>  		error = xfs_attr_rmtval_remove(args);
>  		if (error)
>  			goto out;
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-03 22:12 ` [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform Allison Collins
@ 2020-04-07 15:23   ` Brian Foster
  2020-04-07 21:53     ` Allison Collins
  2020-04-20  5:30   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-07 15:23 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:22PM -0700, Allison Collins wrote:
> In this patch, we hoist code from xfs_attr_set_args into two new helpers
> xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
> to simplify xfs_attr_set_args when we get into delayed attrs later.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 107 +++++++++++++++++++++++++++++++----------------
>  1 file changed, 72 insertions(+), 35 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4225a94..ba26ffe 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
>  }
>  
>  /*
> + * Check to see if the attr should be upgraded from non-existent or shortform to
> + * single-leaf-block attribute list.
> + */
> +static inline bool
> +xfs_attr_is_shortform(
> +	struct xfs_inode    *ip)
> +{
> +	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> +	      (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> +	      ip->i_d.di_anextents == 0);

Logic should be indented similar to the original:

	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
	       (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
		ip->i_d.di_anextents == 0);

Otherwise looks good:

Reviewed-by: Brian Foster <bfoster@redhat.com>

> +}
> +
> +/*
> + * Attempts to set an attr in shortform, or converts the tree to leaf form if
> + * there is not enough room.  If the attr is set, the transaction is committed
> + * and set to NULL.
> + */
> +STATIC int
> +xfs_attr_set_shortform(
> +	struct xfs_da_args	*args,
> +	struct xfs_buf		**leaf_bp)
> +{
> +	struct xfs_inode	*dp = args->dp;
> +	int			error, error2 = 0;
> +
> +	/*
> +	 * Try to add the attr to the attribute list in the inode.
> +	 */
> +	error = xfs_attr_try_sf_addname(dp, args);
> +	if (error != -ENOSPC) {
> +		error2 = xfs_trans_commit(args->trans);
> +		args->trans = NULL;
> +		return error ? error : error2;
> +	}
> +	/*
> +	 * It won't fit in the shortform, transform to a leaf block.  GROT:
> +	 * another possible req'mt for a double-split btree op.
> +	 */
> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
> +	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. Once we're done rolling the transaction we
> +	 * can release the hold and add the attr to the leaf.
> +	 */
> +	xfs_trans_bhold(args->trans, *leaf_bp);
> +	error = xfs_defer_finish(&args->trans);
> +	xfs_trans_bhold_release(args->trans, *leaf_bp);
> +	if (error) {
> +		xfs_trans_brelse(args->trans, *leaf_bp);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
>   * Set the attribute specified in @args.
>   */
>  int
> @@ -212,48 +272,25 @@ xfs_attr_set_args(
>  {
>  	struct xfs_inode	*dp = args->dp;
>  	struct xfs_buf          *leaf_bp = NULL;
> -	int			error, error2 = 0;
> +	int			error = 0;
>  
>  	/*
> -	 * If the attribute list is non-existent or a shortform list,
> -	 * upgrade it to a single-leaf-block attribute list.
> +	 * 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.
>  	 */
> -	if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> -	    (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> -	     dp->i_d.di_anextents == 0)) {
> -
> -		/*
> -		 * Try to add the attr to the attribute list in the inode.
> -		 */
> -		error = xfs_attr_try_sf_addname(dp, args);
> -		if (error != -ENOSPC) {
> -			error2 = xfs_trans_commit(args->trans);
> -			args->trans = NULL;
> -			return error ? error : error2;
> -		}
> -
> -		/*
> -		 * It won't fit in the shortform, transform to a leaf block.
> -		 * GROT: another possible req'mt for a double-split btree op.
> -		 */
> -		error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
> -		if (error)
> -			return error;
> +	if (xfs_attr_is_shortform(dp)) {
>  
>  		/*
> -		 * 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.
> -		 * Once we're done rolling the transaction we can release
> -		 * the hold and add the attr to the leaf.
> +		 * If the attr was successfully set in shortform, the
> +		 * transaction is committed and set to NULL.  Otherwise, is it
> +		 * converted from shortform to leaf, and the transaction is
> +		 * retained.
>  		 */
> -		xfs_trans_bhold(args->trans, leaf_bp);
> -		error = xfs_defer_finish(&args->trans);
> -		xfs_trans_bhold_release(args->trans, leaf_bp);
> -		if (error) {
> -			xfs_trans_brelse(args->trans, leaf_bp);
> +		error = xfs_attr_set_shortform(args, &leaf_bp);
> +		if (error || !args->trans)
>  			return error;
> -		}
>  	}
>  
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete
  2020-04-03 22:12 ` [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete Allison Collins
@ 2020-04-07 15:23   ` Brian Foster
  2020-04-07 21:53     ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-07 15:23 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:23PM -0700, Allison Collins wrote:
> This patch helps to simplify xfs_attr_node_removename by modularizing
> the code around the transactions into helper functions.  This will make
> the function easier to follow when we introduce delayed attributes.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Amir Goldstein <amir73il@gmail.com>
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 45 +++++++++++++++++++++++++++++++--------------
>  1 file changed, 31 insertions(+), 14 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index ba26ffe..8d7a5db 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1153,6 +1153,36 @@ xfs_attr_node_shrink(
>  }
>  
>  /*
> + * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers
> + * for later deletion of the entry.
> + */
> +STATIC int
> +xfs_attr_leaf_mark_incomplete(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	*state)
> +{
> +	int error;

	int			error;

> +
> +	/*
> +	 * Fill in disk block numbers in the state structure
> +	 * so that we can get the buffers back after we commit
> +	 * several transactions in the following calls.
> +	 */
> +	error = xfs_attr_fillstate(state);
> +	if (error)
> +		return error;
> +
> +	/*
> +	 * Mark the attribute as INCOMPLETE
> +	 */
> +	error = xfs_attr3_leaf_setflag(args);
> +	if (error)
> +		return error;
> +
> +	return 0;

	return xfs_attr3_leaf_setflag(args);

Otherwise looks good:

Reviewed-by: Brian Foster <bfoster@redhat.com>

> +}
> +
> +/*
>   * Remove a name from a B-tree attribute list.
>   *
>   * This will involve walking down the Btree, and may involve joining
> @@ -1183,20 +1213,7 @@ xfs_attr_node_removename(
>  	ASSERT(blk->bp != NULL);
>  	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  	if (args->rmtblkno > 0) {
> -		/*
> -		 * Fill in disk block numbers in the state structure
> -		 * so that we can get the buffers back after we commit
> -		 * several transactions in the following calls.
> -		 */
> -		error = xfs_attr_fillstate(state);
> -		if (error)
> -			goto out;
> -
> -		/*
> -		 * Mark the attribute as INCOMPLETE, then bunmapi() the
> -		 * remote value.
> -		 */
> -		error = xfs_attr3_leaf_setflag(args);
> +		error = xfs_attr_leaf_mark_incomplete(args, state);
>  		if (error)
>  			goto out;
>  
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink
  2020-04-07 14:17   ` Brian Foster
@ 2020-04-07 21:52     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-07 21:52 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On 4/7/20 7:17 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:20PM -0700, Allison Collins wrote:
>> This patch adds a new helper function xfs_attr_node_shrink used to
>> shrink an attr name into an inode if it is small enough.  This helps to
>> modularize the greater calling function xfs_attr_node_removename.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 67 ++++++++++++++++++++++++++++++------------------
>>   1 file changed, 42 insertions(+), 25 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index db5a99c..27a9bb5 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -1197,31 +1235,10 @@ xfs_attr_node_removename(
>>   	/*
>>   	 * If the result is small enough, push it all into the inode.
>>   	 */
>> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -		/*
>> -		 * Have to get rid of the copy of this dabuf in the state.
>> -		 */
>> -		ASSERT(state->path.active == 1);
>> -		ASSERT(state->path.blk[0].bp);
>> -		state->path.blk[0].bp = NULL;
>> -
>> -		error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
>> -		if (error)
>> -			goto out;
>> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>> +		error = xfs_attr_node_shrink(args, state);
>>   
>> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
>> -			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
>> -			/* bp is gone due to xfs_da_shrink_inode */
>> -			if (error)
>> -				goto out;
>> -			error = xfs_defer_finish(&args->trans);
>> -			if (error)
>> -				goto out;
>> -		} else
>> -			xfs_trans_brelse(args->trans, bp);
>> -	}
>>   	error = 0;
> 
> Looks like the error handling is busted here..? We used to goto out, now
> we always reset error to 0.
> 
> Brian
Ah, yes the error = 0; should have been removed along with the rest of 
it.  Will remove. Thanks!

Allison

> 
>> -
>>   out:
>>   	if (state)
>>   		xfs_da_state_free(state);
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls
  2020-04-07 14:17   ` Brian Foster
@ 2020-04-07 21:53     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-07 21:53 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/7/20 7:17 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:21PM -0700, Allison Collins wrote:
>> Some calls to xfs_trans_roll_inode in the *_addname routines are not
>> needed. If they are the last operations executed in these functions, and
>> no further changes are made, then higher level routines will roll or
>> commit the tranactions. The xfs_trans_roll in _removename is also not
>> needed because invalidating blocks is not an incore change.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 30 ------------------------------
>>   1 file changed, 30 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 27a9bb5..4225a94 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -700,11 +700,6 @@ xfs_attr_leaf_addname(
>>   				return error;
>>   		}
>>   
>> -		/*
>> -		 * Commit the remove and start the next trans in series.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -
> 
> Ok, so it looks like the only caller immediately rolls again.
> 
>>   	} else if (args->rmtblkno > 0) {
>>   		/*
>>   		 * Added a "remote" value, just clear the incomplete flag.
>> @@ -712,12 +707,6 @@ xfs_attr_leaf_addname(
>>   		error = xfs_attr3_leaf_clearflag(args);
>>   		if (error)
>>   			return error;
>> -
>> -		/*
>> -		 * Commit the flag value change and start the next trans in
>> -		 * series.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>   	}
> 
> Same logic applies here. Makes sense.
> 
>>   	return error;
>>   }
>> @@ -1069,13 +1058,6 @@ xfs_attr_node_addname(
>>   				goto out;
>>   		}
>>   
>> -		/*
>> -		 * Commit and start the next trans in the chain.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -		if (error)
>> -			goto out;
>> -
> 
>  From here, we log the inode and commit, so that seems safe.
> 
> BTW, doesn't that mean the defer_finish() just above (after the
> da3_join()) is unnecessary as well?
Hmm, I think you're right.  Will clean out here too.

> 
>>   	} else if (args->rmtblkno > 0) {
>>   		/*
>>   		 * Added a "remote" value, just clear the incomplete flag.
>> @@ -1083,14 +1065,6 @@ xfs_attr_node_addname(
>>   		error = xfs_attr3_leaf_clearflag(args);
>>   		if (error)
>>   			goto out;
>> -
>> -		 /*
>> -		  * Commit the flag value change and start the next trans in
>> -		  * series.
>> -		  */
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> -		if (error)
>> -			goto out;
>>   	}
>>   	retval = error = 0;
>>   
>> @@ -1189,10 +1163,6 @@ xfs_attr_node_removename(
>>   		if (error)
>>   			goto out;
>>   
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> -		if (error)
>> -			goto out;
>> -
> 
> Hmm, not sure I follow this one. Don't we want to commit the incomplete
> flag before proceeding? Or are we just saying it can be combined with
> the first bunmap since that's going to roll anyways..?
> 
> BTW, the commit log refers to the invalidation as "not incore," which I
> think is opposite. :P xfs_attr_rmtval_invalidate() is incore-only in the
> sense that it doesn't seem to use the transaction. Is that what you
> mean?
Yes, sorry, the commit should say in-core only.  We pulled this one out 
in v5 for that reason.  It used to belong to the later "Add delay ready 
attr remove routines" patch in v6 and v7. Then when we decided to clean 
out these extra transaction rolls, because it simplifies the top level 
transaction rolling.  So I decided to do the removename clean out in 
this patch because it seemed appropriate.  In any case, will amend the 
commit message.  Discussion thread just for brain refresh:

https://lore.kernel.org/linux-xfs/27f6d1f2-8d7e-c759-31e1-6c4ac8c7ccad@oracle.com/

Thanks for the reviews!  Will update!

Allison


> 
> Brian
> 
>>   		error = xfs_attr_rmtval_remove(args);
>>   		if (error)
>>   			goto out;
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-07 15:23   ` Brian Foster
@ 2020-04-07 21:53     ` Allison Collins
  2020-04-10 16:55       ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-07 21:53 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/7/20 8:23 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:22PM -0700, Allison Collins wrote:
>> In this patch, we hoist code from xfs_attr_set_args into two new helpers
>> xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
>> to simplify xfs_attr_set_args when we get into delayed attrs later.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 107 +++++++++++++++++++++++++++++++----------------
>>   1 file changed, 72 insertions(+), 35 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4225a94..ba26ffe 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
>>   }
>>   
>>   /*
>> + * Check to see if the attr should be upgraded from non-existent or shortform to
>> + * single-leaf-block attribute list.
>> + */
>> +static inline bool
>> +xfs_attr_is_shortform(
>> +	struct xfs_inode    *ip)
>> +{
>> +	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>> +	      (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>> +	      ip->i_d.di_anextents == 0);
> 
> Logic should be indented similar to the original:
> 
> 	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> 	       (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> 		ip->i_d.di_anextents == 0);
> 
> Otherwise looks good:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Alrighty, will fix.  Thanks!

Allison

> 
>> +}
>> +
>> +/*
>> + * Attempts to set an attr in shortform, or converts the tree to leaf form if
>> + * there is not enough room.  If the attr is set, the transaction is committed
>> + * and set to NULL.
>> + */
>> +STATIC int
>> +xfs_attr_set_shortform(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_buf		**leaf_bp)
>> +{
>> +	struct xfs_inode	*dp = args->dp;
>> +	int			error, error2 = 0;
>> +
>> +	/*
>> +	 * Try to add the attr to the attribute list in the inode.
>> +	 */
>> +	error = xfs_attr_try_sf_addname(dp, args);
>> +	if (error != -ENOSPC) {
>> +		error2 = xfs_trans_commit(args->trans);
>> +		args->trans = NULL;
>> +		return error ? error : error2;
>> +	}
>> +	/*
>> +	 * It won't fit in the shortform, transform to a leaf block.  GROT:
>> +	 * another possible req'mt for a double-split btree op.
>> +	 */
>> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
>> +	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. Once we're done rolling the transaction we
>> +	 * can release the hold and add the attr to the leaf.
>> +	 */
>> +	xfs_trans_bhold(args->trans, *leaf_bp);
>> +	error = xfs_defer_finish(&args->trans);
>> +	xfs_trans_bhold_release(args->trans, *leaf_bp);
>> +	if (error) {
>> +		xfs_trans_brelse(args->trans, *leaf_bp);
>> +		return error;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>>    * Set the attribute specified in @args.
>>    */
>>   int
>> @@ -212,48 +272,25 @@ xfs_attr_set_args(
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>>   	struct xfs_buf          *leaf_bp = NULL;
>> -	int			error, error2 = 0;
>> +	int			error = 0;
>>   
>>   	/*
>> -	 * If the attribute list is non-existent or a shortform list,
>> -	 * upgrade it to a single-leaf-block attribute list.
>> +	 * 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.
>>   	 */
>> -	if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>> -	    (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>> -	     dp->i_d.di_anextents == 0)) {
>> -
>> -		/*
>> -		 * Try to add the attr to the attribute list in the inode.
>> -		 */
>> -		error = xfs_attr_try_sf_addname(dp, args);
>> -		if (error != -ENOSPC) {
>> -			error2 = xfs_trans_commit(args->trans);
>> -			args->trans = NULL;
>> -			return error ? error : error2;
>> -		}
>> -
>> -		/*
>> -		 * It won't fit in the shortform, transform to a leaf block.
>> -		 * GROT: another possible req'mt for a double-split btree op.
>> -		 */
>> -		error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
>> -		if (error)
>> -			return error;
>> +	if (xfs_attr_is_shortform(dp)) {
>>   
>>   		/*
>> -		 * 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.
>> -		 * Once we're done rolling the transaction we can release
>> -		 * the hold and add the attr to the leaf.
>> +		 * If the attr was successfully set in shortform, the
>> +		 * transaction is committed and set to NULL.  Otherwise, is it
>> +		 * converted from shortform to leaf, and the transaction is
>> +		 * retained.
>>   		 */
>> -		xfs_trans_bhold(args->trans, leaf_bp);
>> -		error = xfs_defer_finish(&args->trans);
>> -		xfs_trans_bhold_release(args->trans, leaf_bp);
>> -		if (error) {
>> -			xfs_trans_brelse(args->trans, leaf_bp);
>> +		error = xfs_attr_set_shortform(args, &leaf_bp);
>> +		if (error || !args->trans)
>>   			return error;
>> -		}
>>   	}
>>   
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete
  2020-04-07 15:23   ` Brian Foster
@ 2020-04-07 21:53     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-07 21:53 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/7/20 8:23 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:23PM -0700, Allison Collins wrote:
>> This patch helps to simplify xfs_attr_node_removename by modularizing
>> the code around the transactions into helper functions.  This will make
>> the function easier to follow when we introduce delayed attributes.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> Reviewed-by: Amir Goldstein <amir73il@gmail.com>
>> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 45 +++++++++++++++++++++++++++++++--------------
>>   1 file changed, 31 insertions(+), 14 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index ba26ffe..8d7a5db 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1153,6 +1153,36 @@ xfs_attr_node_shrink(
>>   }
>>   
>>   /*
>> + * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers
>> + * for later deletion of the entry.
>> + */
>> +STATIC int
>> +xfs_attr_leaf_mark_incomplete(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	*state)
>> +{
>> +	int error;
> 
> 	int			error;
> 
>> +
>> +	/*
>> +	 * Fill in disk block numbers in the state structure
>> +	 * so that we can get the buffers back after we commit
>> +	 * several transactions in the following calls.
>> +	 */
>> +	error = xfs_attr_fillstate(state);
>> +	if (error)
>> +		return error;
>> +
>> +	/*
>> +	 * Mark the attribute as INCOMPLETE
>> +	 */
>> +	error = xfs_attr3_leaf_setflag(args);
>> +	if (error)
>> +		return error;
>> +
>> +	return 0;
> 
> 	return xfs_attr3_leaf_setflag(args);
> 
> Otherwise looks good:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Alrighty, will update.  Thank you!

Allison
> 
>> +}
>> +
>> +/*
>>    * Remove a name from a B-tree attribute list.
>>    *
>>    * This will involve walking down the Btree, and may involve joining
>> @@ -1183,20 +1213,7 @@ xfs_attr_node_removename(
>>   	ASSERT(blk->bp != NULL);
>>   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   	if (args->rmtblkno > 0) {
>> -		/*
>> -		 * Fill in disk block numbers in the state structure
>> -		 * so that we can get the buffers back after we commit
>> -		 * several transactions in the following calls.
>> -		 */
>> -		error = xfs_attr_fillstate(state);
>> -		if (error)
>> -			goto out;
>> -
>> -		/*
>> -		 * Mark the attribute as INCOMPLETE, then bunmapi() the
>> -		 * remote value.
>> -		 */
>> -		error = xfs_attr3_leaf_setflag(args);
>> +		error = xfs_attr_leaf_mark_incomplete(args, state);
>>   		if (error)
>>   			goto out;
>>   
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove
  2020-04-07 14:16   ` Brian Foster
@ 2020-04-07 22:19     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-07 22:19 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On 4/7/20 7:16 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:19PM -0700, Allison Collins wrote:
>> This function is similar to xfs_attr_rmtval_remove, but adapted to
>> return EAGAIN for new transactions. We will use this later when we
>> introduce delayed attributes.  This function will eventually replace
>> xfs_attr_rmtval_remove
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr_remote.c | 25 +++++++++++++++++++++++++
>>   fs/xfs/libxfs/xfs_attr_remote.h |  1 +
>>   2 files changed, 26 insertions(+)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index 4d51969..fd4be9d 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>> @@ -711,3 +711,28 @@ xfs_attr_rmtval_remove(
>>   	}
>>   	return 0;
>>   }
>> +
>> +/*
>> + * Remove the value associated with an attribute by deleting the out-of-line
>> + * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
>> + * transaction and recall the function
>> + */
>> +int
>> +__xfs_attr_rmtval_remove(
>> +	struct xfs_da_args	*args)
>> +{
>> +	int	error, done;
>> +
>> +	/*
>> +	 * Unmap value blocks for this attr.
>> +	 */
>> +	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
>> +			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
>> +	if (error)
>> +		return error;
>> +
>> +	if (!done)
>> +		return -EAGAIN;
>> +
>> +	return 0;
>> +}
> 
> We should let xfs_attr_rmtval_remove() call this function and do the
> roll based on -EAGAIN, then eliminate the higher level function later if
> it becomes unused.
> 
> Brian
Sure, that should work.  Will update.  Thanks!

Allison

> 
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
>> index eff5f95..ee3337b 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.h
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
>> @@ -14,4 +14,5 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args);
>>   int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
>>   		xfs_buf_flags_t incore_flags);
>>   int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
>> +int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
>>   #endif /* __XFS_ATTR_REMOTE_H__ */
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 15/20] xfs: Add remote block helper functions
  2020-04-03 22:12 ` [PATCH v8 15/20] xfs: Add remote block helper functions Allison Collins
@ 2020-04-08 12:09   ` Brian Foster
  2020-04-08 16:32     ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-08 12:09 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:24PM -0700, Allison Collins wrote:
> This patch adds two new helper functions xfs_attr_store_rmt_blk and
> xfs_attr_restore_rmt_blk. These two helpers assist to remove redundant
> code associated with storing and retrieving remote blocks during the
> attr set operations.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
> Reviewed-by: Amir Goldstein <amir73il@gmail.com>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_attr.c | 50 +++++++++++++++++++++++++++++-------------------
>  1 file changed, 30 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 8d7a5db..f70b4f2 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -565,6 +565,30 @@ xfs_attr_shortform_addname(xfs_da_args_t *args)
>   * External routines when attribute list is one block
>   *========================================================================*/
>  
> +/* Store info about a remote block */
> +STATIC void
> +xfs_attr_save_rmt_blk(
> +	struct xfs_da_args	*args)
> +{
> +	args->blkno2 = args->blkno;
> +	args->index2 = args->index;
> +	args->rmtblkno2 = args->rmtblkno;
> +	args->rmtblkcnt2 = args->rmtblkcnt;
> +	args->rmtvaluelen2 = args->rmtvaluelen;
> +}
> +
> +/* Set stored info about a remote block */
> +STATIC void
> +xfs_attr_restore_rmt_blk(
> +	struct xfs_da_args	*args)
> +{
> +	args->blkno = args->blkno2;
> +	args->index = args->index2;
> +	args->rmtblkno = args->rmtblkno2;
> +	args->rmtblkcnt = args->rmtblkcnt2;
> +	args->rmtvaluelen = args->rmtvaluelen2;
> +}
> +
>  /*
>   * Tries to add an attribute to an inode in leaf form
>   *
> @@ -599,11 +623,7 @@ xfs_attr_leaf_try_add(
>  
>  		/* save the attribute state for later removal*/
>  		args->op_flags |= XFS_DA_OP_RENAME;	/* an atomic rename */
> -		args->blkno2 = args->blkno;		/* set 2nd entry info*/
> -		args->index2 = args->index;
> -		args->rmtblkno2 = args->rmtblkno;
> -		args->rmtblkcnt2 = args->rmtblkcnt;
> -		args->rmtvaluelen2 = args->rmtvaluelen;
> +		xfs_attr_save_rmt_blk(args);
>  
>  		/*
>  		 * clear the remote attr state now that it is saved so that the
> @@ -702,11 +722,8 @@ xfs_attr_leaf_addname(
>  		 * Dismantle the "old" attribute/value pair by removing
>  		 * a "remote" value (if it exists).
>  		 */
> -		args->index = args->index2;
> -		args->blkno = args->blkno2;
> -		args->rmtblkno = args->rmtblkno2;
> -		args->rmtblkcnt = args->rmtblkcnt2;
> -		args->rmtvaluelen = args->rmtvaluelen2;
> +		xfs_attr_restore_rmt_blk(args);
> +
>  		if (args->rmtblkno) {
>  			error = xfs_attr_rmtval_remove(args);
>  			if (error)
> @@ -934,11 +951,7 @@ xfs_attr_node_addname(
>  
>  		/* save the attribute state for later removal*/
>  		args->op_flags |= XFS_DA_OP_RENAME;	/* atomic rename op */
> -		args->blkno2 = args->blkno;		/* set 2nd entry info*/
> -		args->index2 = args->index;
> -		args->rmtblkno2 = args->rmtblkno;
> -		args->rmtblkcnt2 = args->rmtblkcnt;
> -		args->rmtvaluelen2 = args->rmtvaluelen;
> +		xfs_attr_save_rmt_blk(args);
>  
>  		/*
>  		 * clear the remote attr state now that it is saved so that the
> @@ -1050,11 +1063,8 @@ xfs_attr_node_addname(
>  		 * Dismantle the "old" attribute/value pair by removing
>  		 * a "remote" value (if it exists).
>  		 */
> -		args->index = args->index2;
> -		args->blkno = args->blkno2;
> -		args->rmtblkno = args->rmtblkno2;
> -		args->rmtblkcnt = args->rmtblkcnt2;
> -		args->rmtvaluelen = args->rmtvaluelen2;
> +		xfs_attr_restore_rmt_blk(args);
> +
>  		if (args->rmtblkno) {
>  			error = xfs_attr_rmtval_remove(args);
>  			if (error)
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup
  2020-04-03 22:12 ` [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup Allison Collins
@ 2020-04-08 12:09   ` Brian Foster
  2020-04-08 16:32     ` Allison Collins
  2020-04-20  6:25   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-08 12:09 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:25PM -0700, Allison Collins wrote:
> This patch adds a new helper function xfs_attr_node_removename_setup.
> This will help modularize xfs_attr_node_removename when we add delay
> ready attributes later.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_attr.c | 40 +++++++++++++++++++++++++++++++---------
>  1 file changed, 31 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index f70b4f2..3c33dc5 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1193,6 +1193,35 @@ xfs_attr_leaf_mark_incomplete(
>  }
>  
>  /*
> + * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
> + * the blocks are valid.  Any remote blocks will be marked incomplete.
> + */
> +STATIC
> +int xfs_attr_node_removename_setup(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	**state)
> +{
> +	int			error;
> +	struct xfs_da_state_blk	*blk;
> +
> +	error = xfs_attr_node_hasname(args, state);
> +	if (error != -EEXIST)
> +		return error;
> +
> +	blk = &(*state)->path.blk[(*state)->path.active - 1];
> +	ASSERT(blk->bp != NULL);
> +	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> +
> +	if (args->rmtblkno > 0) {
> +		error = xfs_attr_leaf_mark_incomplete(args, *state);
> +		if (error)
> +			return error;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
>   * Remove a name from a B-tree attribute list.
>   *
>   * This will involve walking down the Btree, and may involve joining
> @@ -1210,8 +1239,8 @@ xfs_attr_node_removename(
>  
>  	trace_xfs_attr_node_removename(args);
>  
> -	error = xfs_attr_node_hasname(args, &state);
> -	if (error != -EEXIST)
> +	error = xfs_attr_node_removename_setup(args, &state);
> +	if (error)
>  		goto out;
>  
>  	/*
> @@ -1219,14 +1248,7 @@ xfs_attr_node_removename(
>  	 * This is done before we remove the attribute so that we don't
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
> -	blk = &state->path.blk[ state->path.active-1 ];
> -	ASSERT(blk->bp != NULL);
> -	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_leaf_mark_incomplete(args, state);
> -		if (error)
> -			goto out;
> -
>  		error = xfs_attr_rmtval_remove(args);
>  		if (error)
>  			goto out;
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt
  2020-04-03 22:12 ` [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt Allison Collins
@ 2020-04-08 12:10   ` Brian Foster
  2020-04-08 16:32     ` Allison Collins
  2020-04-20  6:38   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-08 12:10 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:26PM -0700, Allison Collins wrote:
> This patch adds another new helper function
> xfs_attr_node_removename_rmt. This will also help modularize
> xfs_attr_node_removename when we add delay ready attributes later.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 32 +++++++++++++++++++++++---------
>  1 file changed, 23 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 3c33dc5..d735570 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1221,6 +1221,28 @@ int xfs_attr_node_removename_setup(
>  	return 0;
>  }
>  
> +STATIC int
> +xfs_attr_node_removename_rmt (
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	*state)
> +{
> +	int			error = 0;
> +
> +	error = xfs_attr_rmtval_remove(args);
> +	if (error)
> +		return error;
> +
> +	/*
> +	 * Refill the state structure with buffers, the prior calls
> +	 * released our buffers.
> +	 */

The comment can be widened to 80 chars now that indentation has been
reduced.

> +	error = xfs_attr_refillstate(state);
> +	if (error)
> +		return error;
> +
> +	return 0;

	return xfs_attr_refillstate(state);

With those nits fixed:

Reviewed-by: Brian Foster <bfoster@redhat.com>

> +}
> +
>  /*
>   * Remove a name from a B-tree attribute list.
>   *
> @@ -1249,15 +1271,7 @@ xfs_attr_node_removename(
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_rmtval_remove(args);
> -		if (error)
> -			goto out;
> -
> -		/*
> -		 * Refill the state structure with buffers, the prior calls
> -		 * released our buffers.
> -		 */
> -		error = xfs_attr_refillstate(state);
> +		error = xfs_attr_node_removename_rmt(args, state);
>  		if (error)
>  			goto out;
>  	}
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 15/20] xfs: Add remote block helper functions
  2020-04-08 12:09   ` Brian Foster
@ 2020-04-08 16:32     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-08 16:32 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/8/20 5:09 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:24PM -0700, Allison Collins wrote:
>> This patch adds two new helper functions xfs_attr_store_rmt_blk and
>> xfs_attr_restore_rmt_blk. These two helpers assist to remove redundant
>> code associated with storing and retrieving remote blocks during the
>> attr set operations.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
>> Reviewed-by: Amir Goldstein <amir73il@gmail.com>
>> ---
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Alrighty, thank you!

Allison
> 
>>   fs/xfs/libxfs/xfs_attr.c | 50 +++++++++++++++++++++++++++++-------------------
>>   1 file changed, 30 insertions(+), 20 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 8d7a5db..f70b4f2 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -565,6 +565,30 @@ xfs_attr_shortform_addname(xfs_da_args_t *args)
>>    * External routines when attribute list is one block
>>    *========================================================================*/
>>   
>> +/* Store info about a remote block */
>> +STATIC void
>> +xfs_attr_save_rmt_blk(
>> +	struct xfs_da_args	*args)
>> +{
>> +	args->blkno2 = args->blkno;
>> +	args->index2 = args->index;
>> +	args->rmtblkno2 = args->rmtblkno;
>> +	args->rmtblkcnt2 = args->rmtblkcnt;
>> +	args->rmtvaluelen2 = args->rmtvaluelen;
>> +}
>> +
>> +/* Set stored info about a remote block */
>> +STATIC void
>> +xfs_attr_restore_rmt_blk(
>> +	struct xfs_da_args	*args)
>> +{
>> +	args->blkno = args->blkno2;
>> +	args->index = args->index2;
>> +	args->rmtblkno = args->rmtblkno2;
>> +	args->rmtblkcnt = args->rmtblkcnt2;
>> +	args->rmtvaluelen = args->rmtvaluelen2;
>> +}
>> +
>>   /*
>>    * Tries to add an attribute to an inode in leaf form
>>    *
>> @@ -599,11 +623,7 @@ xfs_attr_leaf_try_add(
>>   
>>   		/* save the attribute state for later removal*/
>>   		args->op_flags |= XFS_DA_OP_RENAME;	/* an atomic rename */
>> -		args->blkno2 = args->blkno;		/* set 2nd entry info*/
>> -		args->index2 = args->index;
>> -		args->rmtblkno2 = args->rmtblkno;
>> -		args->rmtblkcnt2 = args->rmtblkcnt;
>> -		args->rmtvaluelen2 = args->rmtvaluelen;
>> +		xfs_attr_save_rmt_blk(args);
>>   
>>   		/*
>>   		 * clear the remote attr state now that it is saved so that the
>> @@ -702,11 +722,8 @@ xfs_attr_leaf_addname(
>>   		 * Dismantle the "old" attribute/value pair by removing
>>   		 * a "remote" value (if it exists).
>>   		 */
>> -		args->index = args->index2;
>> -		args->blkno = args->blkno2;
>> -		args->rmtblkno = args->rmtblkno2;
>> -		args->rmtblkcnt = args->rmtblkcnt2;
>> -		args->rmtvaluelen = args->rmtvaluelen2;
>> +		xfs_attr_restore_rmt_blk(args);
>> +
>>   		if (args->rmtblkno) {
>>   			error = xfs_attr_rmtval_remove(args);
>>   			if (error)
>> @@ -934,11 +951,7 @@ xfs_attr_node_addname(
>>   
>>   		/* save the attribute state for later removal*/
>>   		args->op_flags |= XFS_DA_OP_RENAME;	/* atomic rename op */
>> -		args->blkno2 = args->blkno;		/* set 2nd entry info*/
>> -		args->index2 = args->index;
>> -		args->rmtblkno2 = args->rmtblkno;
>> -		args->rmtblkcnt2 = args->rmtblkcnt;
>> -		args->rmtvaluelen2 = args->rmtvaluelen;
>> +		xfs_attr_save_rmt_blk(args);
>>   
>>   		/*
>>   		 * clear the remote attr state now that it is saved so that the
>> @@ -1050,11 +1063,8 @@ xfs_attr_node_addname(
>>   		 * Dismantle the "old" attribute/value pair by removing
>>   		 * a "remote" value (if it exists).
>>   		 */
>> -		args->index = args->index2;
>> -		args->blkno = args->blkno2;
>> -		args->rmtblkno = args->rmtblkno2;
>> -		args->rmtblkcnt = args->rmtblkcnt2;
>> -		args->rmtvaluelen = args->rmtvaluelen2;
>> +		xfs_attr_restore_rmt_blk(args);
>> +
>>   		if (args->rmtblkno) {
>>   			error = xfs_attr_rmtval_remove(args);
>>   			if (error)
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup
  2020-04-08 12:09   ` Brian Foster
@ 2020-04-08 16:32     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-08 16:32 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/8/20 5:09 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:25PM -0700, Allison Collins wrote:
>> This patch adds a new helper function xfs_attr_node_removename_setup.
>> This will help modularize xfs_attr_node_removename when we add delay
>> ready attributes later.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Ok, thank you!

Allison
> 
>>   fs/xfs/libxfs/xfs_attr.c | 40 +++++++++++++++++++++++++++++++---------
>>   1 file changed, 31 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index f70b4f2..3c33dc5 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1193,6 +1193,35 @@ xfs_attr_leaf_mark_incomplete(
>>   }
>>   
>>   /*
>> + * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
>> + * the blocks are valid.  Any remote blocks will be marked incomplete.
>> + */
>> +STATIC
>> +int xfs_attr_node_removename_setup(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	**state)
>> +{
>> +	int			error;
>> +	struct xfs_da_state_blk	*blk;
>> +
>> +	error = xfs_attr_node_hasname(args, state);
>> +	if (error != -EEXIST)
>> +		return error;
>> +
>> +	blk = &(*state)->path.blk[(*state)->path.active - 1];
>> +	ASSERT(blk->bp != NULL);
>> +	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>> +
>> +	if (args->rmtblkno > 0) {
>> +		error = xfs_attr_leaf_mark_incomplete(args, *state);
>> +		if (error)
>> +			return error;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>>    * Remove a name from a B-tree attribute list.
>>    *
>>    * This will involve walking down the Btree, and may involve joining
>> @@ -1210,8 +1239,8 @@ xfs_attr_node_removename(
>>   
>>   	trace_xfs_attr_node_removename(args);
>>   
>> -	error = xfs_attr_node_hasname(args, &state);
>> -	if (error != -EEXIST)
>> +	error = xfs_attr_node_removename_setup(args, &state);
>> +	if (error)
>>   		goto out;
>>   
>>   	/*
>> @@ -1219,14 +1248,7 @@ xfs_attr_node_removename(
>>   	 * This is done before we remove the attribute so that we don't
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>> -	blk = &state->path.blk[ state->path.active-1 ];
>> -	ASSERT(blk->bp != NULL);
>> -	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_leaf_mark_incomplete(args, state);
>> -		if (error)
>> -			goto out;
>> -
>>   		error = xfs_attr_rmtval_remove(args);
>>   		if (error)
>>   			goto out;
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt
  2020-04-08 12:10   ` Brian Foster
@ 2020-04-08 16:32     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-08 16:32 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/8/20 5:10 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:26PM -0700, Allison Collins wrote:
>> This patch adds another new helper function
>> xfs_attr_node_removename_rmt. This will also help modularize
>> xfs_attr_node_removename when we add delay ready attributes later.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 32 +++++++++++++++++++++++---------
>>   1 file changed, 23 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 3c33dc5..d735570 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1221,6 +1221,28 @@ int xfs_attr_node_removename_setup(
>>   	return 0;
>>   }
>>   
>> +STATIC int
>> +xfs_attr_node_removename_rmt (
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	*state)
>> +{
>> +	int			error = 0;
>> +
>> +	error = xfs_attr_rmtval_remove(args);
>> +	if (error)
>> +		return error;
>> +
>> +	/*
>> +	 * Refill the state structure with buffers, the prior calls
>> +	 * released our buffers.
>> +	 */
> 
> The comment can be widened to 80 chars now that indentation has been
> reduced.
> 
>> +	error = xfs_attr_refillstate(state);
>> +	if (error)
>> +		return error;
>> +
>> +	return 0;
> 
> 	return xfs_attr_refillstate(state);
> 
> With those nits fixed:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Alrighty, will fix.  Thanks!

Allison
> 
>> +}
>> +
>>   /*
>>    * Remove a name from a B-tree attribute list.
>>    *
>> @@ -1249,15 +1271,7 @@ xfs_attr_node_removename(
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_rmtval_remove(args);
>> -		if (error)
>> -			goto out;
>> -
>> -		/*
>> -		 * Refill the state structure with buffers, the prior calls
>> -		 * released our buffers.
>> -		 */
>> -		error = xfs_attr_refillstate(state);
>> +		error = xfs_attr_node_removename_rmt(args, state);
>>   		if (error)
>>   			goto out;
>>   	}
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-07 21:53     ` Allison Collins
@ 2020-04-10 16:55       ` Allison Collins
  2020-04-11 12:57         ` Brian Foster
  0 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-10 16:55 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/7/20 2:53 PM, Allison Collins wrote:
> 
> 
> On 4/7/20 8:23 AM, Brian Foster wrote:
>> On Fri, Apr 03, 2020 at 03:12:22PM -0700, Allison Collins wrote:
>>> In this patch, we hoist code from xfs_attr_set_args into two new helpers
>>> xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
>>> to simplify xfs_attr_set_args when we get into delayed attrs later.
>>>
>>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>>> ---
>>>   fs/xfs/libxfs/xfs_attr.c | 107 
>>> +++++++++++++++++++++++++++++++----------------
>>>   1 file changed, 72 insertions(+), 35 deletions(-)
>>>
>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>> index 4225a94..ba26ffe 100644
>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>> @@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
>>>   }
>>>   /*
>>> + * Check to see if the attr should be upgraded from non-existent or 
>>> shortform to
>>> + * single-leaf-block attribute list.
>>> + */
>>> +static inline bool
>>> +xfs_attr_is_shortform(
>>> +    struct xfs_inode    *ip)
>>> +{
>>> +    return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>>> +          (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>>> +          ip->i_d.di_anextents == 0);
>>
>> Logic should be indented similar to the original:
>>
>>     return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>>            (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>>         ip->i_d.di_anextents == 0);
Did you really mean to have the last line offset like this?  On second 
pass, it doesnt look similar to the original, and looks more like it may 
have been a typo in the review.  Just trying to avoid more cycles on 
spacing goofs.  Thx!

Allison

>>
>> Otherwise looks good:
>>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
> Alrighty, will fix.  Thanks!
> 
> Allison
> 
>>
>>> +}
>>> +
>>> +/*
>>> + * Attempts to set an attr in shortform, or converts the tree to 
>>> leaf form if
>>> + * there is not enough room.  If the attr is set, the transaction is 
>>> committed
>>> + * and set to NULL.
>>> + */
>>> +STATIC int
>>> +xfs_attr_set_shortform(
>>> +    struct xfs_da_args    *args,
>>> +    struct xfs_buf        **leaf_bp)
>>> +{
>>> +    struct xfs_inode    *dp = args->dp;
>>> +    int            error, error2 = 0;
>>> +
>>> +    /*
>>> +     * Try to add the attr to the attribute list in the inode.
>>> +     */
>>> +    error = xfs_attr_try_sf_addname(dp, args);
>>> +    if (error != -ENOSPC) {
>>> +        error2 = xfs_trans_commit(args->trans);
>>> +        args->trans = NULL;
>>> +        return error ? error : error2;
>>> +    }
>>> +    /*
>>> +     * It won't fit in the shortform, transform to a leaf block.  GROT:
>>> +     * another possible req'mt for a double-split btree op.
>>> +     */
>>> +    error = xfs_attr_shortform_to_leaf(args, leaf_bp);
>>> +    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. Once we're done rolling the 
>>> transaction we
>>> +     * can release the hold and add the attr to the leaf.
>>> +     */
>>> +    xfs_trans_bhold(args->trans, *leaf_bp);
>>> +    error = xfs_defer_finish(&args->trans);
>>> +    xfs_trans_bhold_release(args->trans, *leaf_bp);
>>> +    if (error) {
>>> +        xfs_trans_brelse(args->trans, *leaf_bp);
>>> +        return error;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +/*
>>>    * Set the attribute specified in @args.
>>>    */
>>>   int
>>> @@ -212,48 +272,25 @@ xfs_attr_set_args(
>>>   {
>>>       struct xfs_inode    *dp = args->dp;
>>>       struct xfs_buf          *leaf_bp = NULL;
>>> -    int            error, error2 = 0;
>>> +    int            error = 0;
>>>       /*
>>> -     * If the attribute list is non-existent or a shortform list,
>>> -     * upgrade it to a single-leaf-block attribute list.
>>> +     * 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.
>>>        */
>>> -    if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>>> -        (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>>> -         dp->i_d.di_anextents == 0)) {
>>> -
>>> -        /*
>>> -         * Try to add the attr to the attribute list in the inode.
>>> -         */
>>> -        error = xfs_attr_try_sf_addname(dp, args);
>>> -        if (error != -ENOSPC) {
>>> -            error2 = xfs_trans_commit(args->trans);
>>> -            args->trans = NULL;
>>> -            return error ? error : error2;
>>> -        }
>>> -
>>> -        /*
>>> -         * It won't fit in the shortform, transform to a leaf block.
>>> -         * GROT: another possible req'mt for a double-split btree op.
>>> -         */
>>> -        error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
>>> -        if (error)
>>> -            return error;
>>> +    if (xfs_attr_is_shortform(dp)) {
>>>           /*
>>> -         * 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.
>>> -         * Once we're done rolling the transaction we can release
>>> -         * the hold and add the attr to the leaf.
>>> +         * If the attr was successfully set in shortform, the
>>> +         * transaction is committed and set to NULL.  Otherwise, is it
>>> +         * converted from shortform to leaf, and the transaction is
>>> +         * retained.
>>>            */
>>> -        xfs_trans_bhold(args->trans, leaf_bp);
>>> -        error = xfs_defer_finish(&args->trans);
>>> -        xfs_trans_bhold_release(args->trans, leaf_bp);
>>> -        if (error) {
>>> -            xfs_trans_brelse(args->trans, leaf_bp);
>>> +        error = xfs_attr_set_shortform(args, &leaf_bp);
>>> +        if (error || !args->trans)
>>>               return error;
>>> -        }
>>>       }
>>>       if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>> -- 
>>> 2.7.4
>>>
>>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-10 16:55       ` Allison Collins
@ 2020-04-11 12:57         ` Brian Foster
  0 siblings, 0 replies; 70+ messages in thread
From: Brian Foster @ 2020-04-11 12:57 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 10, 2020 at 09:55:55AM -0700, Allison Collins wrote:
> 
> 
> On 4/7/20 2:53 PM, Allison Collins wrote:
> > 
> > 
> > On 4/7/20 8:23 AM, Brian Foster wrote:
> > > On Fri, Apr 03, 2020 at 03:12:22PM -0700, Allison Collins wrote:
> > > > In this patch, we hoist code from xfs_attr_set_args into two new helpers
> > > > xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
> > > > to simplify xfs_attr_set_args when we get into delayed attrs later.
> > > > 
> > > > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > > > ---
> > > >   fs/xfs/libxfs/xfs_attr.c | 107
> > > > +++++++++++++++++++++++++++++++----------------
> > > >   1 file changed, 72 insertions(+), 35 deletions(-)
> > > > 
> > > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > > index 4225a94..ba26ffe 100644
> > > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > > @@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
> > > >   }
> > > >   /*
> > > > + * Check to see if the attr should be upgraded from
> > > > non-existent or shortform to
> > > > + * single-leaf-block attribute list.
> > > > + */
> > > > +static inline bool
> > > > +xfs_attr_is_shortform(
> > > > +    struct xfs_inode    *ip)
> > > > +{
> > > > +    return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> > > > +          (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> > > > +          ip->i_d.di_anextents == 0);
> > > 
> > > Logic should be indented similar to the original:
> > > 
> > >     return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> > >            (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> > >         ip->i_d.di_anextents == 0);
> Did you really mean to have the last line offset like this?  On second pass,
> it doesnt look similar to the original, and looks more like it may have been
> a typo in the review.  Just trying to avoid more cycles on spacing goofs.
> Thx!
> 

Nope. What is quoted here is not how my reply appears in my mailer. :/
Anyways, the last line is supposed to be aligned to the first character
inside the opening brace of the previous line.

Brian

> Allison
> 
> > > 
> > > Otherwise looks good:
> > > 
> > > Reviewed-by: Brian Foster <bfoster@redhat.com>
> > Alrighty, will fix.  Thanks!
> > 
> > Allison
> > 
> > > 
> > > > +}
> > > > +
> > > > +/*
> > > > + * Attempts to set an attr in shortform, or converts the tree
> > > > to leaf form if
> > > > + * there is not enough room.  If the attr is set, the
> > > > transaction is committed
> > > > + * and set to NULL.
> > > > + */
> > > > +STATIC int
> > > > +xfs_attr_set_shortform(
> > > > +    struct xfs_da_args    *args,
> > > > +    struct xfs_buf        **leaf_bp)
> > > > +{
> > > > +    struct xfs_inode    *dp = args->dp;
> > > > +    int            error, error2 = 0;
> > > > +
> > > > +    /*
> > > > +     * Try to add the attr to the attribute list in the inode.
> > > > +     */
> > > > +    error = xfs_attr_try_sf_addname(dp, args);
> > > > +    if (error != -ENOSPC) {
> > > > +        error2 = xfs_trans_commit(args->trans);
> > > > +        args->trans = NULL;
> > > > +        return error ? error : error2;
> > > > +    }
> > > > +    /*
> > > > +     * It won't fit in the shortform, transform to a leaf block.  GROT:
> > > > +     * another possible req'mt for a double-split btree op.
> > > > +     */
> > > > +    error = xfs_attr_shortform_to_leaf(args, leaf_bp);
> > > > +    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. Once we're done rolling the
> > > > transaction we
> > > > +     * can release the hold and add the attr to the leaf.
> > > > +     */
> > > > +    xfs_trans_bhold(args->trans, *leaf_bp);
> > > > +    error = xfs_defer_finish(&args->trans);
> > > > +    xfs_trans_bhold_release(args->trans, *leaf_bp);
> > > > +    if (error) {
> > > > +        xfs_trans_brelse(args->trans, *leaf_bp);
> > > > +        return error;
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +/*
> > > >    * Set the attribute specified in @args.
> > > >    */
> > > >   int
> > > > @@ -212,48 +272,25 @@ xfs_attr_set_args(
> > > >   {
> > > >       struct xfs_inode    *dp = args->dp;
> > > >       struct xfs_buf          *leaf_bp = NULL;
> > > > -    int            error, error2 = 0;
> > > > +    int            error = 0;
> > > >       /*
> > > > -     * If the attribute list is non-existent or a shortform list,
> > > > -     * upgrade it to a single-leaf-block attribute list.
> > > > +     * 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.
> > > >        */
> > > > -    if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> > > > -        (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> > > > -         dp->i_d.di_anextents == 0)) {
> > > > -
> > > > -        /*
> > > > -         * Try to add the attr to the attribute list in the inode.
> > > > -         */
> > > > -        error = xfs_attr_try_sf_addname(dp, args);
> > > > -        if (error != -ENOSPC) {
> > > > -            error2 = xfs_trans_commit(args->trans);
> > > > -            args->trans = NULL;
> > > > -            return error ? error : error2;
> > > > -        }
> > > > -
> > > > -        /*
> > > > -         * It won't fit in the shortform, transform to a leaf block.
> > > > -         * GROT: another possible req'mt for a double-split btree op.
> > > > -         */
> > > > -        error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
> > > > -        if (error)
> > > > -            return error;
> > > > +    if (xfs_attr_is_shortform(dp)) {
> > > >           /*
> > > > -         * 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.
> > > > -         * Once we're done rolling the transaction we can release
> > > > -         * the hold and add the attr to the leaf.
> > > > +         * If the attr was successfully set in shortform, the
> > > > +         * transaction is committed and set to NULL.  Otherwise, is it
> > > > +         * converted from shortform to leaf, and the transaction is
> > > > +         * retained.
> > > >            */
> > > > -        xfs_trans_bhold(args->trans, leaf_bp);
> > > > -        error = xfs_defer_finish(&args->trans);
> > > > -        xfs_trans_bhold_release(args->trans, leaf_bp);
> > > > -        if (error) {
> > > > -            xfs_trans_brelse(args->trans, leaf_bp);
> > > > +        error = xfs_attr_set_shortform(args, &leaf_bp);
> > > > +        if (error || !args->trans)
> > > >               return error;
> > > > -        }
> > > >       }
> > > >       if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > > -- 
> > > > 2.7.4
> > > > 
> > > 
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-03 22:12 ` [PATCH v8 18/20] xfs: Add delay ready attr remove routines Allison Collins
@ 2020-04-13 12:30   ` Brian Foster
  2020-04-14 21:35     ` Allison Collins
  2020-04-20  9:06   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-13 12:30 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:27PM -0700, Allison Collins wrote:
> This patch modifies the attr remove routines to be delay ready. This
> means they no longer roll or commit transactions, but instead return
> -EAGAIN to have the calling routine roll and refresh the transaction. In
> this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
> uses a sort of state machine like switch to keep track of where it was
> when EAGAIN was returned. xfs_attr_node_removename has also been
> modified to use the switch, and a new version of xfs_attr_remove_args
> consists of a simple loop to refresh the transaction until the operation
> is completed.
> 
> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
> helper) and then __xfs_attr_rmtval_remove. We will rename
> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> done.
> 
> This patch also adds a new struct xfs_delattr_context, which we will use
> to keep track of the current state of an attribute operation. The new
> xfs_delattr_state enum is used to track various operations that are in
> progress so that we know not to repeat them, and resume where we left
> off before EAGAIN was returned to cycle out the transaction. Other
> members take the place of local variables that need to retain their
> values across multiple function recalls.
> 
> Below is a state machine diagram for attr remove operations. The
> XFS_DAS_* states indicate places where the function would return
> -EAGAIN, and then immediately resume from after being recalled by the
> calling function.  States marked as a "subroutine state" indicate that
> they belong to a subroutine, and so the calling function needs to pass
> them back to that subroutine to allow it to finish where it left off.
> But they otherwise do not have a role in the calling function other than
> just passing through.
> 
>  xfs_attr_remove_iter()
>          XFS_DAS_RM_SHRINK     ─┐
>          (subroutine state)     │
>                                 │
>          XFS_DAS_RMTVAL_REMOVE ─┤
>          (subroutine state)     │
>                                 └─>xfs_attr_node_removename()
>                                                  │
>                                                  v
>                                          need to remove
>                                    ┌─n──  rmt blocks?
>                                    │             │
>                                    │             y
>                                    │             │
>                                    │             v
>                                    │  ┌─>XFS_DAS_RMTVAL_REMOVE
>                                    │  │          │
>                                    │  │          v
>                                    │  └──y── more blks
>                                    │         to remove?
>                                    │             │
>                                    │             n
>                                    │             │
>                                    │             v
>                                    │         need to
>                                    └─────> shrink tree? ─n─┐
>                                                  │         │
>                                                  y         │
>                                                  │         │
>                                                  v         │
>                                          XFS_DAS_RM_SHRINK │
>                                                  │         │
>                                                  v         │
>                                                 done <─────┘
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---

All in all this is starting to look much more simple to me, at least in
the remove path. ;P There's only a few states and the markers that are
introduced are fairly straightforward, etc. Comments to follow..

>  fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
>  fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
>  2 files changed, 168 insertions(+), 38 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index d735570..f700976 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>   */
>  STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>  STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
> +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>  
>  /*
> @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>   */
>  STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>  STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
> +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>  				 struct xfs_da_state **state);
>  STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
>  STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
>  
> +STATIC void
> +xfs_delattr_context_init(
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_args		*args)
> +{
> +	memset(dac, 0, sizeof(struct xfs_delattr_context));
> +	dac->da_args = args;
> +}
> +
>  int
>  xfs_inode_hasattr(
>  	struct xfs_inode	*ip)
> @@ -356,20 +365,66 @@ xfs_has_attr(
>   */
>  int
>  xfs_attr_remove_args(
> -	struct xfs_da_args      *args)
> +	struct xfs_da_args	*args)
>  {
> +	int			error = 0;
> +	struct			xfs_delattr_context dac;
> +
> +	xfs_delattr_context_init(&dac, args);
> +
> +	do {
> +		error = xfs_attr_remove_iter(&dac);
> +		if (error != -EAGAIN)
> +			break;
> +
> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> +			error = xfs_defer_finish(&args->trans);
> +			if (error)
> +				break;
> +		}
> +
> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +		if (error)
> +			break;
> +	} while (true);
> +
> +	return error;
> +}
> +
> +/*
> + * Remove the attribute specified in @args.
> + *
> + * This function may return -EAGAIN to signal that the transaction needs to be
> + * rolled.  Callers should continue calling this function until they receive a
> + * return value other than -EAGAIN.
> + */
> +int
> +xfs_attr_remove_iter(
> +	struct xfs_delattr_context *dac)
> +{
> +	struct xfs_da_args	*args = dac->da_args;
>  	struct xfs_inode	*dp = args->dp;
>  	int			error;
>  
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_RM_SHRINK:
> +	case XFS_DAS_RMTVAL_REMOVE:
> +		return xfs_attr_node_removename(dac);
> +	default:
> +		break;
> +	}
> +

Hmm.. so we're duplicating the call instead of using labels..? I'm
wondering if this can be elegantly combined with the if/else branches
below, particularly since node format is the only situation that seems
to require a roll here.

>  	if (!xfs_inode_hasattr(dp)) {
>  		error = -ENOATTR;
>  	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>  		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>  		error = xfs_attr_shortform_remove(args);
>  	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		error = xfs_attr_leaf_removename(args);
> +		error = xfs_attr_leaf_removename(dac);
>  	} else {
> -		error = xfs_attr_node_removename(args);
> +		error = xfs_attr_node_removename(dac);
>  	}
>  
>  	return error;
> @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
>   */
>  STATIC int
>  xfs_attr_leaf_removename(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> -	struct xfs_inode	*dp;
> -	struct xfs_buf		*bp;
> -	int			error, forkoff;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp;
> +	struct xfs_buf			*bp;
> +	int				error, forkoff;
>  
>  	trace_xfs_attr_leaf_removename(args);
>  
> @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
>  		/* bp is gone due to xfs_da_shrink_inode */
>  		if (error)
>  			return error;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> +
> +		dac->flags |= XFS_DAC_DEFER_FINISH;

There's no -EAGAIN return here, is this an exit path for the remove?

>  	}
>  	return 0;
>  }
> @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
>   */
>  STATIC int
>  xfs_attr_node_shrink(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state     *state)
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_state		*state)
>  {
> -	struct xfs_inode	*dp = args->dp;
> -	int			error, forkoff;
> -	struct xfs_buf		*bp;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	int				error, forkoff;
> +	struct xfs_buf			*bp;
>  
>  	/*
>  	 * Have to get rid of the copy of this dabuf in the state.
> @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
>  		if (error)
>  			return error;
>  
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> +		dac->flags |= XFS_DAC_DEFER_FINISH;

Same question here.

>  	} else
>  		xfs_trans_brelse(args->trans, bp);
>  
> @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
>  
>  /*
>   * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
> - * the blocks are valid.  Any remote blocks will be marked incomplete.
> + * the blocks are valid.  Any remote blocks will be marked incomplete and
> + * invalidated.
>   */
>  STATIC
>  int xfs_attr_node_removename_setup(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state	**state)
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_state		**state)
>  {
> +	struct xfs_da_args	*args = dac->da_args;
>  	int			error;
>  	struct xfs_da_state_blk	*blk;
>  
> @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
>  	ASSERT(blk->bp != NULL);
>  	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  
> +	/*
> +	 * Store blk and state in the context incase we need to cycle out the
> +	 * transaction
> +	 */
> +	dac->blk = blk;
> +	dac->da_state = *state;
> +
>  	if (args->rmtblkno > 0) {
>  		error = xfs_attr_leaf_mark_incomplete(args, *state);
>  		if (error)
>  			return error;
> +
> +		error = xfs_attr_rmtval_invalidate(args);
> +		if (error)
> +			return error;

Seems like this moves code, which should probably happen in a separate
patch.

>  	}
>  
>  	return 0;
> @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
>  {
>  	int			error = 0;
>  
> -	error = xfs_attr_rmtval_remove(args);
> +	/*
> +	 * May return -EAGAIN to request that the caller recall this function
> +	 */
> +	error = __xfs_attr_rmtval_remove(args);
>  	if (error)
>  		return error;
>  
> @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
>   * This will involve walking down the Btree, and may involve joining
>   * leaf nodes and even joining intermediate nodes up to and including
>   * the root node (a special case of an intermediate node).
> + *
> + * This routine is meant to function as either an inline or 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_node_removename(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> +	struct xfs_da_args	*args = dac->da_args;
>  	struct xfs_da_state	*state;
>  	struct xfs_da_state_blk	*blk;
>  	int			retval, error;
>  	struct xfs_inode	*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
> +	state = dac->da_state;
> +	blk = dac->blk;
> +
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_RMTVAL_REMOVE:
> +		goto das_rmtval_remove;
> +	case XFS_DAS_RM_SHRINK:
> +		goto das_rm_shrink;
> +	default:
> +		break;
> +	}
>  
> -	error = xfs_attr_node_removename_setup(args, &state);
> +	error = xfs_attr_node_removename_setup(dac, &state);
>  	if (error)
>  		goto out;
>  
> @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
>  	 * This is done before we remove the attribute so that we don't
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
> +
> +das_rmtval_remove:
> +

I wonder if we need this label just to protect the setup. Perhaps if we
had something like:

	/* set up the remove only once... */
	if (dela_state == 0)
		error = xfs_attr_node_removename_setup(...);

... we could reduce another state.

We could also accomplish the same thing with an explicit state to
indicate the setup already occurred or a new dac flag, though I'm not
sure a flag is appropriate if it would only be used here.

Brian

>  	if (args->rmtblkno > 0) {
>  		error = xfs_attr_node_removename_rmt(args, state);
> -		if (error)
> -			goto out;
> +		if (error) {
> +			if (error == -EAGAIN)
> +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
> +			return error;
> +		}
>  	}
>  
>  	/*
> @@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
>  		error = xfs_da3_join(state);
>  		if (error)
>  			goto out;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			goto out;
> -		/*
> -		 * Commit the Btree join operation and start a new trans.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> -			goto out;
> +
> +		dac->flags |= XFS_DAC_DEFER_FINISH;
> +		dac->dela_state = XFS_DAS_RM_SHRINK;
> +		return -EAGAIN;
>  	}
>  
> +das_rm_shrink:
> +	dac->dela_state = XFS_DAS_RM_SHRINK;
> +
>  	/*
>  	 * If the result is small enough, push it all into the inode.
>  	 */
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> -		error = xfs_attr_node_shrink(args, state);
> +		error = xfs_attr_node_shrink(dac, state);
>  
>  	error = 0;
>  out:
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 66575b8..0e8ae1a 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -74,6 +74,43 @@ struct xfs_attr_list_context {
>  };
>  
>  
> +/*
> + * ========================================================================
> + * Structure used to pass context around among the delayed routines.
> + * ========================================================================
> + */
> +
> +/*
> + * Enum values for xfs_delattr_context.da_state
> + *
> + * These values are used by delayed attribute operations to keep track  of where
> + * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
> + * calling function to roll the transaction, and then recall the subroutine to
> + * finish the operation.  The enum is then used by the subroutine to jump back
> + * to where it was and resume executing where it left off.
> + */
> +enum xfs_delattr_state {
> +				      /* Zero is uninitalized */
> +	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
> +	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
> +};
> +
> +/*
> + * Defines for xfs_delattr_context.flags
> + */
> +#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
> +
> +/*
> + * Context used for keeping track of delayed attribute operations
> + */
> +struct xfs_delattr_context {
> +	struct xfs_da_args      *da_args;
> +	struct xfs_da_state     *da_state;
> +	struct xfs_da_state_blk *blk;
> +	unsigned int            flags;
> +	enum xfs_delattr_state  dela_state;
> +};
> +
>  /*========================================================================
>   * Function prototypes for the kernel.
>   *========================================================================*/
> @@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
>  int xfs_attr_set_args(struct xfs_da_args *args);
>  int xfs_has_attr(struct xfs_da_args *args);
>  int xfs_attr_remove_args(struct xfs_da_args *args);
> +int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>  bool xfs_attr_namecheck(const void *name, size_t length);
>  
>  #endif	/* __XFS_ATTR_H__ */
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-03 22:12 ` [PATCH v8 19/20] xfs: Add delay ready attr set routines Allison Collins
@ 2020-04-13 13:40   ` Brian Foster
  2020-04-15 22:08     ` Allison Collins
  2020-04-20 11:45   ` Chandan Rajendra
  1 sibling, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-13 13:40 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:28PM -0700, Allison Collins wrote:
> This patch modifies the attr set routines to be delay ready. This means
> they no longer roll or commit transactions, but instead return -EAGAIN
> to have the calling routine roll and refresh the transaction.  In this
> series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
> state machine like switch to keep track of where it was when EAGAIN was
> returned.
> 
> Two new helper functions have been added: xfs_attr_rmtval_set_init and
> xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
> xfs_attr_rmtval_set, but they store the current block in the delay attr
> context to allow the caller to roll the transaction between allocations.
> This helps to simplify and consolidate code used by
> xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
> now become a simple loop to refresh the transaction until the operation
> is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
> removed.
> 
> Below is a state machine diagram for attr set operations. The XFS_DAS_*
> states indicate places where the function would return -EAGAIN, and then
> immediately resume from after being recalled by the calling function.
> States marked as a "subroutine state" indicate that they belong to a
> subroutine, and so the calling function needs to pass them back to that
> subroutine to allow it to finish where it left off.  But they otherwise
> do not have a role in the calling function other than just passing
> through.
> 
>  xfs_attr_set_iter()
>                  │
>                  v
>            need to upgrade
>           from sf to leaf? ──n─┐
>                  │             │
>                  y             │
>                  │             │
>                  V             │
>           XFS_DAS_ADD_LEAF     │
>                  │             │
>                  v             │
>   ┌──────n── fork has   <──────┘
>   │         only 1 blk?
>   │              │
>   │              y
>   │              │
>   │              v
>   │     xfs_attr_leaf_try_add()
>   │              │
>   │              v
>   │          had enough
>   ├──────n──   space?
>   │              │
>   │              y
>   │              │
>   │              v
>   │      XFS_DAS_FOUND_LBLK  ──┐
>   │                            │
>   │      XFS_DAS_FLIP_LFLAG  ──┤
>   │      (subroutine state)    │
>   │                            │
>   │      XFS_DAS_ALLOC_LEAF  ──┤
>   │      (subroutine state)    │
>   │                            └─>xfs_attr_leaf_addname()
>   │                                              │
>   │                                              v
>   │                                ┌─────n──  need to
>   │                                │        alloc blks?
>   │                                │             │
>   │                                │             y
>   │                                │             │
>   │                                │             v
>   │                                │  ┌─>XFS_DAS_ALLOC_LEAF
>   │                                │  │          │
>   │                                │  │          v
>   │                                │  └──y── need to alloc
>   │                                │         more blocks?
>   │                                │             │
>   │                                │             n
>   │                                │             │
>   │                                │             v
>   │                                │          was this
>   │                                └────────> a rename? ──n─┐
>   │                                              │          │
>   │                                              y          │
>   │                                              │          │
>   │                                              v          │
>   │                                        flip incomplete  │
>   │                                            flag         │
>   │                                              │          │
>   │                                              v          │
>   │                                      XFS_DAS_FLIP_LFLAG │
>   │                                              │          │
>   │                                              v          │
>   │                                            remove       │
>   │                        XFS_DAS_RM_LBLK ─> old name      │
>   │                                 ^            │          │
>   │                                 │            v          │
>   │                                 └──────y── more to      │
>   │                                            remove       │
>   │                                              │          │
>   │                                              n          │
>   │                                              │          │
>   │                                              v          │
>   │                                             done <──────┘
>   └────> XFS_DAS_LEAF_TO_NODE ─┐
>                                │
>          XFS_DAS_FOUND_NBLK  ──┤
>          (subroutine state)    │
>                                │
>          XFS_DAS_ALLOC_NODE  ──┤
>          (subroutine state)    │
>                                │
>          XFS_DAS_FLIP_NFLAG  ──┤
>          (subroutine state)    │
>                                │
>                                └─>xfs_attr_node_addname()
>                                                  │
>                                                  v
>                                          find space to store
>                                         attr. Split if needed
>                                                  │
>                                                  v
>                                          XFS_DAS_FOUND_NBLK
>                                                  │
>                                                  v
>                                    ┌─────n──  need to
>                                    │        alloc blks?
>                                    │             │
>                                    │             y
>                                    │             │
>                                    │             v
>                                    │  ┌─>XFS_DAS_ALLOC_NODE
>                                    │  │          │
>                                    │  │          v
>                                    │  └──y── need to alloc
>                                    │         more blocks?
>                                    │             │
>                                    │             n
>                                    │             │
>                                    │             v
>                                    │          was this
>                                    └────────> a rename? ──n─┐
>                                                  │          │
>                                                  y          │
>                                                  │          │
>                                                  v          │
>                                            flip incomplete  │
>                                                flag         │
>                                                  │          │
>                                                  v          │
>                                          XFS_DAS_FLIP_NFLAG │
>                                                  │          │
>                                                  v          │
>                                                remove       │
>                            XFS_DAS_RM_NBLK ─> old name      │
>                                     ^            │          │
>                                     │            v          │
>                                     └──────y── more to      │
>                                                remove       │
>                                                  │          │
>                                                  n          │
>                                                  │          │
>                                                  v          │
>                                                 done <──────┘
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---

Only a cursory pass given the previous feedback...

>  fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
>  fs/xfs/libxfs/xfs_attr.h        |  16 ++
>  fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
>  fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
>  fs/xfs/libxfs/xfs_attr_remote.h |   4 +
>  fs/xfs/xfs_attr_inactive.c      |   1 +
>  fs/xfs/xfs_trace.h              |   1 -
>  7 files changed, 351 insertions(+), 167 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index f700976..c160b7a 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>   * Internal routines when attribute list is one block.
>   */
>  STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
> -STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> +STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>  
> @@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>   * Internal routines when attribute list is more than one block.
>   */
>  STATIC int xfs_attr_node_get(xfs_da_args_t *args);
> -STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> +STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>  				 struct xfs_da_state **state);
>  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 void
>  xfs_delattr_context_init(
> @@ -227,8 +228,11 @@ xfs_attr_is_shortform(
>  
>  /*
>   * Attempts to set an attr in shortform, or converts the tree to leaf form if
> - * there is not enough room.  If the attr is set, the transaction is committed
> - * and set to NULL.
> + * there is not enough room.  This function is meant to operate as a helper
> + * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
> + * that the calling function should roll the transaction, and then proceed to
> + * add the attr in leaf form.  This subroutine does not expect to be recalled
> + * again like the other delayed attr routines do.
>   */
>  STATIC int
>  xfs_attr_set_shortform(
> @@ -236,16 +240,16 @@ xfs_attr_set_shortform(
>  	struct xfs_buf		**leaf_bp)
>  {
>  	struct xfs_inode	*dp = args->dp;
> -	int			error, error2 = 0;
> +	int			error = 0;
>  
>  	/*
>  	 * Try to add the attr to the attribute list in the inode.
>  	 */
>  	error = xfs_attr_try_sf_addname(dp, args);
> +
> +	/* Should only be 0, -EEXIST or ENOSPC */
>  	if (error != -ENOSPC) {
> -		error2 = xfs_trans_commit(args->trans);
> -		args->trans = NULL;
> -		return error ? error : error2;
> +		return error;
>  	}
>  	/*
>  	 * It won't fit in the shortform, transform to a leaf block.  GROT:
> @@ -258,18 +262,10 @@ xfs_attr_set_shortform(
>  	/*
>  	 * 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. Once we're done rolling the transaction we
> -	 * can release the hold and add the attr to the leaf.
> +	 * with the write verifier.
>  	 */
>  	xfs_trans_bhold(args->trans, *leaf_bp);
> -	error = xfs_defer_finish(&args->trans);
> -	xfs_trans_bhold_release(args->trans, *leaf_bp);
> -	if (error) {
> -		xfs_trans_brelse(args->trans, *leaf_bp);
> -		return error;
> -	}
> -
> -	return 0;
> +	return -EAGAIN;
>  }
>  
>  /*
> @@ -279,9 +275,83 @@ int
>  xfs_attr_set_args(
>  	struct xfs_da_args	*args)
>  {
> -	struct xfs_inode	*dp = args->dp;
> -	struct xfs_buf          *leaf_bp = NULL;
> -	int			error = 0;
> +	struct xfs_buf			*leaf_bp = NULL;
> +	int				error = 0;
> +	struct xfs_delattr_context	dac;
> +
> +	xfs_delattr_context_init(&dac, args);
> +
> +	do {
> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
> +		if (error != -EAGAIN)
> +			break;
> +
> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> +			error = xfs_defer_finish(&args->trans);
> +			if (error)
> +				break;
> +		}
> +
> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +		if (error)
> +			break;
> +
> +		if (leaf_bp) {
> +			xfs_trans_bjoin(args->trans, leaf_bp);
> +			xfs_trans_bhold(args->trans, leaf_bp);
> +		}
> +
> +	} while (true);
> +
> +	return error;
> +}
> +
> +/*
> + * 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.
> + */
> +int
> +xfs_attr_set_iter(
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_buf			**leaf_bp)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	int				error = 0;
> +	int				sf_size;
> +
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_ADD_LEAF:
> +		goto das_add_leaf;
> +	case XFS_DAS_ALLOC_LEAF:
> +	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:
> +	case XFS_DAS_LEAF_TO_NODE:
> +		goto das_node;
> +	default:
> +		break;
> +	}
> +
> +	/*
> +	 * New inodes may not have an attribute fork yet. So set the attribute
> +	 * fork appropriately
> +	 */
> +	if (XFS_IFORK_Q((args->dp)) == 0) {
> +		sf_size = sizeof(struct xfs_attr_sf_hdr) +
> +		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
> +		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
> +		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
> +		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
> +	}
>  

Is this hunk moved from somewhere? If so, we should probably handle that
in a separate patch. I think we really want these last couple of patches
to introduce the state/markers and not much else.

>  	/*
>  	 * If the attribute list is already in leaf format, jump straight to
> @@ -292,40 +362,53 @@ xfs_attr_set_args(
>  	if (xfs_attr_is_shortform(dp)) {
>  
>  		/*
> -		 * If the attr was successfully set in shortform, the
> -		 * transaction is committed and set to NULL.  Otherwise, is it
> -		 * converted from shortform to leaf, and the transaction is
> -		 * retained.
> +		 * 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 || !args->trans)
> -			return error;
> +		error = xfs_attr_set_shortform(args, leaf_bp);
> +		if (error == -EAGAIN) {
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_ADD_LEAF;
> +		}
> +		return error;

Similar to the previous patch, I wonder if we need the explicit states
that are otherwise handled by existing inode state. For example, if the
above returns -EAGAIN, xfs_attr_is_shortform() is no longer true on
reentry, right? If that's the case for the other conversions, it seems
like we might only need one state (XFS_DAS_FOUND_LBLK) for this
function.

BTW, that general approach might be more clear if we lifted the format
conversions into this level from down in the format specific add
handlers. The goal would be to make the high level flow look something
like:

	if (shortform) {
		error = sf_try_add();
		if (error == -ENOSPC) {
			shortform_to_leaf(...);
			...
			return -EAGAIN;
		}
	} else if (xfs_bmap_one_block(...)) {
		error = xfs_attr_leaf_try_add(args, *leaf_bp);
		if (error == -ENOSPC) {
			leaf_to_node(...);
			return -EAGAIN;
		}

		... state stuff for leaf add ...
	} else {
		error = xfs_attr_node_addname(dac);
	}

Hm? Of course, something like that should be incorporated via
independent refactoring patches.

>  	}
>  
> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		error = xfs_attr_leaf_addname(args);
> -		if (error != -ENOSPC)
> -			return error;
> +das_add_leaf:
>  
> -		/*
> -		 * Commit that transaction so that the node_addname()
> -		 * call can manage its own transactions.
> -		 */
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> +	/*
> +	 * After a shortform to leaf conversion, we need to hold the leaf and
> +	 * cylce out the transaction.  When we get back, we need to release
> +	 * the leaf.
> +	 */
> +	if (*leaf_bp != NULL) {
> +		xfs_trans_brelse(args->trans, *leaf_bp);
> +		*leaf_bp = NULL;
> +	}
>  
> -		/*
> -		 * Commit the current trans (including the inode) and
> -		 * start a new one.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> +		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> +		switch (error) {
> +		case -ENOSPC:
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> +			return -EAGAIN;
> +		case 0:
> +			dac->dela_state = XFS_DAS_FOUND_LBLK;
> +			return -EAGAIN;
> +		default:
>  			return error;
> -
> +		}
> +das_leaf:
> +		error = xfs_attr_leaf_addname(dac);
> +		if (error == -ENOSPC) {
> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> +			return -EAGAIN;
> +		}
> +		return error;
>  	}
> -
> -	error = xfs_attr_node_addname(args);
> +das_node:
> +	error = xfs_attr_node_addname(dac);
>  	return error;
>  }
>  
> @@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
>   *
>   * This leaf block cannot have a "remote" value, we only call this routine
>   * if bmap_one_block() says there is only one block (ie: no remote blks).
> + *
> + * 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_leaf_addname(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> -	int			error, forkoff;
> -	struct xfs_buf		*bp = NULL;
> -	struct xfs_inode	*dp = args->dp;
> -
> -	trace_xfs_attr_leaf_addname(args);
> -
> -	error = xfs_attr_leaf_try_add(args, bp);
> -	if (error)
> -		return error;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_buf			*bp = NULL;
> +	int				error, forkoff;
> +	struct xfs_inode		*dp = args->dp;
>  
> -	/*
> -	 * Commit the transaction that added the attr name so that
> -	 * later routines can manage their own transactions.
> -	 */
> -	error = xfs_trans_roll_inode(&args->trans, dp);
> -	if (error)
> -		return error;
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_FLIP_LFLAG:
> +		goto das_flip_flag;
> +	case XFS_DAS_ALLOC_LEAF:
> +		goto das_alloc_leaf;
> +	case XFS_DAS_RM_LBLK:
> +		goto das_rm_lblk;
> +	default:
> +		break;
> +	}
>  
>  	/*
>  	 * If there was an out-of-line value, allocate the blocks we
> @@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
>  	 * maximum size of a transaction and/or hit a deadlock.
>  	 */
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_rmtval_set(args);
> +
> +		/* Open coded xfs_attr_rmtval_set without trans handling */
> +		error = xfs_attr_rmtval_set_init(dac);
> +		if (error)
> +			return error;
> +
> +		/*
> +		 * Roll through the "value", allocating blocks on disk as
> +		 * required.
> +		 */
> +das_alloc_leaf:

If we filter out the setup above, it seems like this state could be
reduced to check for ->blkcnt > 0.

> +		while (dac->blkcnt > 0) {
> +			error = xfs_attr_rmtval_set_blk(dac);
> +			if (error)
> +				return error;
> +
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_ALLOC_LEAF;
> +			return -EAGAIN;
> +		}
> +
> +		error = xfs_attr_rmtval_set_value(args);
>  		if (error)
>  			return error;
>  	}
> @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
>  		error = xfs_attr3_leaf_flipflags(args);
>  		if (error)
>  			return error;
> -		/*
> -		 * Commit the flag value change and start the next trans in
> -		 * series.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -		if (error)
> -			return error;
> -
> +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> +		return -EAGAIN;
> +das_flip_flag:
>  		/*
>  		 * Dismantle the "old" attribute/value pair by removing
>  		 * a "remote" value (if it exists).
>  		 */
>  		xfs_attr_restore_rmt_blk(args);
>  
> +		xfs_attr_rmtval_invalidate(args);
> +das_rm_lblk:
>  		if (args->rmtblkno) {
> -			error = xfs_attr_rmtval_remove(args);
> +			error = __xfs_attr_rmtval_remove(args);
> +
> +			if (error == -EAGAIN) {
> +				dac->dela_state = XFS_DAS_RM_LBLK;
> +				return -EAGAIN;
> +			}
> +

This whole function looks like it could use more refactoring to split
out the rename case.

>  			if (error)
>  				return error;
>  		}
> @@ -799,15 +910,11 @@ xfs_attr_leaf_addname(
>  		/*
>  		 * If the result is small enough, shrink it all into the inode.
>  		 */
> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
> +		forkoff = xfs_attr_shortform_allfit(bp, dp);
> +		if (forkoff)
>  			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
> -			/* bp is gone due to xfs_da_shrink_inode */
> -			if (error)
> -				return error;
> -			error = xfs_defer_finish(&args->trans);
> -			if (error)
> -				return error;
> -		}
> +
> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>  
>  	} else if (args->rmtblkno > 0) {
>  		/*
> @@ -967,16 +1074,23 @@ xfs_attr_node_hasname(
>   *
>   * "Remote" attribute values confuse the issue and atomic rename operations
>   * add a whole extra layer of confusion on top of that.
> + *
> + * 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_node_addname(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> -	struct xfs_da_state	*state;
> -	struct xfs_da_state_blk	*blk;
> -	struct xfs_inode	*dp;
> -	struct xfs_mount	*mp;
> -	int			retval, error;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_da_state		*state = NULL;
> +	struct xfs_da_state_blk		*blk;
> +	struct xfs_inode		*dp;
> +	struct xfs_mount		*mp;
> +	int				retval = 0;
> +	int				error = 0;
>  
>  	trace_xfs_attr_node_addname(args);
>  
> @@ -985,7 +1099,21 @@ xfs_attr_node_addname(
>  	 */
>  	dp = args->dp;
>  	mp = dp->i_mount;
> -restart:
> +
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_FLIP_NFLAG:
> +		goto das_flip_flag;
> +	case XFS_DAS_FOUND_NBLK:
> +		goto das_found_nblk;
> +	case XFS_DAS_ALLOC_NODE:
> +		goto das_alloc_node;
> +	case XFS_DAS_RM_NBLK:
> +		goto das_rm_nblk;
> +	default:
> +		break;
> +	}
> +
>  	/*
>  	 * Search to see if name already exists, and get back a pointer
>  	 * to where it should go.
> @@ -1031,19 +1159,13 @@ xfs_attr_node_addname(
>  			error = xfs_attr3_leaf_to_node(args);
>  			if (error)
>  				goto out;
> -			error = xfs_defer_finish(&args->trans);
> -			if (error)
> -				goto out;
>  
>  			/*
> -			 * Commit the node conversion and start the next
> -			 * trans in the chain.
> +			 * Restart routine from the top.  No need to set  the
> +			 * state
>  			 */
> -			error = xfs_trans_roll_inode(&args->trans, dp);
> -			if (error)
> -				goto out;
> -
> -			goto restart;
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			return -EAGAIN;
>  		}
>  
>  		/*
> @@ -1055,9 +1177,7 @@ xfs_attr_node_addname(
>  		error = xfs_da3_split(state);
>  		if (error)
>  			goto out;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			goto out;
> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>  	} else {
>  		/*
>  		 * Addition succeeded, update Btree hashvals.
> @@ -1072,13 +1192,9 @@ xfs_attr_node_addname(
>  	xfs_da_state_free(state);
>  	state = NULL;
>  
> -	/*
> -	 * Commit the leaf addition or btree split and start the next
> -	 * trans in the chain.
> -	 */
> -	error = xfs_trans_roll_inode(&args->trans, dp);
> -	if (error)
> -		goto out;
> +	dac->dela_state = XFS_DAS_FOUND_NBLK;
> +	return -EAGAIN;
> +das_found_nblk:

Same deal here. Any time we have this return -EAGAIN followed by a label
pattern I think we're going to want to think about refactoring things
more first to avoid dumping it in the middle of some unnecessarily large
function.

Brian

>  
>  	/*
>  	 * If there was an out-of-line value, allocate the blocks we
> @@ -1087,7 +1203,27 @@ xfs_attr_node_addname(
>  	 * maximum size of a transaction and/or hit a deadlock.
>  	 */
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_rmtval_set(args);
> +		/* Open coded xfs_attr_rmtval_set without trans handling */
> +		error = xfs_attr_rmtval_set_init(dac);
> +		if (error)
> +			return error;
> +
> +		/*
> +		 * Roll through the "value", allocating blocks on disk as
> +		 * required.
> +		 */
> +das_alloc_node:
> +		while (dac->blkcnt > 0) {
> +			error = xfs_attr_rmtval_set_blk(dac);
> +			if (error)
> +				return error;
> +
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_ALLOC_NODE;
> +			return -EAGAIN;
> +		}
> +
> +		error = xfs_attr_rmtval_set_value(args);
>  		if (error)
>  			return error;
>  	}
> @@ -1110,18 +1246,26 @@ xfs_attr_node_addname(
>  		 * Commit the flag value change and start the next trans in
>  		 * series
>  		 */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -		if (error)
> -			goto out;
> -
> +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> +		return -EAGAIN;
> +das_flip_flag:
>  		/*
>  		 * Dismantle the "old" attribute/value pair by removing
>  		 * a "remote" value (if it exists).
>  		 */
>  		xfs_attr_restore_rmt_blk(args);
>  
> +		xfs_attr_rmtval_invalidate(args);
> +
> +das_rm_nblk:
>  		if (args->rmtblkno) {
> -			error = xfs_attr_rmtval_remove(args);
> +			error = __xfs_attr_rmtval_remove(args);
> +
> +			if (error == -EAGAIN) {
> +				dac->dela_state = XFS_DAS_RM_NBLK;
> +				return -EAGAIN;
> +			}
> +
>  			if (error)
>  				return error;
>  		}
> @@ -1139,7 +1283,6 @@ xfs_attr_node_addname(
>  		error = xfs_da3_node_lookup_int(state, &retval);
>  		if (error)
>  			goto out;
> -
>  		/*
>  		 * Remove the name and update the hashvals in the tree.
>  		 */
> @@ -1147,7 +1290,6 @@ xfs_attr_node_addname(
>  		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  		error = xfs_attr3_leaf_remove(blk->bp, args);
>  		xfs_da3_fixhashpath(state, &state->path);
> -
>  		/*
>  		 * Check to see if the tree needs to be collapsed.
>  		 */
> @@ -1155,11 +1297,9 @@ xfs_attr_node_addname(
>  			error = xfs_da3_join(state);
>  			if (error)
>  				goto out;
> -			error = xfs_defer_finish(&args->trans);
> -			if (error)
> -				goto out;
> -		}
>  
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +		}
>  	} else if (args->rmtblkno > 0) {
>  		/*
>  		 * Added a "remote" value, just clear the incomplete flag.
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 0e8ae1a..67af9d1 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -93,6 +93,16 @@ enum xfs_delattr_state {
>  				      /* Zero is uninitalized */
>  	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
>  	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
> +	XFS_DAS_ADD_LEAF,	      /* We are adding a leaf attr */
> +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
> +	XFS_DAS_LEAF_TO_NODE,	      /* Converted leaf to node */
> +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
> +	XFS_DAS_ALLOC_LEAF,	      /* We are allocating leaf blocks */
> +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
> +	XFS_DAS_RM_LBLK,	      /* A rename is removing leaf blocks */
> +	XFS_DAS_ALLOC_NODE,	      /* We are allocating node blocks */
> +	XFS_DAS_FLIP_NFLAG,	      /* Flipped node INCOMPLETE attr flag */
> +	XFS_DAS_RM_NBLK,	      /* A rename is removing node blocks */
>  };
>  
>  /*
> @@ -105,8 +115,13 @@ enum xfs_delattr_state {
>   */
>  struct xfs_delattr_context {
>  	struct xfs_da_args      *da_args;
> +	struct xfs_bmbt_irec	map;
> +	struct xfs_buf		*leaf_bp;
> +	xfs_fileoff_t		lfileoff;
>  	struct xfs_da_state     *da_state;
>  	struct xfs_da_state_blk *blk;
> +	xfs_dablk_t		lblkno;
> +	int			blkcnt;
>  	unsigned int            flags;
>  	enum xfs_delattr_state  dela_state;
>  };
> @@ -126,6 +141,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>  int xfs_attr_get(struct xfs_da_args *args);
>  int xfs_attr_set(struct xfs_da_args *args);
>  int xfs_attr_set_args(struct xfs_da_args *args);
> +int xfs_attr_set_iter(struct xfs_delattr_context *dac, struct xfs_buf **leaf_bp);
>  int xfs_has_attr(struct xfs_da_args *args);
>  int xfs_attr_remove_args(struct xfs_da_args *args);
>  int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> index f55402b..4d15f45 100644
> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> @@ -19,6 +19,7 @@
>  #include "xfs_bmap_btree.h"
>  #include "xfs_bmap.h"
>  #include "xfs_attr_sf.h"
> +#include "xfs_attr.h"
>  #include "xfs_attr_remote.h"
>  #include "xfs_attr.h"
>  #include "xfs_attr_leaf.h"
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index fd4be9d..9607fd2 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -443,7 +443,7 @@ xfs_attr_rmtval_get(
>   * Find a "hole" in the attribute address space large enough for us to drop the
>   * new attribute's value into
>   */
> -STATIC int
> +int
>  xfs_attr_rmt_find_hole(
>  	struct xfs_da_args	*args)
>  {
> @@ -470,7 +470,7 @@ xfs_attr_rmt_find_hole(
>  	return 0;
>  }
>  
> -STATIC int
> +int
>  xfs_attr_rmtval_set_value(
>  	struct xfs_da_args	*args)
>  {
> @@ -630,6 +630,71 @@ xfs_attr_rmtval_set(
>  }
>  
>  /*
> + * Find a hole for the attr and store it in the delayed attr context.  This
> + * initializes the context to roll through allocating an attr extent for a
> + * delayed attr operation
> + */
> +int
> +xfs_attr_rmtval_set_init(
> +	struct xfs_delattr_context	*dac)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_bmbt_irec		*map = &dac->map;
> +	int error;
> +
> +	dac->lblkno = 0;
> +	dac->lfileoff = 0;
> +	dac->blkcnt = 0;
> +	args->rmtblkcnt = 0;
> +	args->rmtblkno = 0;
> +	memset(map, 0, sizeof(struct xfs_bmbt_irec));
> +
> +	error = xfs_attr_rmt_find_hole(args);
> +	if (error)
> +		return error;
> +
> +	dac->blkcnt = args->rmtblkcnt;
> +	dac->lblkno = args->rmtblkno;
> +
> +	return error;
> +}
> +
> +/*
> + * Write one block of the value associated with an attribute into the
> + * out-of-line buffer that we have defined for it. This is similar to a subset
> + * of xfs_attr_rmtval_set, but records the current block to the delayed attr
> + * context, and leaves transaction handling to the caller.
> + */
> +int
> +xfs_attr_rmtval_set_blk(
> +	struct xfs_delattr_context	*dac)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	struct xfs_bmbt_irec		*map = &dac->map;
> +	int nmap;
> +	int error;
> +
> +	nmap = 1;
> +	error = xfs_bmapi_write(args->trans, dp,
> +		  (xfs_fileoff_t)dac->lblkno,
> +		  dac->blkcnt, XFS_BMAPI_ATTRFORK,
> +		  args->total, map, &nmap);
> +	if (error)
> +		return error;
> +
> +	ASSERT(nmap == 1);
> +	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
> +	       (map->br_startblock != HOLESTARTBLOCK));
> +
> +	/* roll attribute extent map forwards */
> +	dac->lblkno += map->br_blockcount;
> +	dac->blkcnt -= map->br_blockcount;
> +
> +	return 0;
> +}
> +
> +/*
>   * Remove the value associated with an attribute by deleting the
>   * out-of-line buffer that it is stored on.
>   */
> @@ -671,48 +736,6 @@ xfs_attr_rmtval_invalidate(
>  }
>  
>  /*
> - * Remove the value associated with an attribute by deleting the
> - * out-of-line buffer that it is stored on.
> - */
> -int
> -xfs_attr_rmtval_remove(
> -	struct xfs_da_args      *args)
> -{
> -	xfs_dablk_t		lblkno;
> -	int			blkcnt;
> -	int			error = 0;
> -	int			done = 0;
> -
> -	trace_xfs_attr_rmtval_remove(args);
> -
> -	error = xfs_attr_rmtval_invalidate(args);
> -	if (error)
> -		return error;
> -	/*
> -	 * Keep de-allocating extents until the remote-value region is gone.
> -	 */
> -	lblkno = args->rmtblkno;
> -	blkcnt = args->rmtblkcnt;
> -	while (!done) {
> -		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
> -				    XFS_BMAPI_ATTRFORK, 1, &done);
> -		if (error)
> -			return error;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> -
> -		/*
> -		 * Close out trans and start the next one in the chain.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -		if (error)
> -			return error;
> -	}
> -	return 0;
> -}
> -
> -/*
>   * Remove the value associated with an attribute by deleting the out-of-line
>   * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
>   * transaction and recall the function
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> index ee3337b..482dff9 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.h
> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> @@ -15,4 +15,8 @@ int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
>  		xfs_buf_flags_t incore_flags);
>  int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
>  int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
> +int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
> +int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
> +int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
> +int xfs_attr_rmtval_set_init(struct xfs_delattr_context *dac);
>  #endif /* __XFS_ATTR_REMOTE_H__ */
> diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
> index c42f90e..3e8cec5 100644
> --- a/fs/xfs/xfs_attr_inactive.c
> +++ b/fs/xfs/xfs_attr_inactive.c
> @@ -15,6 +15,7 @@
>  #include "xfs_da_format.h"
>  #include "xfs_da_btree.h"
>  #include "xfs_inode.h"
> +#include "xfs_attr.h"
>  #include "xfs_attr_remote.h"
>  #include "xfs_trans.h"
>  #include "xfs_bmap.h"
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index a4323a6..26dc8bf 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -1784,7 +1784,6 @@ DEFINE_ATTR_EVENT(xfs_attr_refillstate);
>  
>  DEFINE_ATTR_EVENT(xfs_attr_rmtval_get);
>  DEFINE_ATTR_EVENT(xfs_attr_rmtval_set);
> -DEFINE_ATTR_EVENT(xfs_attr_rmtval_remove);
>  
>  #define DEFINE_DA_EVENT(name) \
>  DEFINE_EVENT(xfs_da_class, name, \
> -- 
> 2.7.4
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-13 12:30   ` Brian Foster
@ 2020-04-14 21:35     ` Allison Collins
  2020-04-15 11:46       ` Brian Foster
  0 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-14 21:35 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs, dchinner



On 4/13/20 5:30 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:27PM -0700, Allison Collins wrote:
>> This patch modifies the attr remove routines to be delay ready. This
>> means they no longer roll or commit transactions, but instead return
>> -EAGAIN to have the calling routine roll and refresh the transaction. In
>> this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
>> uses a sort of state machine like switch to keep track of where it was
>> when EAGAIN was returned. xfs_attr_node_removename has also been
>> modified to use the switch, and a new version of xfs_attr_remove_args
>> consists of a simple loop to refresh the transaction until the operation
>> is completed.
>>
>> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
>> counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
>> helper) and then __xfs_attr_rmtval_remove. We will rename
>> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
>> done.
>>
>> This patch also adds a new struct xfs_delattr_context, which we will use
>> to keep track of the current state of an attribute operation. The new
>> xfs_delattr_state enum is used to track various operations that are in
>> progress so that we know not to repeat them, and resume where we left
>> off before EAGAIN was returned to cycle out the transaction. Other
>> members take the place of local variables that need to retain their
>> values across multiple function recalls.
>>
>> Below is a state machine diagram for attr remove operations. The
>> XFS_DAS_* states indicate places where the function would return
>> -EAGAIN, and then immediately resume from after being recalled by the
>> calling function.  States marked as a "subroutine state" indicate that
>> they belong to a subroutine, and so the calling function needs to pass
>> them back to that subroutine to allow it to finish where it left off.
>> But they otherwise do not have a role in the calling function other than
>> just passing through.
>>
>>   xfs_attr_remove_iter()
>>           XFS_DAS_RM_SHRINK     ─┐
>>           (subroutine state)     │
>>                                  │
>>           XFS_DAS_RMTVAL_REMOVE ─┤
>>           (subroutine state)     │
>>                                  └─>xfs_attr_node_removename()
>>                                                   │
>>                                                   v
>>                                           need to remove
>>                                     ┌─n──  rmt blocks?
>>                                     │             │
>>                                     │             y
>>                                     │             │
>>                                     │             v
>>                                     │  ┌─>XFS_DAS_RMTVAL_REMOVE
>>                                     │  │          │
>>                                     │  │          v
>>                                     │  └──y── more blks
>>                                     │         to remove?
>>                                     │             │
>>                                     │             n
>>                                     │             │
>>                                     │             v
>>                                     │         need to
>>                                     └─────> shrink tree? ─n─┐
>>                                                   │         │
>>                                                   y         │
>>                                                   │         │
>>                                                   v         │
>>                                           XFS_DAS_RM_SHRINK │
>>                                                   │         │
>>                                                   v         │
>>                                                  done <─────┘
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
> 
> All in all this is starting to look much more simple to me, at least in
> the remove path. ;P There's only a few states and the markers that are
> introduced are fairly straightforward, etc. Comments to follow..
> 
>>   fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
>>   fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
>>   2 files changed, 168 insertions(+), 38 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index d735570..f700976 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>>    */
>>   STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>>   STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
>> -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
>> +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>   
>>   /*
>> @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>    */
>>   STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>>   STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
>> -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
>> +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>>   				 struct xfs_da_state **state);
>>   STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
>>   STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
>>   
>> +STATIC void
>> +xfs_delattr_context_init(
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_args		*args)
>> +{
>> +	memset(dac, 0, sizeof(struct xfs_delattr_context));
>> +	dac->da_args = args;
>> +}
>> +
>>   int
>>   xfs_inode_hasattr(
>>   	struct xfs_inode	*ip)
>> @@ -356,20 +365,66 @@ xfs_has_attr(
>>    */
>>   int
>>   xfs_attr_remove_args(
>> -	struct xfs_da_args      *args)
>> +	struct xfs_da_args	*args)
>>   {
>> +	int			error = 0;
>> +	struct			xfs_delattr_context dac;
>> +
>> +	xfs_delattr_context_init(&dac, args);
>> +
>> +	do {
>> +		error = xfs_attr_remove_iter(&dac);
>> +		if (error != -EAGAIN)
>> +			break;
>> +
>> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
>> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
>> +			error = xfs_defer_finish(&args->trans);
>> +			if (error)
>> +				break;
>> +		}
>> +
>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> +		if (error)
>> +			break;
>> +	} while (true);
>> +
>> +	return error;
>> +}
>> +
>> +/*
>> + * Remove the attribute specified in @args.
>> + *
>> + * This function may return -EAGAIN to signal that the transaction needs to be
>> + * rolled.  Callers should continue calling this function until they receive a
>> + * return value other than -EAGAIN.
>> + */
>> +int
>> +xfs_attr_remove_iter(
>> +	struct xfs_delattr_context *dac)
>> +{
>> +	struct xfs_da_args	*args = dac->da_args;
>>   	struct xfs_inode	*dp = args->dp;
>>   	int			error;
>>   
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_RM_SHRINK:
>> +	case XFS_DAS_RMTVAL_REMOVE:
>> +		return xfs_attr_node_removename(dac);
>> +	default:
>> +		break;
>> +	}
>> +
> 
> Hmm.. so we're duplicating the call instead of using labels..? 

Yes, this was a suggestion made during v7.  I suspect Dave may have been 
wanting to simplify things by escaping the use of labels.  At least in 
so far as the remove path is concerned.  Though he may not have realized 
this would create a duplication call?  I will cc him here; the 
conditions for calling xfs_attr_node_removename are: the below if/else 
sequence exhausts with no successes, and defaults into the else case 
(ie: the entry condition), OR one of the above states is set (which is a 
re-entry condition)


I'm
> wondering if this can be elegantly combined with the if/else branches
> below, particularly since node format is the only situation that seems
> to require a roll here.
> 
>>   	if (!xfs_inode_hasattr(dp)) {
>>   		error = -ENOATTR;
>>   	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>>   		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>>   		error = xfs_attr_shortform_remove(args);
>>   	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -		error = xfs_attr_leaf_removename(args);
>> +		error = xfs_attr_leaf_removename(dac);
>>   	} else {
>> -		error = xfs_attr_node_removename(args);
>> +		error = xfs_attr_node_removename(dac);
>>   	}
>>   
>>   	return error;

If we want to try and combine this into if/elses with no duplication, I 
believe the simplest arrangement would look something like this:


int
xfs_attr_remove_iter(
	struct xfs_delattr_context *dac)
{
	struct xfs_da_args	*args = dac->da_args;
	struct xfs_inode	*dp = args->dp;

	if (dac->dela_state != XFS_DAS_RM_SHRINK &&
	    dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
		if (!xfs_inode_hasattr(dp)) {
			return -ENOATTR;
		} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
			return xfs_attr_shortform_remove(args);
		} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
			return xfs_attr_leaf_removename(dac);
		}
	}

	return xfs_attr_node_removename(dac);
}

Let me know what folks think of that.  I'm not always clear on where 
people stand with aesthetics. (IE, is it better to have a duplicate call 
if it gets rid of a label?  Is the solution with the least amount of LOC 
always preferable?)  This area seems simple enough maybe we can get it 
ironed out here with out another version.

IMHO I think the above code sort of obfuscates that the code flow is 
really just one if/else switch with one function that has the 
statemachine behavior.  But its not bad either if that's what people 
prefer.  I'd like to find something every can be sort of happy with.  :-)

>> @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
>>    */
>>   STATIC int
>>   xfs_attr_leaf_removename(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	struct xfs_inode	*dp;
>> -	struct xfs_buf		*bp;
>> -	int			error, forkoff;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp;
>> +	struct xfs_buf			*bp;
>> +	int				error, forkoff;
>>   
>>   	trace_xfs_attr_leaf_removename(args);
>>   
>> @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
>>   		/* bp is gone due to xfs_da_shrink_inode */
>>   		if (error)
>>   			return error;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> +
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
> 
> There's no -EAGAIN return here, is this an exit path for the remove?
I think so, maybe I can remove this and the other one you pointed out in 
patch 12 along with the other unneeded transaction handling.

> 
>>   	}
>>   	return 0;
>>   }
>> @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
>>    */
>>   STATIC int
>>   xfs_attr_node_shrink(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state     *state)
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_state		*state)
>>   {
>> -	struct xfs_inode	*dp = args->dp;
>> -	int			error, forkoff;
>> -	struct xfs_buf		*bp;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>> +	int				error, forkoff;
>> +	struct xfs_buf			*bp;
>>   
>>   	/*
>>   	 * Have to get rid of the copy of this dabuf in the state.
>> @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
>>   		if (error)
>>   			return error;
>>   
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
> 
> Same question here.
> 
>>   	} else
>>   		xfs_trans_brelse(args->trans, bp);
>>   
>> @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
>>   
>>   /*
>>    * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
>> - * the blocks are valid.  Any remote blocks will be marked incomplete.
>> + * the blocks are valid.  Any remote blocks will be marked incomplete and
>> + * invalidated.
>>    */
>>   STATIC
>>   int xfs_attr_node_removename_setup(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state	**state)
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_state		**state)
>>   {
>> +	struct xfs_da_args	*args = dac->da_args;
>>   	int			error;
>>   	struct xfs_da_state_blk	*blk;
>>   
>> @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
>>   	ASSERT(blk->bp != NULL);
>>   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   
>> +	/*
>> +	 * Store blk and state in the context incase we need to cycle out the
>> +	 * transaction
>> +	 */
>> +	dac->blk = blk;
>> +	dac->da_state = *state;
>> +
>>   	if (args->rmtblkno > 0) {
>>   		error = xfs_attr_leaf_mark_incomplete(args, *state);
>>   		if (error)
>>   			return error;
>> +
>> +		error = xfs_attr_rmtval_invalidate(args);
>> +		if (error)
>> +			return error;
> 
> Seems like this moves code, which should probably happen in a separate
> patch.
Ok, this pairs with the  xfs_attr_rmtval_remove to 
__xfs_attr_rmtval_remove below.  Basically xfs_attr_rmtval_remove is the 
combination of xfs_attr_rmtval_invalidate and __xfs_attr_rmtval_remove. 
So thats why we see xfs_attr_rmtval_remove going away and 
xfs_attr_rmtval_invalidate + __xfs_attr_rmtval_remove coming in.

How about a patch that pulls xfs_attr_rmtval_invalidate out of 
xfs_attr_rmtval_remove and into the calling functions?  I think that 
might be more clear.

> 
>>   	}
>>   
>>   	return 0;
>> @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
>>   {
>>   	int			error = 0;
>>   
>> -	error = xfs_attr_rmtval_remove(args);
>> +	/*
>> +	 * May return -EAGAIN to request that the caller recall this function
>> +	 */
>> +	error = __xfs_attr_rmtval_remove(args);
>>   	if (error)
>>   		return error;
>>   
>> @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
>>    * This will involve walking down the Btree, and may involve joining
>>    * leaf nodes and even joining intermediate nodes up to and including
>>    * the root node (a special case of an intermediate node).
>> + *
>> + * This routine is meant to function as either an inline or 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_node_removename(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> +	struct xfs_da_args	*args = dac->da_args;
>>   	struct xfs_da_state	*state;
>>   	struct xfs_da_state_blk	*blk;
>>   	int			retval, error;
>>   	struct xfs_inode	*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>> +	state = dac->da_state;
>> +	blk = dac->blk;
>> +
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_RMTVAL_REMOVE:
>> +		goto das_rmtval_remove;
>> +	case XFS_DAS_RM_SHRINK:
>> +		goto das_rm_shrink;
>> +	default:
>> +		break;
>> +	}
>>   
>> -	error = xfs_attr_node_removename_setup(args, &state);
>> +	error = xfs_attr_node_removename_setup(dac, &state);
>>   	if (error)
>>   		goto out;
>>   
>> @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
>>   	 * This is done before we remove the attribute so that we don't
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>> +
>> +das_rmtval_remove:
>> +
> 
> I wonder if we need this label just to protect the setup. Perhaps if we
> had something like:
> 
> 	/* set up the remove only once... */
> 	if (dela_state == 0)
> 		error = xfs_attr_node_removename_setup(...);
> 
> ... we could reduce another state.
> 
> We could also accomplish the same thing with an explicit state to
> indicate the setup already occurred or a new dac flag, though I'm not
> sure a flag is appropriate if it would only be used here.
> 
> Brian

Mmmm, dela_state == 0 will conflict a bit when we get into fully delayed 
attrs.  Basically when this is getting called from the delayed 
operations path, it sets dela_state to a new XFS_DAS_INIT. Because we 
have to set up args mid fight, we need the extra state to not do that 
twice.

But even without getting into that right away, what you're proposing 
only gets rid of the label.  It doesnt get rid of the state.  We still 
have to set the state to not be zero (or what ever the initial value 
is).  So we still need the unique value of  XFS_DAS_RMTVAL_REMOVE

Really what you would need here in order to do what you are describeing 
is dela_state != XFS_DAS_RMTVAL_REMOVE.  If I assume to simplify away to 
the lease amount of LOC we get this:


STATIC int
xfs_attr_node_removename(
         struct xfs_delattr_context      *dac)
{
         struct xfs_da_args      *args = dac->da_args;
         struct xfs_da_state     *state;
         struct xfs_da_state_blk *blk;
         int                     retval, error;
         struct xfs_inode        *dp = args->dp;

         trace_xfs_attr_node_removename(args);
         state = dac->da_state;
         blk = dac->blk;

         if (dac->dela_state == XFS_DAS_RM_SHRINK) {
                 goto das_rm_shrink;
         } else if (dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
                 error = xfs_attr_node_removename_setup(dac, &state);
                 if (error)
                         goto out;
         }

         /*
          * If there is an out-of-line value, de-allocate the blocks.
          * This is done before we remove the attribute so that we don't
          * overflow the maximum size of a transaction and/or hit a 
deadlock.
          */ 

         if (args->rmtblkno > 0) {
                 error = xfs_attr_node_removename_rmt(args, state);
                 if (error) {
                         if (error == -EAGAIN)
                                 dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
                         return error;
                 }
         }

.....


Let me know what folks think of this.  Again, I think I like the 
switches and the labels just because it makes it more clear where the 
jump points are, even if its more LOC.  But again, this isnt bad either 
if this is more preferable to folks.  If there's another arrangment that 
is preferable, let me know, it's not difficult to run it through the 
test cases to make sure it's functional.  It may be a faster way to hash 
out what people want to see.

Thank you again for all the reviewing!!!

Allison

> 
>>   	if (args->rmtblkno > 0) {
>>   		error = xfs_attr_node_removename_rmt(args, state);
>> -		if (error)
>> -			goto out;
>> +		if (error) {
>> +			if (error == -EAGAIN)
>> +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
>> +			return error;
>> +		}
>>   	}
>>   
>>   	/*
>> @@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
>>   		error = xfs_da3_join(state);
>>   		if (error)
>>   			goto out;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			goto out;
>> -		/*
>> -		 * Commit the Btree join operation and start a new trans.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -		if (error)
>> -			goto out;
>> +
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>> +		dac->dela_state = XFS_DAS_RM_SHRINK;
>> +		return -EAGAIN;
>>   	}
>>   
>> +das_rm_shrink:
>> +	dac->dela_state = XFS_DAS_RM_SHRINK;
>> +
>>   	/*
>>   	 * If the result is small enough, push it all into the inode.
>>   	 */
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>> -		error = xfs_attr_node_shrink(args, state);
>> +		error = xfs_attr_node_shrink(dac, state);
>>   
>>   	error = 0;
>>   out:
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 66575b8..0e8ae1a 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -74,6 +74,43 @@ struct xfs_attr_list_context {
>>   };
>>   
>>   
>> +/*
>> + * ========================================================================
>> + * Structure used to pass context around among the delayed routines.
>> + * ========================================================================
>> + */
>> +
>> +/*
>> + * Enum values for xfs_delattr_context.da_state
>> + *
>> + * These values are used by delayed attribute operations to keep track  of where
>> + * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
>> + * calling function to roll the transaction, and then recall the subroutine to
>> + * finish the operation.  The enum is then used by the subroutine to jump back
>> + * to where it was and resume executing where it left off.
>> + */
>> +enum xfs_delattr_state {
>> +				      /* Zero is uninitalized */
>> +	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
>> +	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
>> +};
>> +
>> +/*
>> + * Defines for xfs_delattr_context.flags
>> + */
>> +#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
>> +
>> +/*
>> + * Context used for keeping track of delayed attribute operations
>> + */
>> +struct xfs_delattr_context {
>> +	struct xfs_da_args      *da_args;
>> +	struct xfs_da_state     *da_state;
>> +	struct xfs_da_state_blk *blk;
>> +	unsigned int            flags;
>> +	enum xfs_delattr_state  dela_state;
>> +};
>> +
>>   /*========================================================================
>>    * Function prototypes for the kernel.
>>    *========================================================================*/
>> @@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
>>   int xfs_attr_set_args(struct xfs_da_args *args);
>>   int xfs_has_attr(struct xfs_da_args *args);
>>   int xfs_attr_remove_args(struct xfs_da_args *args);
>> +int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>   bool xfs_attr_namecheck(const void *name, size_t length);
>>   
>>   #endif	/* __XFS_ATTR_H__ */
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST
  2020-04-03 22:12 ` [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST Allison Collins
  2020-04-06 14:31   ` Brian Foster
@ 2020-04-15  6:43   ` Chandan Rajendra
  1 sibling, 0 replies; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-15  6:43 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> Delayed operations cannot return error codes.  So we must check for
> these conditions first before starting set or remove operations
>

The changes are logically correct.

Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 2a0d3d3..f7e289e 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -404,6 +404,17 @@ xfs_attr_set(
>  				args->total, 0, quota_flags);
>  		if (error)
>  			goto out_trans_cancel;
> +
> +		error = xfs_has_attr(args);
> +		if (error == -EEXIST && (args->attr_flags & XATTR_CREATE))
> +			goto out_trans_cancel;
> +
> +		if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
> +			goto out_trans_cancel;
> +
> +		if (error != -ENOATTR && error != -EEXIST)
> +			goto out_trans_cancel;
> +
>  		error = xfs_attr_set_args(args);
>  		if (error)
>  			goto out_trans_cancel;
> @@ -411,6 +422,10 @@ xfs_attr_set(
>  		if (!args->trans)
>  			goto out_unlock;
>  	} else {
> +		error = xfs_has_attr(args);
> +		if (error != -EEXIST)
> +			goto out_trans_cancel;
> +
>  		error = xfs_attr_remove_args(args);
>  		if (error)
>  			goto out_trans_cancel;
> 


-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink
  2020-04-03 22:12 ` [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink Allison Collins
  2020-04-07 14:17   ` Brian Foster
@ 2020-04-15 10:16   ` Chandan Rajendra
  2020-04-15 22:13     ` Allison Collins
  1 sibling, 1 reply; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-15 10:16 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> This patch adds a new helper function xfs_attr_node_shrink used to
> shrink an attr name into an inode if it is small enough.  This helps to
> modularize the greater calling function xfs_attr_node_removename.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 67 ++++++++++++++++++++++++++++++------------------
>  1 file changed, 42 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index db5a99c..27a9bb5 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1103,6 +1103,45 @@ xfs_attr_node_addname(
>  }
>  
>  /*
> + * Shrink an attribute from leaf to shortform
> + */
> +STATIC int
> +xfs_attr_node_shrink(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state     *state)
> +{
> +	struct xfs_inode	*dp = args->dp;
> +	int			error, forkoff;
> +	struct xfs_buf		*bp;
> +
> +	/*
> +	 * Have to get rid of the copy of this dabuf in the state.
> +	 */
> +	ASSERT(state->path.active == 1);
> +	ASSERT(state->path.blk[0].bp);
> +	state->path.blk[0].bp = NULL;
> +
> +	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
> +	if (error)
> +		return error;
> +
> +	forkoff = xfs_attr_shortform_allfit(bp, dp);
> +	if (forkoff) {
> +		error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
> +		/* bp is gone due to xfs_da_shrink_inode */
> +		if (error)
> +			return error;
> +
> +		error = xfs_defer_finish(&args->trans);
> +		if (error)
> +			return error;
> +	} else
> +		xfs_trans_brelse(args->trans, bp);
> +
> +	return 0;
> +}
> +
> +/*
>   * Remove a name from a B-tree attribute list.
>   *
>   * This will involve walking down the Btree, and may involve joining
> @@ -1115,8 +1154,7 @@ xfs_attr_node_removename(
>  {
>  	struct xfs_da_state	*state;
>  	struct xfs_da_state_blk	*blk;
> -	struct xfs_buf		*bp;
> -	int			retval, error, forkoff;
> +	int			retval, error;
>  	struct xfs_inode	*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
> @@ -1197,31 +1235,10 @@ xfs_attr_node_removename(
>  	/*
>  	 * If the result is small enough, push it all into the inode.
>  	 */
> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		/*
> -		 * Have to get rid of the copy of this dabuf in the state.
> -		 */
> -		ASSERT(state->path.active == 1);
> -		ASSERT(state->path.blk[0].bp);
> -		state->path.blk[0].bp = NULL;
> -
> -		error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
> -		if (error)
> -			goto out;
> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> +		error = xfs_attr_node_shrink(args, state);

If a non-zero error value is returned by the above statement, the following
statement i.e. "error = 0" will overwrite it.
Apart from that everything else looks fine.

Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

>  
> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
> -			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
> -			/* bp is gone due to xfs_da_shrink_inode */
> -			if (error)
> -				goto out;
> -			error = xfs_defer_finish(&args->trans);
> -			if (error)
> -				goto out;
> -		} else
> -			xfs_trans_brelse(args->trans, bp);
> -	}
>  	error = 0;
> -
>  out:
>  	if (state)
>  		xfs_da_state_free(state);
> 


-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-14 21:35     ` Allison Collins
@ 2020-04-15 11:46       ` Brian Foster
  2020-04-16  3:17         ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-15 11:46 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs, dchinner

On Tue, Apr 14, 2020 at 02:35:43PM -0700, Allison Collins wrote:
> 
> 
> On 4/13/20 5:30 AM, Brian Foster wrote:
> > On Fri, Apr 03, 2020 at 03:12:27PM -0700, Allison Collins wrote:
> > > This patch modifies the attr remove routines to be delay ready. This
> > > means they no longer roll or commit transactions, but instead return
> > > -EAGAIN to have the calling routine roll and refresh the transaction. In
> > > this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
> > > uses a sort of state machine like switch to keep track of where it was
> > > when EAGAIN was returned. xfs_attr_node_removename has also been
> > > modified to use the switch, and a new version of xfs_attr_remove_args
> > > consists of a simple loop to refresh the transaction until the operation
> > > is completed.
> > > 
> > > Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> > > counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
> > > helper) and then __xfs_attr_rmtval_remove. We will rename
> > > __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> > > done.
> > > 
> > > This patch also adds a new struct xfs_delattr_context, which we will use
> > > to keep track of the current state of an attribute operation. The new
> > > xfs_delattr_state enum is used to track various operations that are in
> > > progress so that we know not to repeat them, and resume where we left
> > > off before EAGAIN was returned to cycle out the transaction. Other
> > > members take the place of local variables that need to retain their
> > > values across multiple function recalls.
> > > 
> > > Below is a state machine diagram for attr remove operations. The
> > > XFS_DAS_* states indicate places where the function would return
> > > -EAGAIN, and then immediately resume from after being recalled by the
> > > calling function.  States marked as a "subroutine state" indicate that
> > > they belong to a subroutine, and so the calling function needs to pass
> > > them back to that subroutine to allow it to finish where it left off.
> > > But they otherwise do not have a role in the calling function other than
> > > just passing through.
> > > 
> > >   xfs_attr_remove_iter()
> > >           XFS_DAS_RM_SHRINK     ─┐
> > >           (subroutine state)     │
> > >                                  │
> > >           XFS_DAS_RMTVAL_REMOVE ─┤
> > >           (subroutine state)     │
> > >                                  └─>xfs_attr_node_removename()
> > >                                                   │
> > >                                                   v
> > >                                           need to remove
> > >                                     ┌─n──  rmt blocks?
> > >                                     │             │
> > >                                     │             y
> > >                                     │             │
> > >                                     │             v
> > >                                     │  ┌─>XFS_DAS_RMTVAL_REMOVE
> > >                                     │  │          │
> > >                                     │  │          v
> > >                                     │  └──y── more blks
> > >                                     │         to remove?
> > >                                     │             │
> > >                                     │             n
> > >                                     │             │
> > >                                     │             v
> > >                                     │         need to
> > >                                     └─────> shrink tree? ─n─┐
> > >                                                   │         │
> > >                                                   y         │
> > >                                                   │         │
> > >                                                   v         │
> > >                                           XFS_DAS_RM_SHRINK │
> > >                                                   │         │
> > >                                                   v         │
> > >                                                  done <─────┘
> > > 
> > > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > > ---
> > 
> > All in all this is starting to look much more simple to me, at least in
> > the remove path. ;P There's only a few states and the markers that are
> > introduced are fairly straightforward, etc. Comments to follow..
> > 
> > >   fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
> > >   fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
> > >   2 files changed, 168 insertions(+), 38 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index d735570..f700976 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
> > >    */
> > >   STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
> > >   STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> > > -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
> > > +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
> > >   STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > >   /*
> > > @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > >    */
> > >   STATIC int xfs_attr_node_get(xfs_da_args_t *args);
> > >   STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> > > -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
> > > +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
> > >   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
> > >   				 struct xfs_da_state **state);
> > >   STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
> > >   STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
> > > +STATIC void
> > > +xfs_delattr_context_init(
> > > +	struct xfs_delattr_context	*dac,
> > > +	struct xfs_da_args		*args)
> > > +{
> > > +	memset(dac, 0, sizeof(struct xfs_delattr_context));
> > > +	dac->da_args = args;
> > > +}
> > > +
> > >   int
> > >   xfs_inode_hasattr(
> > >   	struct xfs_inode	*ip)
> > > @@ -356,20 +365,66 @@ xfs_has_attr(
> > >    */
> > >   int
> > >   xfs_attr_remove_args(
> > > -	struct xfs_da_args      *args)
> > > +	struct xfs_da_args	*args)
> > >   {
> > > +	int			error = 0;
> > > +	struct			xfs_delattr_context dac;
> > > +
> > > +	xfs_delattr_context_init(&dac, args);
> > > +
> > > +	do {
> > > +		error = xfs_attr_remove_iter(&dac);
> > > +		if (error != -EAGAIN)
> > > +			break;
> > > +
> > > +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> > > +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> > > +			error = xfs_defer_finish(&args->trans);
> > > +			if (error)
> > > +				break;
> > > +		}
> > > +
> > > +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > +		if (error)
> > > +			break;
> > > +	} while (true);
> > > +
> > > +	return error;
> > > +}
> > > +
> > > +/*
> > > + * Remove the attribute specified in @args.
> > > + *
> > > + * This function may return -EAGAIN to signal that the transaction needs to be
> > > + * rolled.  Callers should continue calling this function until they receive a
> > > + * return value other than -EAGAIN.
> > > + */
> > > +int
> > > +xfs_attr_remove_iter(
> > > +	struct xfs_delattr_context *dac)
> > > +{
> > > +	struct xfs_da_args	*args = dac->da_args;
> > >   	struct xfs_inode	*dp = args->dp;
> > >   	int			error;
> > > +	/* State machine switch */
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_RM_SHRINK:
> > > +	case XFS_DAS_RMTVAL_REMOVE:
> > > +		return xfs_attr_node_removename(dac);
> > > +	default:
> > > +		break;
> > > +	}
> > > +
> > 
> > Hmm.. so we're duplicating the call instead of using labels..?
> 
> Yes, this was a suggestion made during v7.  I suspect Dave may have been
> wanting to simplify things by escaping the use of labels.  At least in so
> far as the remove path is concerned.  Though he may not have realized this
> would create a duplication call?  I will cc him here; the conditions for
> calling xfs_attr_node_removename are: the below if/else sequence exhausts
> with no successes, and defaults into the else case (ie: the entry
> condition), OR one of the above states is set (which is a re-entry
> condition)
> 

Ok.

> 
> I'm
> > wondering if this can be elegantly combined with the if/else branches
> > below, particularly since node format is the only situation that seems
> > to require a roll here.
> > 
> > >   	if (!xfs_inode_hasattr(dp)) {
> > >   		error = -ENOATTR;
> > >   	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
> > >   		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
> > >   		error = xfs_attr_shortform_remove(args);
> > >   	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > -		error = xfs_attr_leaf_removename(args);
> > > +		error = xfs_attr_leaf_removename(dac);
> > >   	} else {
> > > -		error = xfs_attr_node_removename(args);
> > > +		error = xfs_attr_node_removename(dac);
> > >   	}
> > >   	return error;
> 
> If we want to try and combine this into if/elses with no duplication, I
> believe the simplest arrangement would look something like this:
> 
> 
> int
> xfs_attr_remove_iter(
> 	struct xfs_delattr_context *dac)
> {
> 	struct xfs_da_args	*args = dac->da_args;
> 	struct xfs_inode	*dp = args->dp;
> 
> 	if (dac->dela_state != XFS_DAS_RM_SHRINK &&
> 	    dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
> 		if (!xfs_inode_hasattr(dp)) {
> 			return -ENOATTR;
> 		} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
> 			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
> 			return xfs_attr_shortform_remove(args);
> 		} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> 			return xfs_attr_leaf_removename(dac);
> 		}
> 	}
> 
> 	return xfs_attr_node_removename(dac);
> }
> 
> Let me know what folks think of that.  I'm not always clear on where people
> stand with aesthetics. (IE, is it better to have a duplicate call if it gets
> rid of a label?  Is the solution with the least amount of LOC always
> preferable?)  This area seems simple enough maybe we can get it ironed out
> here with out another version.
> 
> IMHO I think the above code sort of obfuscates that the code flow is really
> just one if/else switch with one function that has the statemachine
> behavior.  But its not bad either if that's what people prefer.  I'd like to
> find something every can be sort of happy with.  :-)
> 

If you want my .02, some combination of the above is cleanest from an
aesthetic pov:

{
	...
	if (RM_SHRINK || RMTVAL_REMOVE)
		goto node;

	if (!hasattr)
		return -ENOATTR;
	else if (local)
		return shortform_remove();
	else if (oneblock)
		return leaf_removename();

node:
	return node_removename();
}

I find that easiest to read at a glance, but I don't feel terribly
strongly about it I guess.

> > > @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
> > >    */
> > >   STATIC int
> > >   xfs_attr_leaf_removename(
> > > -	struct xfs_da_args	*args)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	struct xfs_inode	*dp;
> > > -	struct xfs_buf		*bp;
> > > -	int			error, forkoff;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_inode		*dp;
> > > +	struct xfs_buf			*bp;
> > > +	int				error, forkoff;
> > >   	trace_xfs_attr_leaf_removename(args);
> > > @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
> > >   		/* bp is gone due to xfs_da_shrink_inode */
> > >   		if (error)
> > >   			return error;
> > > -		error = xfs_defer_finish(&args->trans);
> > > -		if (error)
> > > -			return error;
> > > +
> > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > 
> > There's no -EAGAIN return here, is this an exit path for the remove?
> I think so, maybe I can remove this and the other one you pointed out in
> patch 12 along with the other unneeded transaction handling.
> 
> > 
> > >   	}
> > >   	return 0;
> > >   }
> > > @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
> > >    */
> > >   STATIC int
> > >   xfs_attr_node_shrink(
> > > -	struct xfs_da_args	*args,
> > > -	struct xfs_da_state     *state)
> > > +	struct xfs_delattr_context	*dac,
> > > +	struct xfs_da_state		*state)
> > >   {
> > > -	struct xfs_inode	*dp = args->dp;
> > > -	int			error, forkoff;
> > > -	struct xfs_buf		*bp;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_inode		*dp = args->dp;
> > > +	int				error, forkoff;
> > > +	struct xfs_buf			*bp;
> > >   	/*
> > >   	 * Have to get rid of the copy of this dabuf in the state.
> > > @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
> > >   		if (error)
> > >   			return error;
> > > -		error = xfs_defer_finish(&args->trans);
> > > -		if (error)
> > > -			return error;
> > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > 
> > Same question here.
> > 
> > >   	} else
> > >   		xfs_trans_brelse(args->trans, bp);
> > > @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
> > >   /*
> > >    * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
> > > - * the blocks are valid.  Any remote blocks will be marked incomplete.
> > > + * the blocks are valid.  Any remote blocks will be marked incomplete and
> > > + * invalidated.
> > >    */
> > >   STATIC
> > >   int xfs_attr_node_removename_setup(
> > > -	struct xfs_da_args	*args,
> > > -	struct xfs_da_state	**state)
> > > +	struct xfs_delattr_context	*dac,
> > > +	struct xfs_da_state		**state)
> > >   {
> > > +	struct xfs_da_args	*args = dac->da_args;
> > >   	int			error;
> > >   	struct xfs_da_state_blk	*blk;
> > > @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
> > >   	ASSERT(blk->bp != NULL);
> > >   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> > > +	/*
> > > +	 * Store blk and state in the context incase we need to cycle out the
> > > +	 * transaction
> > > +	 */
> > > +	dac->blk = blk;
> > > +	dac->da_state = *state;
> > > +
> > >   	if (args->rmtblkno > 0) {
> > >   		error = xfs_attr_leaf_mark_incomplete(args, *state);
> > >   		if (error)
> > >   			return error;
> > > +
> > > +		error = xfs_attr_rmtval_invalidate(args);
> > > +		if (error)
> > > +			return error;
> > 
> > Seems like this moves code, which should probably happen in a separate
> > patch.
> Ok, this pairs with the  xfs_attr_rmtval_remove to __xfs_attr_rmtval_remove
> below.  Basically xfs_attr_rmtval_remove is the combination of
> xfs_attr_rmtval_invalidate and __xfs_attr_rmtval_remove. So thats why we see
> xfs_attr_rmtval_remove going away and xfs_attr_rmtval_invalidate +
> __xfs_attr_rmtval_remove coming in.
> 
> How about a patch that pulls xfs_attr_rmtval_invalidate out of
> xfs_attr_rmtval_remove and into the calling functions?  I think that might
> be more clear.
> 

Yes, separate patch please. I think that if the earlier refactoring
parts of the series are split out properly (i.e., no dependencies on
subsequent patches) and reviewed, perhaps we can start getting some of
those patches merged while the latter bits are worked out.

> > 
> > >   	}
> > >   	return 0;
> > > @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
> > >   {
> > >   	int			error = 0;
> > > -	error = xfs_attr_rmtval_remove(args);
> > > +	/*
> > > +	 * May return -EAGAIN to request that the caller recall this function
> > > +	 */
> > > +	error = __xfs_attr_rmtval_remove(args);
> > >   	if (error)
> > >   		return error;
> > > @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
> > >    * This will involve walking down the Btree, and may involve joining
> > >    * leaf nodes and even joining intermediate nodes up to and including
> > >    * the root node (a special case of an intermediate node).
> > > + *
> > > + * This routine is meant to function as either an inline or 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_node_removename(
> > > -	struct xfs_da_args	*args)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > +	struct xfs_da_args	*args = dac->da_args;
> > >   	struct xfs_da_state	*state;
> > >   	struct xfs_da_state_blk	*blk;
> > >   	int			retval, error;
> > >   	struct xfs_inode	*dp = args->dp;
> > >   	trace_xfs_attr_node_removename(args);
> > > +	state = dac->da_state;
> > > +	blk = dac->blk;
> > > +
> > > +	/* State machine switch */
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_RMTVAL_REMOVE:
> > > +		goto das_rmtval_remove;
> > > +	case XFS_DAS_RM_SHRINK:
> > > +		goto das_rm_shrink;
> > > +	default:
> > > +		break;
> > > +	}
> > > -	error = xfs_attr_node_removename_setup(args, &state);
> > > +	error = xfs_attr_node_removename_setup(dac, &state);
> > >   	if (error)
> > >   		goto out;
> > > @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
> > >   	 * This is done before we remove the attribute so that we don't
> > >   	 * overflow the maximum size of a transaction and/or hit a deadlock.
> > >   	 */
> > > +
> > > +das_rmtval_remove:
> > > +
> > 
> > I wonder if we need this label just to protect the setup. Perhaps if we
> > had something like:
> > 
> > 	/* set up the remove only once... */
> > 	if (dela_state == 0)
> > 		error = xfs_attr_node_removename_setup(...);
> > 
> > ... we could reduce another state.
> > 
> > We could also accomplish the same thing with an explicit state to
> > indicate the setup already occurred or a new dac flag, though I'm not
> > sure a flag is appropriate if it would only be used here.
> > 
> > Brian
> 
> Mmmm, dela_state == 0 will conflict a bit when we get into fully delayed
> attrs.  Basically when this is getting called from the delayed operations
> path, it sets dela_state to a new XFS_DAS_INIT. Because we have to set up
> args mid fight, we need the extra state to not do that twice.
> 

Can we address that when the conflict is introduced?

> But even without getting into that right away, what you're proposing only
> gets rid of the label.  It doesnt get rid of the state.  We still have to
> set the state to not be zero (or what ever the initial value is).  So we
> still need the unique value of  XFS_DAS_RMTVAL_REMOVE
> 

Yeah, I was partly thinking of the setup call being tied to a flag
rather than a state. That way the logic is something like the typical:

	if (!setup)
		do_setup();
	...

... and it's one less bit of code tied into the state machine. All in
all, it's more that having a label right at the top of a function like
that kind of looks like it's asking for some form of simplification.

> Really what you would need here in order to do what you are describeing is
> dela_state != XFS_DAS_RMTVAL_REMOVE.  If I assume to simplify away to the
> lease amount of LOC we get this:
> 
> 
> STATIC int
> xfs_attr_node_removename(
>         struct xfs_delattr_context      *dac)
> {
>         struct xfs_da_args      *args = dac->da_args;
>         struct xfs_da_state     *state;
>         struct xfs_da_state_blk *blk;
>         int                     retval, error;
>         struct xfs_inode        *dp = args->dp;
> 
>         trace_xfs_attr_node_removename(args);
>         state = dac->da_state;
>         blk = dac->blk;
> 
>         if (dac->dela_state == XFS_DAS_RM_SHRINK) {
>                 goto das_rm_shrink;
>         } else if (dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
>                 error = xfs_attr_node_removename_setup(dac, &state);
>                 if (error)
>                         goto out;
>         }
> 
>         /*
>          * If there is an out-of-line value, de-allocate the blocks.
>          * This is done before we remove the attribute so that we don't
>          * overflow the maximum size of a transaction and/or hit a deadlock.
>          */
> 
>         if (args->rmtblkno > 0) {
>                 error = xfs_attr_node_removename_rmt(args, state);
>                 if (error) {
>                         if (error == -EAGAIN)
>                                 dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
>                         return error;
>                 }
>         }
> 
> .....
> 
> 
> Let me know what folks think of this.  Again, I think I like the switches
> and the labels just because it makes it more clear where the jump points
> are, even if its more LOC.  But again, this isnt bad either if this is more
> preferable to folks.  If there's another arrangment that is preferable, let
> me know, it's not difficult to run it through the test cases to make sure
> it's functional.  It may be a faster way to hash out what people want to
> see.
> 

I prefer to see the state management stuff as boilerplate as possible.
The above pattern of creating separate reentry calls to the same
functions is not nearly as clear to me, particularly in this instance
where we have multiple branches of reentry logic (as opposed to the
earlier example of only one).

IOW, I agree that the jumps are preferable and more intuitive. I'm just
trying to be reductive by considering what could be factored out vs.
trying to fundamentally rework the approach or aggressively reduce LOC
or anything like that. IMO, simplicity of the code is usually top
priority.

Brian

> Thank you again for all the reviewing!!!
> 
> Allison
> 
> > 
> > >   	if (args->rmtblkno > 0) {
> > >   		error = xfs_attr_node_removename_rmt(args, state);
> > > -		if (error)
> > > -			goto out;
> > > +		if (error) {
> > > +			if (error == -EAGAIN)
> > > +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
> > > +			return error;
> > > +		}
> > >   	}
> > >   	/*
> > > @@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
> > >   		error = xfs_da3_join(state);
> > >   		if (error)
> > >   			goto out;
> > > -		error = xfs_defer_finish(&args->trans);
> > > -		if (error)
> > > -			goto out;
> > > -		/*
> > > -		 * Commit the Btree join operation and start a new trans.
> > > -		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > -		if (error)
> > > -			goto out;
> > > +
> > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +		dac->dela_state = XFS_DAS_RM_SHRINK;
> > > +		return -EAGAIN;
> > >   	}
> > > +das_rm_shrink:
> > > +	dac->dela_state = XFS_DAS_RM_SHRINK;
> > > +
> > >   	/*
> > >   	 * If the result is small enough, push it all into the inode.
> > >   	 */
> > >   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> > > -		error = xfs_attr_node_shrink(args, state);
> > > +		error = xfs_attr_node_shrink(dac, state);
> > >   	error = 0;
> > >   out:
> > > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > > index 66575b8..0e8ae1a 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.h
> > > +++ b/fs/xfs/libxfs/xfs_attr.h
> > > @@ -74,6 +74,43 @@ struct xfs_attr_list_context {
> > >   };
> > > +/*
> > > + * ========================================================================
> > > + * Structure used to pass context around among the delayed routines.
> > > + * ========================================================================
> > > + */
> > > +
> > > +/*
> > > + * Enum values for xfs_delattr_context.da_state
> > > + *
> > > + * These values are used by delayed attribute operations to keep track  of where
> > > + * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
> > > + * calling function to roll the transaction, and then recall the subroutine to
> > > + * finish the operation.  The enum is then used by the subroutine to jump back
> > > + * to where it was and resume executing where it left off.
> > > + */
> > > +enum xfs_delattr_state {
> > > +				      /* Zero is uninitalized */
> > > +	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
> > > +	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
> > > +};
> > > +
> > > +/*
> > > + * Defines for xfs_delattr_context.flags
> > > + */
> > > +#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
> > > +
> > > +/*
> > > + * Context used for keeping track of delayed attribute operations
> > > + */
> > > +struct xfs_delattr_context {
> > > +	struct xfs_da_args      *da_args;
> > > +	struct xfs_da_state     *da_state;
> > > +	struct xfs_da_state_blk *blk;
> > > +	unsigned int            flags;
> > > +	enum xfs_delattr_state  dela_state;
> > > +};
> > > +
> > >   /*========================================================================
> > >    * Function prototypes for the kernel.
> > >    *========================================================================*/
> > > @@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
> > >   int xfs_attr_set_args(struct xfs_da_args *args);
> > >   int xfs_has_attr(struct xfs_da_args *args);
> > >   int xfs_attr_remove_args(struct xfs_da_args *args);
> > > +int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
> > >   bool xfs_attr_namecheck(const void *name, size_t length);
> > >   #endif	/* __XFS_ATTR_H__ */
> > > -- 
> > > 2.7.4
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-13 13:40   ` Brian Foster
@ 2020-04-15 22:08     ` Allison Collins
  2020-04-16 11:01       ` Brian Foster
  0 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-15 22:08 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/13/20 6:40 AM, Brian Foster wrote:
> On Fri, Apr 03, 2020 at 03:12:28PM -0700, Allison Collins wrote:
>> This patch modifies the attr set routines to be delay ready. This means
>> they no longer roll or commit transactions, but instead return -EAGAIN
>> to have the calling routine roll and refresh the transaction.  In this
>> series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
>> state machine like switch to keep track of where it was when EAGAIN was
>> returned.
>>
>> Two new helper functions have been added: xfs_attr_rmtval_set_init and
>> xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
>> xfs_attr_rmtval_set, but they store the current block in the delay attr
>> context to allow the caller to roll the transaction between allocations.
>> This helps to simplify and consolidate code used by
>> xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
>> now become a simple loop to refresh the transaction until the operation
>> is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
>> removed.
>>
>> Below is a state machine diagram for attr set operations. The XFS_DAS_*
>> states indicate places where the function would return -EAGAIN, and then
>> immediately resume from after being recalled by the calling function.
>> States marked as a "subroutine state" indicate that they belong to a
>> subroutine, and so the calling function needs to pass them back to that
>> subroutine to allow it to finish where it left off.  But they otherwise
>> do not have a role in the calling function other than just passing
>> through.
>>
>>   xfs_attr_set_iter()
>>                   │
>>                   v
>>             need to upgrade
>>            from sf to leaf? ──n─┐
>>                   │             │
>>                   y             │
>>                   │             │
>>                   V             │
>>            XFS_DAS_ADD_LEAF     │
>>                   │             │
>>                   v             │
>>    ┌──────n── fork has   <──────┘
>>    │         only 1 blk?
>>    │              │
>>    │              y
>>    │              │
>>    │              v
>>    │     xfs_attr_leaf_try_add()
>>    │              │
>>    │              v
>>    │          had enough
>>    ├──────n──   space?
>>    │              │
>>    │              y
>>    │              │
>>    │              v
>>    │      XFS_DAS_FOUND_LBLK  ──┐
>>    │                            │
>>    │      XFS_DAS_FLIP_LFLAG  ──┤
>>    │      (subroutine state)    │
>>    │                            │
>>    │      XFS_DAS_ALLOC_LEAF  ──┤
>>    │      (subroutine state)    │
>>    │                            └─>xfs_attr_leaf_addname()
>>    │                                              │
>>    │                                              v
>>    │                                ┌─────n──  need to
>>    │                                │        alloc blks?
>>    │                                │             │
>>    │                                │             y
>>    │                                │             │
>>    │                                │             v
>>    │                                │  ┌─>XFS_DAS_ALLOC_LEAF
>>    │                                │  │          │
>>    │                                │  │          v
>>    │                                │  └──y── need to alloc
>>    │                                │         more blocks?
>>    │                                │             │
>>    │                                │             n
>>    │                                │             │
>>    │                                │             v
>>    │                                │          was this
>>    │                                └────────> a rename? ──n─┐
>>    │                                              │          │
>>    │                                              y          │
>>    │                                              │          │
>>    │                                              v          │
>>    │                                        flip incomplete  │
>>    │                                            flag         │
>>    │                                              │          │
>>    │                                              v          │
>>    │                                      XFS_DAS_FLIP_LFLAG │
>>    │                                              │          │
>>    │                                              v          │
>>    │                                            remove       │
>>    │                        XFS_DAS_RM_LBLK ─> old name      │
>>    │                                 ^            │          │
>>    │                                 │            v          │
>>    │                                 └──────y── more to      │
>>    │                                            remove       │
>>    │                                              │          │
>>    │                                              n          │
>>    │                                              │          │
>>    │                                              v          │
>>    │                                             done <──────┘
>>    └────> XFS_DAS_LEAF_TO_NODE ─┐
>>                                 │
>>           XFS_DAS_FOUND_NBLK  ──┤
>>           (subroutine state)    │
>>                                 │
>>           XFS_DAS_ALLOC_NODE  ──┤
>>           (subroutine state)    │
>>                                 │
>>           XFS_DAS_FLIP_NFLAG  ──┤
>>           (subroutine state)    │
>>                                 │
>>                                 └─>xfs_attr_node_addname()
>>                                                   │
>>                                                   v
>>                                           find space to store
>>                                          attr. Split if needed
>>                                                   │
>>                                                   v
>>                                           XFS_DAS_FOUND_NBLK
>>                                                   │
>>                                                   v
>>                                     ┌─────n──  need to
>>                                     │        alloc blks?
>>                                     │             │
>>                                     │             y
>>                                     │             │
>>                                     │             v
>>                                     │  ┌─>XFS_DAS_ALLOC_NODE
>>                                     │  │          │
>>                                     │  │          v
>>                                     │  └──y── need to alloc
>>                                     │         more blocks?
>>                                     │             │
>>                                     │             n
>>                                     │             │
>>                                     │             v
>>                                     │          was this
>>                                     └────────> a rename? ──n─┐
>>                                                   │          │
>>                                                   y          │
>>                                                   │          │
>>                                                   v          │
>>                                             flip incomplete  │
>>                                                 flag         │
>>                                                   │          │
>>                                                   v          │
>>                                           XFS_DAS_FLIP_NFLAG │
>>                                                   │          │
>>                                                   v          │
>>                                                 remove       │
>>                             XFS_DAS_RM_NBLK ─> old name      │
>>                                      ^            │          │
>>                                      │            v          │
>>                                      └──────y── more to      │
>>                                                 remove       │
>>                                                   │          │
>>                                                   n          │
>>                                                   │          │
>>                                                   v          │
>>                                                  done <──────┘
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
> 
> Only a cursory pass given the previous feedback...
> 
>>   fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
>>   fs/xfs/libxfs/xfs_attr.h        |  16 ++
>>   fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
>>   fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
>>   fs/xfs/libxfs/xfs_attr_remote.h |   4 +
>>   fs/xfs/xfs_attr_inactive.c      |   1 +
>>   fs/xfs/xfs_trace.h              |   1 -
>>   7 files changed, 351 insertions(+), 167 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index f700976..c160b7a 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>>    * Internal routines when attribute list is one block.
>>    */
>>   STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>> -STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
>> +STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>   
>> @@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>    * Internal routines when attribute list is more than one block.
>>    */
>>   STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>> -STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
>> +STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>>   				 struct xfs_da_state **state);
>>   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 void
>>   xfs_delattr_context_init(
>> @@ -227,8 +228,11 @@ xfs_attr_is_shortform(
>>   
>>   /*
>>    * Attempts to set an attr in shortform, or converts the tree to leaf form if
>> - * there is not enough room.  If the attr is set, the transaction is committed
>> - * and set to NULL.
>> + * there is not enough room.  This function is meant to operate as a helper
>> + * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
>> + * that the calling function should roll the transaction, and then proceed to
>> + * add the attr in leaf form.  This subroutine does not expect to be recalled
>> + * again like the other delayed attr routines do.
>>    */
>>   STATIC int
>>   xfs_attr_set_shortform(
>> @@ -236,16 +240,16 @@ xfs_attr_set_shortform(
>>   	struct xfs_buf		**leaf_bp)
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>> -	int			error, error2 = 0;
>> +	int			error = 0;
>>   
>>   	/*
>>   	 * Try to add the attr to the attribute list in the inode.
>>   	 */
>>   	error = xfs_attr_try_sf_addname(dp, args);
>> +
>> +	/* Should only be 0, -EEXIST or ENOSPC */
>>   	if (error != -ENOSPC) {
>> -		error2 = xfs_trans_commit(args->trans);
>> -		args->trans = NULL;
>> -		return error ? error : error2;
>> +		return error;
>>   	}
>>   	/*
>>   	 * It won't fit in the shortform, transform to a leaf block.  GROT:
>> @@ -258,18 +262,10 @@ xfs_attr_set_shortform(
>>   	/*
>>   	 * 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. Once we're done rolling the transaction we
>> -	 * can release the hold and add the attr to the leaf.
>> +	 * with the write verifier.
>>   	 */
>>   	xfs_trans_bhold(args->trans, *leaf_bp);
>> -	error = xfs_defer_finish(&args->trans);
>> -	xfs_trans_bhold_release(args->trans, *leaf_bp);
>> -	if (error) {
>> -		xfs_trans_brelse(args->trans, *leaf_bp);
>> -		return error;
>> -	}
>> -
>> -	return 0;
>> +	return -EAGAIN;
>>   }
>>   
>>   /*
>> @@ -279,9 +275,83 @@ int
>>   xfs_attr_set_args(
>>   	struct xfs_da_args	*args)
>>   {
>> -	struct xfs_inode	*dp = args->dp;
>> -	struct xfs_buf          *leaf_bp = NULL;
>> -	int			error = 0;
>> +	struct xfs_buf			*leaf_bp = NULL;
>> +	int				error = 0;
>> +	struct xfs_delattr_context	dac;
>> +
>> +	xfs_delattr_context_init(&dac, args);
>> +
>> +	do {
>> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
>> +		if (error != -EAGAIN)
>> +			break;
>> +
>> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
>> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
>> +			error = xfs_defer_finish(&args->trans);
>> +			if (error)
>> +				break;
>> +		}
>> +
>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> +		if (error)
>> +			break;
>> +
>> +		if (leaf_bp) {
>> +			xfs_trans_bjoin(args->trans, leaf_bp);
>> +			xfs_trans_bhold(args->trans, leaf_bp);
>> +		}
>> +
>> +	} while (true);
>> +
>> +	return error;
>> +}
>> +
>> +/*
>> + * 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.
>> + */
>> +int
>> +xfs_attr_set_iter(
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_buf			**leaf_bp)
>> +{
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>> +	int				error = 0;
>> +	int				sf_size;
>> +
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_ADD_LEAF:
>> +		goto das_add_leaf;
>> +	case XFS_DAS_ALLOC_LEAF:
>> +	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:
>> +	case XFS_DAS_LEAF_TO_NODE:
>> +		goto das_node;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	/*
>> +	 * New inodes may not have an attribute fork yet. So set the attribute
>> +	 * fork appropriately
>> +	 */
>> +	if (XFS_IFORK_Q((args->dp)) == 0) {
>> +		sf_size = sizeof(struct xfs_attr_sf_hdr) +
>> +		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
>> +		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
>> +		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
>> +		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
>> +	}
>>   
> 
> Is this hunk moved from somewhere? If so, we should probably handle that
> in a separate patch. I think we really want these last couple of patches
> to introduce the state/markers and not much else.
Oh, this hunk is new, not moved, I believe it's been here since I picked 
up the series quite a while ago.  It actually has more to do with parent 
pointers than delayed atts.  Basically when we try to add the parent 
pointer during a create, the inode isnt fully constructed yet, so we 
have add the fork here. I will put this in a separate patch and move it 
further up the set.

> 
>>   	/*
>>   	 * If the attribute list is already in leaf format, jump straight to
>> @@ -292,40 +362,53 @@ xfs_attr_set_args(
>>   	if (xfs_attr_is_shortform(dp)) {
>>   
>>   		/*
>> -		 * If the attr was successfully set in shortform, the
>> -		 * transaction is committed and set to NULL.  Otherwise, is it
>> -		 * converted from shortform to leaf, and the transaction is
>> -		 * retained.
>> +		 * 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 || !args->trans)
>> -			return error;
>> +		error = xfs_attr_set_shortform(args, leaf_bp);
>> +		if (error == -EAGAIN) {
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			dac->dela_state = XFS_DAS_ADD_LEAF;
>> +		}
>> +		return error;
> 
> Similar to the previous patch, I wonder if we need the explicit states
> that are otherwise handled by existing inode state. For example, if the
> above returns -EAGAIN, xfs_attr_is_shortform() is no longer true on
> reentry, right? If that's the case for the other conversions, it seems
> like we might only need one state (XFS_DAS_FOUND_LBLK) for this
> function.
Ok, it looks like I can get away with out XFS_DAS_ADD_LEAF and 
XFS_DAS_LEAF_TO_NODE.

This is actually a little similar to how it was done when I was trying 
to use flags instead of the state enums a long time ago.  At the time, I 
got the impression people were concerned about the complexity and 
maintainability.  So we went back to the enums because the explicite 
jumping made it clear where the re-entry was to resume, and reduced the 
risk that a person may mistakenly introduce a change that disrupts the 
reentry flow.  Though perhaps the current arrangement of refactoring has 
made that less concerning?

> 
> BTW, that general approach might be more clear if we lifted the format
> conversions into this level from down in the format specific add
> handlers. The goal would be to make the high level flow look something
> like:
> 
> 	if (shortform) {
> 		error = sf_try_add();
> 		if (error == -ENOSPC) {
> 			shortform_to_leaf(...);
> 			...
> 			return -EAGAIN;
> 		}
Well, actually this piece was hoisted out into the helper function in 
patch 13.  So pulling this up is sort of like letting go of patch 13. :-)

The history is: initially I had tried to reduce indentation here in v6 
by taking advantage of the _add_leaf label. Because I think in v5 we 
were trying to unnest where some of the jump points were.  So in v6 I 
had: "if(!shortform) goto leaf;" Which un-nests this shortform code.

Then in the v7 review I think Dave suggested I should add the helper and 
invert the check. So now we have "if(shortform()){ call_helper(); handle 
-EAGAIN; }"

So pulling up is a bit of a circle now.  But still functional if people 
prefer it as it was.  Just need to keep track of the history so we avoid 
going around again. :-)


> 	} else if (xfs_bmap_one_block(...)) {
Mmm.... cant jump into the leaf logic right away.  We need to handle 
releasing leaf_bp which looks like it got lost in the pseudo code?  IIRC 
there is a race condition with the AIL that is resolved by holding the 
leaf buffer across the transaction roll.  So we need to check for that 
and release it upon reentry.


> 		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> 		if (error == -ENOSPC) {
> 			leaf_to_node(...);
> 			return -EAGAIN;
> 		}
Ok, so lift ENOSPC handler from xfs_attr_leaf_try_add

> 
> 		... state stuff for leaf add ...
> 	} else {
> 		error = xfs_attr_node_addname(dac);
> 	}
> 
> Hm? Of course, something like that should be incorporated via
> independent refactoring patches.
I think what this becomes right now is:

Drop patch 13
Add new patch xfs: Lift ENOSPC handler from xfs_attr_leaf_try_add

?

> 
>>   	}
>>   
>> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -		error = xfs_attr_leaf_addname(args);
>> -		if (error != -ENOSPC)
>> -			return error;
>> +das_add_leaf:
>>   
>> -		/*
>> -		 * Commit that transaction so that the node_addname()
>> -		 * call can manage its own transactions.
>> -		 */
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> +	/*
>> +	 * After a shortform to leaf conversion, we need to hold the leaf and
>> +	 * cylce out the transaction.  When we get back, we need to release
>> +	 * the leaf.
>> +	 */
>> +	if (*leaf_bp != NULL) {
>> +		xfs_trans_brelse(args->trans, *leaf_bp);
>> +		*leaf_bp = NULL;
>> +	}
>>   
>> -		/*
>> -		 * Commit the current trans (including the inode) and
>> -		 * start a new one.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -		if (error)
>> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> +		error = xfs_attr_leaf_try_add(args, *leaf_bp);
>> +		switch (error) {
>> +		case -ENOSPC:
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
>> +			return -EAGAIN;
>> +		case 0:
>> +			dac->dela_state = XFS_DAS_FOUND_LBLK;
>> +			return -EAGAIN;
>> +		default:
>>   			return error;
>> -
>> +		}
>> +das_leaf:
>> +		error = xfs_attr_leaf_addname(dac);
>> +		if (error == -ENOSPC) {
>> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
>> +			return -EAGAIN;
>> +		}
>> +		return error;
>>   	}
>> -
>> -	error = xfs_attr_node_addname(args);
>> +das_node:
>> +	error = xfs_attr_node_addname(dac);
>>   	return error;
>>   }
>>   
>> @@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
>>    *
>>    * This leaf block cannot have a "remote" value, we only call this routine
>>    * if bmap_one_block() says there is only one block (ie: no remote blks).
>> + *
>> + * 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_leaf_addname(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	int			error, forkoff;
>> -	struct xfs_buf		*bp = NULL;
>> -	struct xfs_inode	*dp = args->dp;
>> -
>> -	trace_xfs_attr_leaf_addname(args);
>> -
>> -	error = xfs_attr_leaf_try_add(args, bp);
>> -	if (error)
>> -		return error;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_buf			*bp = NULL;
>> +	int				error, forkoff;
>> +	struct xfs_inode		*dp = args->dp;
>>   
>> -	/*
>> -	 * Commit the transaction that added the attr name so that
>> -	 * later routines can manage their own transactions.
>> -	 */
>> -	error = xfs_trans_roll_inode(&args->trans, dp);
>> -	if (error)
>> -		return error;
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_FLIP_LFLAG:
>> +		goto das_flip_flag;
>> +	case XFS_DAS_ALLOC_LEAF:
>> +		goto das_alloc_leaf;
>> +	case XFS_DAS_RM_LBLK:
>> +		goto das_rm_lblk;
>> +	default:
>> +		break;
>> +	}
>>   
>>   	/*
>>   	 * If there was an out-of-line value, allocate the blocks we
>> @@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
>>   	 * maximum size of a transaction and/or hit a deadlock.
>>   	 */
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_rmtval_set(args);
>> +
>> +		/* Open coded xfs_attr_rmtval_set without trans handling */
>> +		error = xfs_attr_rmtval_set_init(dac);
>> +		if (error)
>> +			return error;
>> +
>> +		/*
>> +		 * Roll through the "value", allocating blocks on disk as
>> +		 * required.
>> +		 */
>> +das_alloc_leaf:
> 
> If we filter out the setup above, it seems like this state could be
> reduced to check for ->blkcnt > 0.
By filter out the set up, you mean to introduce the setup flag like you 
mentioned earlier? For example:
	if(flags & setup == 0){
		setup();
		flags |= setup;
	}

To be clear, if we did that, we're talking about adding an "int 
setup_flags" to the dac. And then defineing a XFS_DAC_SETUP_* scheme. 
Like a XFS_DAC_SETUP_ADD_NAME, and an XFS_DAC_SETUP_NODE_REMVE_NAME and 
so on.  Because we cant have multiple functions sharing the same set up 
flag if they ever call each others.

Is that what you mean to imply?  Otherwise I may not be clear on what 
you mean by filtering out the set up.

> 
>> +		while (dac->blkcnt > 0) {
>> +			error = xfs_attr_rmtval_set_blk(dac);
>> +			if (error)
>> +				return error;
>> +
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			dac->dela_state = XFS_DAS_ALLOC_LEAF;
>> +			return -EAGAIN;
>> +		}
>> +
>> +		error = xfs_attr_rmtval_set_value(args);
>>   		if (error)
>>   			return error;
>>   	}
>> @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
>>   		error = xfs_attr3_leaf_flipflags(args);
>>   		if (error)
>>   			return error;
>> -		/*
>> -		 * Commit the flag value change and start the next trans in
>> -		 * series.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> -		if (error)
>> -			return error;
>> -
>> +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
>> +		return -EAGAIN;
>> +das_flip_flag:
>>   		/*
>>   		 * Dismantle the "old" attribute/value pair by removing
>>   		 * a "remote" value (if it exists).
>>   		 */
>>   		xfs_attr_restore_rmt_blk(args);
>>   
>> +		xfs_attr_rmtval_invalidate(args);
>> +das_rm_lblk:
>>   		if (args->rmtblkno) {
>> -			error = xfs_attr_rmtval_remove(args);
>> +			error = __xfs_attr_rmtval_remove(args);
>> +
>> +			if (error == -EAGAIN) {
>> +				dac->dela_state = XFS_DAS_RM_LBLK;
>> +				return -EAGAIN;
>> +			}
>> +
> 
> This whole function looks like it could use more refactoring to split
> out the rename case.
Hmm, how about we take every thing inside the "if (args->op_flags & 
XFS_DA_OP_RENAME) {"  and put it in a xfs_attr_leaf_rename() helper? 
Then pull the helper into the calling function?

> 
>>   			if (error)
>>   				return error;
>>   		}
>> @@ -799,15 +910,11 @@ xfs_attr_leaf_addname(
>>   		/*
>>   		 * If the result is small enough, shrink it all into the inode.
>>   		 */
>> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
>> +		forkoff = xfs_attr_shortform_allfit(bp, dp);
>> +		if (forkoff)
>>   			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
>> -			/* bp is gone due to xfs_da_shrink_inode */
>> -			if (error)
>> -				return error;
>> -			error = xfs_defer_finish(&args->trans);
>> -			if (error)
>> -				return error;
>> -		}
>> +
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   
>>   	} else if (args->rmtblkno > 0) {
>>   		/*
>> @@ -967,16 +1074,23 @@ xfs_attr_node_hasname(
>>    *
>>    * "Remote" attribute values confuse the issue and atomic rename operations
>>    * add a whole extra layer of confusion on top of that.
>> + *
>> + * 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_node_addname(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	struct xfs_da_state	*state;
>> -	struct xfs_da_state_blk	*blk;
>> -	struct xfs_inode	*dp;
>> -	struct xfs_mount	*mp;
>> -	int			retval, error;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_da_state		*state = NULL;
>> +	struct xfs_da_state_blk		*blk;
>> +	struct xfs_inode		*dp;
>> +	struct xfs_mount		*mp;
>> +	int				retval = 0;
>> +	int				error = 0;
>>   
>>   	trace_xfs_attr_node_addname(args);
>>   
>> @@ -985,7 +1099,21 @@ xfs_attr_node_addname(
>>   	 */
>>   	dp = args->dp;
>>   	mp = dp->i_mount;
>> -restart:
>> +
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_FLIP_NFLAG:
>> +		goto das_flip_flag;
>> +	case XFS_DAS_FOUND_NBLK:
>> +		goto das_found_nblk;
>> +	case XFS_DAS_ALLOC_NODE:
>> +		goto das_alloc_node;
>> +	case XFS_DAS_RM_NBLK:
>> +		goto das_rm_nblk;
>> +	default:
>> +		break;
>> +	}
>> +
>>   	/*
>>   	 * Search to see if name already exists, and get back a pointer
>>   	 * to where it should go.
>> @@ -1031,19 +1159,13 @@ xfs_attr_node_addname(
>>   			error = xfs_attr3_leaf_to_node(args);
>>   			if (error)
>>   				goto out;
>> -			error = xfs_defer_finish(&args->trans);
>> -			if (error)
>> -				goto out;
>>   
>>   			/*
>> -			 * Commit the node conversion and start the next
>> -			 * trans in the chain.
>> +			 * Restart routine from the top.  No need to set  the
>> +			 * state
>>   			 */
>> -			error = xfs_trans_roll_inode(&args->trans, dp);
>> -			if (error)
>> -				goto out;
>> -
>> -			goto restart;
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			return -EAGAIN;
>>   		}
>>   
>>   		/*
>> @@ -1055,9 +1177,7 @@ xfs_attr_node_addname(
>>   		error = xfs_da3_split(state);
>>   		if (error)
>>   			goto out;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			goto out;
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	} else {
>>   		/*
>>   		 * Addition succeeded, update Btree hashvals.
>> @@ -1072,13 +1192,9 @@ xfs_attr_node_addname(
>>   	xfs_da_state_free(state);
>>   	state = NULL;
>>   
>> -	/*
>> -	 * Commit the leaf addition or btree split and start the next
>> -	 * trans in the chain.
>> -	 */
>> -	error = xfs_trans_roll_inode(&args->trans, dp);
>> -	if (error)
>> -		goto out;
>> +	dac->dela_state = XFS_DAS_FOUND_NBLK;
>> +	return -EAGAIN;
>> +das_found_nblk:
> 
> Same deal here. Any time we have this return -EAGAIN followed by a label
> pattern I think we're going to want to think about refactoring things
> more first to avoid dumping it in the middle of some unnecessarily large
> function.
Ok, similar pattern here too then?  Everything in "if (args->op_flags & 
XFS_DA_OP_RENAME) {" goes in a new xfs_attr_node_rename() helper?  Then 
hoist upwards?

Thanks for the reviewing!!
Allison

> 
> Brian
> 
>>   
>>   	/*
>>   	 * If there was an out-of-line value, allocate the blocks we
>> @@ -1087,7 +1203,27 @@ xfs_attr_node_addname(
>>   	 * maximum size of a transaction and/or hit a deadlock.
>>   	 */
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_rmtval_set(args);
>> +		/* Open coded xfs_attr_rmtval_set without trans handling */
>> +		error = xfs_attr_rmtval_set_init(dac);
>> +		if (error)
>> +			return error;
>> +
>> +		/*
>> +		 * Roll through the "value", allocating blocks on disk as
>> +		 * required.
>> +		 */
>> +das_alloc_node:
>> +		while (dac->blkcnt > 0) {
>> +			error = xfs_attr_rmtval_set_blk(dac);
>> +			if (error)
>> +				return error;
>> +
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			dac->dela_state = XFS_DAS_ALLOC_NODE;
>> +			return -EAGAIN;
>> +		}
>> +
>> +		error = xfs_attr_rmtval_set_value(args);
>>   		if (error)
>>   			return error;
>>   	}
>> @@ -1110,18 +1246,26 @@ xfs_attr_node_addname(
>>   		 * Commit the flag value change and start the next trans in
>>   		 * series
>>   		 */
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> -		if (error)
>> -			goto out;
>> -
>> +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
>> +		return -EAGAIN;
>> +das_flip_flag:
>>   		/*
>>   		 * Dismantle the "old" attribute/value pair by removing
>>   		 * a "remote" value (if it exists).
>>   		 */
>>   		xfs_attr_restore_rmt_blk(args);
>>   
>> +		xfs_attr_rmtval_invalidate(args);
>> +
>> +das_rm_nblk:
>>   		if (args->rmtblkno) {
>> -			error = xfs_attr_rmtval_remove(args);
>> +			error = __xfs_attr_rmtval_remove(args);
>> +
>> +			if (error == -EAGAIN) {
>> +				dac->dela_state = XFS_DAS_RM_NBLK;
>> +				return -EAGAIN;
>> +			}
>> +
>>   			if (error)
>>   				return error;
>>   		}
>> @@ -1139,7 +1283,6 @@ xfs_attr_node_addname(
>>   		error = xfs_da3_node_lookup_int(state, &retval);
>>   		if (error)
>>   			goto out;
>> -
>>   		/*
>>   		 * Remove the name and update the hashvals in the tree.
>>   		 */
>> @@ -1147,7 +1290,6 @@ xfs_attr_node_addname(
>>   		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   		error = xfs_attr3_leaf_remove(blk->bp, args);
>>   		xfs_da3_fixhashpath(state, &state->path);
>> -
>>   		/*
>>   		 * Check to see if the tree needs to be collapsed.
>>   		 */
>> @@ -1155,11 +1297,9 @@ xfs_attr_node_addname(
>>   			error = xfs_da3_join(state);
>>   			if (error)
>>   				goto out;
>> -			error = xfs_defer_finish(&args->trans);
>> -			if (error)
>> -				goto out;
>> -		}
>>   
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +		}
>>   	} else if (args->rmtblkno > 0) {
>>   		/*
>>   		 * Added a "remote" value, just clear the incomplete flag.
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 0e8ae1a..67af9d1 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -93,6 +93,16 @@ enum xfs_delattr_state {
>>   				      /* Zero is uninitalized */
>>   	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
>>   	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
>> +	XFS_DAS_ADD_LEAF,	      /* We are adding a leaf attr */
>> +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
>> +	XFS_DAS_LEAF_TO_NODE,	      /* Converted leaf to node */
>> +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
>> +	XFS_DAS_ALLOC_LEAF,	      /* We are allocating leaf blocks */
>> +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
>> +	XFS_DAS_RM_LBLK,	      /* A rename is removing leaf blocks */
>> +	XFS_DAS_ALLOC_NODE,	      /* We are allocating node blocks */
>> +	XFS_DAS_FLIP_NFLAG,	      /* Flipped node INCOMPLETE attr flag */
>> +	XFS_DAS_RM_NBLK,	      /* A rename is removing node blocks */
>>   };
>>   
>>   /*
>> @@ -105,8 +115,13 @@ enum xfs_delattr_state {
>>    */
>>   struct xfs_delattr_context {
>>   	struct xfs_da_args      *da_args;
>> +	struct xfs_bmbt_irec	map;
>> +	struct xfs_buf		*leaf_bp;
>> +	xfs_fileoff_t		lfileoff;
>>   	struct xfs_da_state     *da_state;
>>   	struct xfs_da_state_blk *blk;
>> +	xfs_dablk_t		lblkno;
>> +	int			blkcnt;
>>   	unsigned int            flags;
>>   	enum xfs_delattr_state  dela_state;
>>   };
>> @@ -126,6 +141,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>>   int xfs_attr_get(struct xfs_da_args *args);
>>   int xfs_attr_set(struct xfs_da_args *args);
>>   int xfs_attr_set_args(struct xfs_da_args *args);
>> +int xfs_attr_set_iter(struct xfs_delattr_context *dac, struct xfs_buf **leaf_bp);
>>   int xfs_has_attr(struct xfs_da_args *args);
>>   int xfs_attr_remove_args(struct xfs_da_args *args);
>>   int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
>> index f55402b..4d15f45 100644
>> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
>> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
>> @@ -19,6 +19,7 @@
>>   #include "xfs_bmap_btree.h"
>>   #include "xfs_bmap.h"
>>   #include "xfs_attr_sf.h"
>> +#include "xfs_attr.h"
>>   #include "xfs_attr_remote.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_attr_leaf.h"
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index fd4be9d..9607fd2 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>> @@ -443,7 +443,7 @@ xfs_attr_rmtval_get(
>>    * Find a "hole" in the attribute address space large enough for us to drop the
>>    * new attribute's value into
>>    */
>> -STATIC int
>> +int
>>   xfs_attr_rmt_find_hole(
>>   	struct xfs_da_args	*args)
>>   {
>> @@ -470,7 +470,7 @@ xfs_attr_rmt_find_hole(
>>   	return 0;
>>   }
>>   
>> -STATIC int
>> +int
>>   xfs_attr_rmtval_set_value(
>>   	struct xfs_da_args	*args)
>>   {
>> @@ -630,6 +630,71 @@ xfs_attr_rmtval_set(
>>   }
>>   
>>   /*
>> + * Find a hole for the attr and store it in the delayed attr context.  This
>> + * initializes the context to roll through allocating an attr extent for a
>> + * delayed attr operation
>> + */
>> +int
>> +xfs_attr_rmtval_set_init(
>> +	struct xfs_delattr_context	*dac)
>> +{
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_bmbt_irec		*map = &dac->map;
>> +	int error;
>> +
>> +	dac->lblkno = 0;
>> +	dac->lfileoff = 0;
>> +	dac->blkcnt = 0;
>> +	args->rmtblkcnt = 0;
>> +	args->rmtblkno = 0;
>> +	memset(map, 0, sizeof(struct xfs_bmbt_irec));
>> +
>> +	error = xfs_attr_rmt_find_hole(args);
>> +	if (error)
>> +		return error;
>> +
>> +	dac->blkcnt = args->rmtblkcnt;
>> +	dac->lblkno = args->rmtblkno;
>> +
>> +	return error;
>> +}
>> +
>> +/*
>> + * Write one block of the value associated with an attribute into the
>> + * out-of-line buffer that we have defined for it. This is similar to a subset
>> + * of xfs_attr_rmtval_set, but records the current block to the delayed attr
>> + * context, and leaves transaction handling to the caller.
>> + */
>> +int
>> +xfs_attr_rmtval_set_blk(
>> +	struct xfs_delattr_context	*dac)
>> +{
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>> +	struct xfs_bmbt_irec		*map = &dac->map;
>> +	int nmap;
>> +	int error;
>> +
>> +	nmap = 1;
>> +	error = xfs_bmapi_write(args->trans, dp,
>> +		  (xfs_fileoff_t)dac->lblkno,
>> +		  dac->blkcnt, XFS_BMAPI_ATTRFORK,
>> +		  args->total, map, &nmap);
>> +	if (error)
>> +		return error;
>> +
>> +	ASSERT(nmap == 1);
>> +	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
>> +	       (map->br_startblock != HOLESTARTBLOCK));
>> +
>> +	/* roll attribute extent map forwards */
>> +	dac->lblkno += map->br_blockcount;
>> +	dac->blkcnt -= map->br_blockcount;
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>>    * Remove the value associated with an attribute by deleting the
>>    * out-of-line buffer that it is stored on.
>>    */
>> @@ -671,48 +736,6 @@ xfs_attr_rmtval_invalidate(
>>   }
>>   
>>   /*
>> - * Remove the value associated with an attribute by deleting the
>> - * out-of-line buffer that it is stored on.
>> - */
>> -int
>> -xfs_attr_rmtval_remove(
>> -	struct xfs_da_args      *args)
>> -{
>> -	xfs_dablk_t		lblkno;
>> -	int			blkcnt;
>> -	int			error = 0;
>> -	int			done = 0;
>> -
>> -	trace_xfs_attr_rmtval_remove(args);
>> -
>> -	error = xfs_attr_rmtval_invalidate(args);
>> -	if (error)
>> -		return error;
>> -	/*
>> -	 * Keep de-allocating extents until the remote-value region is gone.
>> -	 */
>> -	lblkno = args->rmtblkno;
>> -	blkcnt = args->rmtblkcnt;
>> -	while (!done) {
>> -		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
>> -				    XFS_BMAPI_ATTRFORK, 1, &done);
>> -		if (error)
>> -			return error;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> -
>> -		/*
>> -		 * Close out trans and start the next one in the chain.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> -		if (error)
>> -			return error;
>> -	}
>> -	return 0;
>> -}
>> -
>> -/*
>>    * Remove the value associated with an attribute by deleting the out-of-line
>>    * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
>>    * transaction and recall the function
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
>> index ee3337b..482dff9 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.h
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
>> @@ -15,4 +15,8 @@ int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
>>   		xfs_buf_flags_t incore_flags);
>>   int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
>>   int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
>> +int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
>> +int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
>> +int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
>> +int xfs_attr_rmtval_set_init(struct xfs_delattr_context *dac);
>>   #endif /* __XFS_ATTR_REMOTE_H__ */
>> diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
>> index c42f90e..3e8cec5 100644
>> --- a/fs/xfs/xfs_attr_inactive.c
>> +++ b/fs/xfs/xfs_attr_inactive.c
>> @@ -15,6 +15,7 @@
>>   #include "xfs_da_format.h"
>>   #include "xfs_da_btree.h"
>>   #include "xfs_inode.h"
>> +#include "xfs_attr.h"
>>   #include "xfs_attr_remote.h"
>>   #include "xfs_trans.h"
>>   #include "xfs_bmap.h"
>> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
>> index a4323a6..26dc8bf 100644
>> --- a/fs/xfs/xfs_trace.h
>> +++ b/fs/xfs/xfs_trace.h
>> @@ -1784,7 +1784,6 @@ DEFINE_ATTR_EVENT(xfs_attr_refillstate);
>>   
>>   DEFINE_ATTR_EVENT(xfs_attr_rmtval_get);
>>   DEFINE_ATTR_EVENT(xfs_attr_rmtval_set);
>> -DEFINE_ATTR_EVENT(xfs_attr_rmtval_remove);
>>   
>>   #define DEFINE_DA_EVENT(name) \
>>   DEFINE_EVENT(xfs_da_class, name, \
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink
  2020-04-15 10:16   ` Chandan Rajendra
@ 2020-04-15 22:13     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-15 22:13 UTC (permalink / raw)
  To: Chandan Rajendra; +Cc: linux-xfs



On 4/15/20 3:16 AM, Chandan Rajendra wrote:
> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote:
>> This patch adds a new helper function xfs_attr_node_shrink used to
>> shrink an attr name into an inode if it is small enough.  This helps to
>> modularize the greater calling function xfs_attr_node_removename.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 67 ++++++++++++++++++++++++++++++------------------
>>   1 file changed, 42 insertions(+), 25 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index db5a99c..27a9bb5 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1103,6 +1103,45 @@ xfs_attr_node_addname(
>>   }
>>   
>>   /*
>> + * Shrink an attribute from leaf to shortform
>> + */
>> +STATIC int
>> +xfs_attr_node_shrink(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state     *state)
>> +{
>> +	struct xfs_inode	*dp = args->dp;
>> +	int			error, forkoff;
>> +	struct xfs_buf		*bp;
>> +
>> +	/*
>> +	 * Have to get rid of the copy of this dabuf in the state.
>> +	 */
>> +	ASSERT(state->path.active == 1);
>> +	ASSERT(state->path.blk[0].bp);
>> +	state->path.blk[0].bp = NULL;
>> +
>> +	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
>> +	if (error)
>> +		return error;
>> +
>> +	forkoff = xfs_attr_shortform_allfit(bp, dp);
>> +	if (forkoff) {
>> +		error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
>> +		/* bp is gone due to xfs_da_shrink_inode */
>> +		if (error)
>> +			return error;
>> +
>> +		error = xfs_defer_finish(&args->trans);
>> +		if (error)
>> +			return error;
>> +	} else
>> +		xfs_trans_brelse(args->trans, bp);
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>>    * Remove a name from a B-tree attribute list.
>>    *
>>    * This will involve walking down the Btree, and may involve joining
>> @@ -1115,8 +1154,7 @@ xfs_attr_node_removename(
>>   {
>>   	struct xfs_da_state	*state;
>>   	struct xfs_da_state_blk	*blk;
>> -	struct xfs_buf		*bp;
>> -	int			retval, error, forkoff;
>> +	int			retval, error;
>>   	struct xfs_inode	*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>> @@ -1197,31 +1235,10 @@ xfs_attr_node_removename(
>>   	/*
>>   	 * If the result is small enough, push it all into the inode.
>>   	 */
>> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -		/*
>> -		 * Have to get rid of the copy of this dabuf in the state.
>> -		 */
>> -		ASSERT(state->path.active == 1);
>> -		ASSERT(state->path.blk[0].bp);
>> -		state->path.blk[0].bp = NULL;
>> -
>> -		error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
>> -		if (error)
>> -			goto out;
>> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>> +		error = xfs_attr_node_shrink(args, state);
> 
> If a non-zero error value is returned by the above statement, the following
> statement i.e. "error = 0" will overwrite it.
> Apart from that everything else looks fine.
> 
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

Yep, I've removed the trailing error = 0; in the new v9.  Thanks for the 
review!

Allison
> 
>>   
>> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
>> -			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
>> -			/* bp is gone due to xfs_da_shrink_inode */
>> -			if (error)
>> -				goto out;
>> -			error = xfs_defer_finish(&args->trans);
>> -			if (error)
>> -				goto out;
>> -		} else
>> -			xfs_trans_brelse(args->trans, bp);
>> -	}
>>   	error = 0;
>> -
>>   out:
>>   	if (state)
>>   		xfs_da_state_free(state);
>>
> 
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-15 11:46       ` Brian Foster
@ 2020-04-16  3:17         ` Allison Collins
  2020-04-16 10:58           ` Brian Foster
  0 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-16  3:17 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs, dchinner



On 4/15/20 4:46 AM, Brian Foster wrote:
> On Tue, Apr 14, 2020 at 02:35:43PM -0700, Allison Collins wrote:
>>
>>
>> On 4/13/20 5:30 AM, Brian Foster wrote:
>>> On Fri, Apr 03, 2020 at 03:12:27PM -0700, Allison Collins wrote:
>>>> This patch modifies the attr remove routines to be delay ready. This
>>>> means they no longer roll or commit transactions, but instead return
>>>> -EAGAIN to have the calling routine roll and refresh the transaction. In
>>>> this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
>>>> uses a sort of state machine like switch to keep track of where it was
>>>> when EAGAIN was returned. xfs_attr_node_removename has also been
>>>> modified to use the switch, and a new version of xfs_attr_remove_args
>>>> consists of a simple loop to refresh the transaction until the operation
>>>> is completed.
>>>>
>>>> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
>>>> counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
>>>> helper) and then __xfs_attr_rmtval_remove. We will rename
>>>> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
>>>> done.
>>>>
>>>> This patch also adds a new struct xfs_delattr_context, which we will use
>>>> to keep track of the current state of an attribute operation. The new
>>>> xfs_delattr_state enum is used to track various operations that are in
>>>> progress so that we know not to repeat them, and resume where we left
>>>> off before EAGAIN was returned to cycle out the transaction. Other
>>>> members take the place of local variables that need to retain their
>>>> values across multiple function recalls.
>>>>
>>>> Below is a state machine diagram for attr remove operations. The
>>>> XFS_DAS_* states indicate places where the function would return
>>>> -EAGAIN, and then immediately resume from after being recalled by the
>>>> calling function.  States marked as a "subroutine state" indicate that
>>>> they belong to a subroutine, and so the calling function needs to pass
>>>> them back to that subroutine to allow it to finish where it left off.
>>>> But they otherwise do not have a role in the calling function other than
>>>> just passing through.
>>>>
>>>>    xfs_attr_remove_iter()
>>>>            XFS_DAS_RM_SHRINK     ─┐
>>>>            (subroutine state)     │
>>>>                                   │
>>>>            XFS_DAS_RMTVAL_REMOVE ─┤
>>>>            (subroutine state)     │
>>>>                                   └─>xfs_attr_node_removename()
>>>>                                                    │
>>>>                                                    v
>>>>                                            need to remove
>>>>                                      ┌─n──  rmt blocks?
>>>>                                      │             │
>>>>                                      │             y
>>>>                                      │             │
>>>>                                      │             v
>>>>                                      │  ┌─>XFS_DAS_RMTVAL_REMOVE
>>>>                                      │  │          │
>>>>                                      │  │          v
>>>>                                      │  └──y── more blks
>>>>                                      │         to remove?
>>>>                                      │             │
>>>>                                      │             n
>>>>                                      │             │
>>>>                                      │             v
>>>>                                      │         need to
>>>>                                      └─────> shrink tree? ─n─┐
>>>>                                                    │         │
>>>>                                                    y         │
>>>>                                                    │         │
>>>>                                                    v         │
>>>>                                            XFS_DAS_RM_SHRINK │
>>>>                                                    │         │
>>>>                                                    v         │
>>>>                                                   done <─────┘
>>>>
>>>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>>>> ---
>>>
>>> All in all this is starting to look much more simple to me, at least in
>>> the remove path. ;P There's only a few states and the markers that are
>>> introduced are fairly straightforward, etc. Comments to follow..
>>>
>>>>    fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
>>>>    fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
>>>>    2 files changed, 168 insertions(+), 38 deletions(-)
>>>>
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>>> index d735570..f700976 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>>> @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>>>>     */
>>>>    STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>>>>    STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
>>>> -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
>>>> +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>>>>    STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>>>    /*
>>>> @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>>>     */
>>>>    STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>>>>    STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
>>>> -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
>>>> +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>>>>    STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>>>>    				 struct xfs_da_state **state);
>>>>    STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
>>>>    STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
>>>> +STATIC void
>>>> +xfs_delattr_context_init(
>>>> +	struct xfs_delattr_context	*dac,
>>>> +	struct xfs_da_args		*args)
>>>> +{
>>>> +	memset(dac, 0, sizeof(struct xfs_delattr_context));
>>>> +	dac->da_args = args;
>>>> +}
>>>> +
>>>>    int
>>>>    xfs_inode_hasattr(
>>>>    	struct xfs_inode	*ip)
>>>> @@ -356,20 +365,66 @@ xfs_has_attr(
>>>>     */
>>>>    int
>>>>    xfs_attr_remove_args(
>>>> -	struct xfs_da_args      *args)
>>>> +	struct xfs_da_args	*args)
>>>>    {
>>>> +	int			error = 0;
>>>> +	struct			xfs_delattr_context dac;
>>>> +
>>>> +	xfs_delattr_context_init(&dac, args);
>>>> +
>>>> +	do {
>>>> +		error = xfs_attr_remove_iter(&dac);
>>>> +		if (error != -EAGAIN)
>>>> +			break;
>>>> +
>>>> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
>>>> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
>>>> +			error = xfs_defer_finish(&args->trans);
>>>> +			if (error)
>>>> +				break;
>>>> +		}
>>>> +
>>>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>> +		if (error)
>>>> +			break;
>>>> +	} while (true);
>>>> +
>>>> +	return error;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Remove the attribute specified in @args.
>>>> + *
>>>> + * This function may return -EAGAIN to signal that the transaction needs to be
>>>> + * rolled.  Callers should continue calling this function until they receive a
>>>> + * return value other than -EAGAIN.
>>>> + */
>>>> +int
>>>> +xfs_attr_remove_iter(
>>>> +	struct xfs_delattr_context *dac)
>>>> +{
>>>> +	struct xfs_da_args	*args = dac->da_args;
>>>>    	struct xfs_inode	*dp = args->dp;
>>>>    	int			error;
>>>> +	/* State machine switch */
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_RM_SHRINK:
>>>> +	case XFS_DAS_RMTVAL_REMOVE:
>>>> +		return xfs_attr_node_removename(dac);
>>>> +	default:
>>>> +		break;
>>>> +	}
>>>> +
>>>
>>> Hmm.. so we're duplicating the call instead of using labels..?
>>
>> Yes, this was a suggestion made during v7.  I suspect Dave may have been
>> wanting to simplify things by escaping the use of labels.  At least in so
>> far as the remove path is concerned.  Though he may not have realized this
>> would create a duplication call?  I will cc him here; the conditions for
>> calling xfs_attr_node_removename are: the below if/else sequence exhausts
>> with no successes, and defaults into the else case (ie: the entry
>> condition), OR one of the above states is set (which is a re-entry
>> condition)
>>
> 
> Ok.
> 
>>
>> I'm
>>> wondering if this can be elegantly combined with the if/else branches
>>> below, particularly since node format is the only situation that seems
>>> to require a roll here.
>>>
>>>>    	if (!xfs_inode_hasattr(dp)) {
>>>>    		error = -ENOATTR;
>>>>    	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>>>>    		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>>>>    		error = xfs_attr_shortform_remove(args);
>>>>    	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>> -		error = xfs_attr_leaf_removename(args);
>>>> +		error = xfs_attr_leaf_removename(dac);
>>>>    	} else {
>>>> -		error = xfs_attr_node_removename(args);
>>>> +		error = xfs_attr_node_removename(dac);
>>>>    	}
>>>>    	return error;
>>
>> If we want to try and combine this into if/elses with no duplication, I
>> believe the simplest arrangement would look something like this:
>>
>>
>> int
>> xfs_attr_remove_iter(
>> 	struct xfs_delattr_context *dac)
>> {
>> 	struct xfs_da_args	*args = dac->da_args;
>> 	struct xfs_inode	*dp = args->dp;
>>
>> 	if (dac->dela_state != XFS_DAS_RM_SHRINK &&
>> 	    dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
>> 		if (!xfs_inode_hasattr(dp)) {
>> 			return -ENOATTR;
>> 		} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>> 			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>> 			return xfs_attr_shortform_remove(args);
>> 		} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> 			return xfs_attr_leaf_removename(dac);
>> 		}
>> 	}
>>
>> 	return xfs_attr_node_removename(dac);
>> }
>>
>> Let me know what folks think of that.  I'm not always clear on where people
>> stand with aesthetics. (IE, is it better to have a duplicate call if it gets
>> rid of a label?  Is the solution with the least amount of LOC always
>> preferable?)  This area seems simple enough maybe we can get it ironed out
>> here with out another version.
>>
>> IMHO I think the above code sort of obfuscates that the code flow is really
>> just one if/else switch with one function that has the statemachine
>> behavior.  But its not bad either if that's what people prefer.  I'd like to
>> find something every can be sort of happy with.  :-)
>>
> 
> If you want my .02, some combination of the above is cleanest from an
> aesthetic pov:
> 
> {
> 	...
> 	if (RM_SHRINK || RMTVAL_REMOVE)
> 		goto node;
> 
> 	if (!hasattr)
> 		return -ENOATTR;
> 	else if (local)
> 		return shortform_remove();
> 	else if (oneblock)
> 		return leaf_removename();
> 
> node:
> 	return node_removename();
> }
> 
> I find that easiest to read at a glance, but I don't feel terribly
> strongly about it I guess.

I am entirely fine with that if everyone else is :-)

> 
>>>> @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
>>>>     */
>>>>    STATIC int
>>>>    xfs_attr_leaf_removename(
>>>> -	struct xfs_da_args	*args)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> -	struct xfs_inode	*dp;
>>>> -	struct xfs_buf		*bp;
>>>> -	int			error, forkoff;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_inode		*dp;
>>>> +	struct xfs_buf			*bp;
>>>> +	int				error, forkoff;
>>>>    	trace_xfs_attr_leaf_removename(args);
>>>> @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
>>>>    		/* bp is gone due to xfs_da_shrink_inode */
>>>>    		if (error)
>>>>    			return error;
>>>> -		error = xfs_defer_finish(&args->trans);
>>>> -		if (error)
>>>> -			return error;
>>>> +
>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>
>>> There's no -EAGAIN return here, is this an exit path for the remove?
>> I think so, maybe I can remove this and the other one you pointed out in
>> patch 12 along with the other unneeded transaction handling.
>>
>>>
>>>>    	}
>>>>    	return 0;
>>>>    }
>>>> @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
>>>>     */
>>>>    STATIC int
>>>>    xfs_attr_node_shrink(
>>>> -	struct xfs_da_args	*args,
>>>> -	struct xfs_da_state     *state)
>>>> +	struct xfs_delattr_context	*dac,
>>>> +	struct xfs_da_state		*state)
>>>>    {
>>>> -	struct xfs_inode	*dp = args->dp;
>>>> -	int			error, forkoff;
>>>> -	struct xfs_buf		*bp;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_inode		*dp = args->dp;
>>>> +	int				error, forkoff;
>>>> +	struct xfs_buf			*bp;
>>>>    	/*
>>>>    	 * Have to get rid of the copy of this dabuf in the state.
>>>> @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
>>>>    		if (error)
>>>>    			return error;
>>>> -		error = xfs_defer_finish(&args->trans);
>>>> -		if (error)
>>>> -			return error;
>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>
>>> Same question here.
>>>
>>>>    	} else
>>>>    		xfs_trans_brelse(args->trans, bp);
>>>> @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
>>>>    /*
>>>>     * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
>>>> - * the blocks are valid.  Any remote blocks will be marked incomplete.
>>>> + * the blocks are valid.  Any remote blocks will be marked incomplete and
>>>> + * invalidated.
>>>>     */
>>>>    STATIC
>>>>    int xfs_attr_node_removename_setup(
>>>> -	struct xfs_da_args	*args,
>>>> -	struct xfs_da_state	**state)
>>>> +	struct xfs_delattr_context	*dac,
>>>> +	struct xfs_da_state		**state)
>>>>    {
>>>> +	struct xfs_da_args	*args = dac->da_args;
>>>>    	int			error;
>>>>    	struct xfs_da_state_blk	*blk;
>>>> @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
>>>>    	ASSERT(blk->bp != NULL);
>>>>    	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>>> +	/*
>>>> +	 * Store blk and state in the context incase we need to cycle out the
>>>> +	 * transaction
>>>> +	 */
>>>> +	dac->blk = blk;
>>>> +	dac->da_state = *state;
>>>> +
>>>>    	if (args->rmtblkno > 0) {
>>>>    		error = xfs_attr_leaf_mark_incomplete(args, *state);
>>>>    		if (error)
>>>>    			return error;
>>>> +
>>>> +		error = xfs_attr_rmtval_invalidate(args);
>>>> +		if (error)
>>>> +			return error;
>>>
>>> Seems like this moves code, which should probably happen in a separate
>>> patch.
>> Ok, this pairs with the  xfs_attr_rmtval_remove to __xfs_attr_rmtval_remove
>> below.  Basically xfs_attr_rmtval_remove is the combination of
>> xfs_attr_rmtval_invalidate and __xfs_attr_rmtval_remove. So thats why we see
>> xfs_attr_rmtval_remove going away and xfs_attr_rmtval_invalidate +
>> __xfs_attr_rmtval_remove coming in.
>>
>> How about a patch that pulls xfs_attr_rmtval_invalidate out of
>> xfs_attr_rmtval_remove and into the calling functions?  I think that might
>> be more clear.
>>
> 
> Yes, separate patch please. I think that if the earlier refactoring
> parts of the series are split out properly (i.e., no dependencies on
> subsequent patches) and reviewed, perhaps we can start getting some of
> those patches merged while the latter bits are worked out.
Ok then, will do

> 
>>>
>>>>    	}
>>>>    	return 0;
>>>> @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
>>>>    {
>>>>    	int			error = 0;
>>>> -	error = xfs_attr_rmtval_remove(args);
>>>> +	/*
>>>> +	 * May return -EAGAIN to request that the caller recall this function
>>>> +	 */
>>>> +	error = __xfs_attr_rmtval_remove(args);
>>>>    	if (error)
>>>>    		return error;
>>>> @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
>>>>     * This will involve walking down the Btree, and may involve joining
>>>>     * leaf nodes and even joining intermediate nodes up to and including
>>>>     * the root node (a special case of an intermediate node).
>>>> + *
>>>> + * This routine is meant to function as either an inline or 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_node_removename(
>>>> -	struct xfs_da_args	*args)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> +	struct xfs_da_args	*args = dac->da_args;
>>>>    	struct xfs_da_state	*state;
>>>>    	struct xfs_da_state_blk	*blk;
>>>>    	int			retval, error;
>>>>    	struct xfs_inode	*dp = args->dp;
>>>>    	trace_xfs_attr_node_removename(args);
>>>> +	state = dac->da_state;
>>>> +	blk = dac->blk;
>>>> +
>>>> +	/* State machine switch */
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_RMTVAL_REMOVE:
>>>> +		goto das_rmtval_remove;
>>>> +	case XFS_DAS_RM_SHRINK:
>>>> +		goto das_rm_shrink;
>>>> +	default:
>>>> +		break;
>>>> +	}
>>>> -	error = xfs_attr_node_removename_setup(args, &state);
>>>> +	error = xfs_attr_node_removename_setup(dac, &state);
>>>>    	if (error)
>>>>    		goto out;
>>>> @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
>>>>    	 * This is done before we remove the attribute so that we don't
>>>>    	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>>>    	 */
>>>> +
>>>> +das_rmtval_remove:
>>>> +
>>>
>>> I wonder if we need this label just to protect the setup. Perhaps if we
>>> had something like:
>>>
>>> 	/* set up the remove only once... */
>>> 	if (dela_state == 0)
>>> 		error = xfs_attr_node_removename_setup(...);
>>>
>>> ... we could reduce another state.
>>>
>>> We could also accomplish the same thing with an explicit state to
>>> indicate the setup already occurred or a new dac flag, though I'm not
>>> sure a flag is appropriate if it would only be used here.
>>>
>>> Brian
>>
>> Mmmm, dela_state == 0 will conflict a bit when we get into fully delayed
>> attrs.  Basically when this is getting called from the delayed operations
>> path, it sets dela_state to a new XFS_DAS_INIT. Because we have to set up
>> args mid fight, we need the extra state to not do that twice.
>>
> 
> Can we address that when the conflict is introduced?
Sure, i was just looking ahead, but focus should stay here

> 
>> But even without getting into that right away, what you're proposing only
>> gets rid of the label.  It doesnt get rid of the state.  We still have to
>> set the state to not be zero (or what ever the initial value is).  So we
>> still need the unique value of  XFS_DAS_RMTVAL_REMOVE
>>
> 
> Yeah, I was partly thinking of the setup call being tied to a flag
> rather than a state. That way the logic is something like the typical:
> 
> 	if (!setup)
> 		do_setup();
> 	...
> 
> ... and it's one less bit of code tied into the state machine. All in
> all, it's more that having a label right at the top of a function like
> that kind of looks like it's asking for some form of simplification.
Ok, this is similar to the discussion going on in the next patch.  To be 
clear, if we add a flag, we need to keep it in the delay_attr_context so 
that it persists across re-calls.  We dont want multiple functions 
shareing the same setup flag, so really we're talking about a new flag 
scheme like XFS_DAC_SETUP_* for any function that needs a set up flag? 
Is that what you had in mind?

> 
>> Really what you would need here in order to do what you are describeing is
>> dela_state != XFS_DAS_RMTVAL_REMOVE.  If I assume to simplify away to the
>> lease amount of LOC we get this:
>>
>>
>> STATIC int
>> xfs_attr_node_removename(
>>          struct xfs_delattr_context      *dac)
>> {
>>          struct xfs_da_args      *args = dac->da_args;
>>          struct xfs_da_state     *state;
>>          struct xfs_da_state_blk *blk;
>>          int                     retval, error;
>>          struct xfs_inode        *dp = args->dp;
>>
>>          trace_xfs_attr_node_removename(args);
>>          state = dac->da_state;
>>          blk = dac->blk;
>>
>>          if (dac->dela_state == XFS_DAS_RM_SHRINK) {
>>                  goto das_rm_shrink;
>>          } else if (dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
>>                  error = xfs_attr_node_removename_setup(dac, &state);
>>                  if (error)
>>                          goto out;
>>          }
>>
>>          /*
>>           * If there is an out-of-line value, de-allocate the blocks.
>>           * This is done before we remove the attribute so that we don't
>>           * overflow the maximum size of a transaction and/or hit a deadlock.
>>           */
>>
>>          if (args->rmtblkno > 0) {
>>                  error = xfs_attr_node_removename_rmt(args, state);
>>                  if (error) {
>>                          if (error == -EAGAIN)
>>                                  dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
>>                          return error;
>>                  }
>>          }
>>
>> .....
>>
>>
>> Let me know what folks think of this.  Again, I think I like the switches
>> and the labels just because it makes it more clear where the jump points
>> are, even if its more LOC.  But again, this isnt bad either if this is more
>> preferable to folks.  If there's another arrangment that is preferable, let
>> me know, it's not difficult to run it through the test cases to make sure
>> it's functional.  It may be a faster way to hash out what people want to
>> see.
>>
> 
> I prefer to see the state management stuff as boilerplate as possible.
> The above pattern of creating separate reentry calls to the same
> functions is not nearly as clear to me, particularly in this instance
> where we have multiple branches of reentry logic (as opposed to the
> earlier example of only one).
> 
> IOW, I agree that the jumps are preferable and more intuitive. I'm just
> trying to be reductive by considering what could be factored out vs.
> trying to fundamentally rework the approach or aggressively reduce LOC
> or anything like that. IMO, simplicity of the code is usually top
> priority.
> 
> Brian

Ok, lets firm up what we want this setup flag to look like, and then we 
can simplify the current label and goto scheme :-)

Thank you!!
Allison


> 
>> Thank you again for all the reviewing!!!
>>
>> Allison
>>
>>>
>>>>    	if (args->rmtblkno > 0) {
>>>>    		error = xfs_attr_node_removename_rmt(args, state);
>>>> -		if (error)
>>>> -			goto out;
>>>> +		if (error) {
>>>> +			if (error == -EAGAIN)
>>>> +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
>>>> +			return error;
>>>> +		}
>>>>    	}
>>>>    	/*
>>>> @@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
>>>>    		error = xfs_da3_join(state);
>>>>    		if (error)
>>>>    			goto out;
>>>> -		error = xfs_defer_finish(&args->trans);
>>>> -		if (error)
>>>> -			goto out;
>>>> -		/*
>>>> -		 * Commit the Btree join operation and start a new trans.
>>>> -		 */
>>>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -		if (error)
>>>> -			goto out;
>>>> +
>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +		dac->dela_state = XFS_DAS_RM_SHRINK;
>>>> +		return -EAGAIN;
>>>>    	}
>>>> +das_rm_shrink:
>>>> +	dac->dela_state = XFS_DAS_RM_SHRINK;
>>>> +
>>>>    	/*
>>>>    	 * If the result is small enough, push it all into the inode.
>>>>    	 */
>>>>    	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>>>> -		error = xfs_attr_node_shrink(args, state);
>>>> +		error = xfs_attr_node_shrink(dac, state);
>>>>    	error = 0;
>>>>    out:
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>>>> index 66575b8..0e8ae1a 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.h
>>>> +++ b/fs/xfs/libxfs/xfs_attr.h
>>>> @@ -74,6 +74,43 @@ struct xfs_attr_list_context {
>>>>    };
>>>> +/*
>>>> + * ========================================================================
>>>> + * Structure used to pass context around among the delayed routines.
>>>> + * ========================================================================
>>>> + */
>>>> +
>>>> +/*
>>>> + * Enum values for xfs_delattr_context.da_state
>>>> + *
>>>> + * These values are used by delayed attribute operations to keep track  of where
>>>> + * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
>>>> + * calling function to roll the transaction, and then recall the subroutine to
>>>> + * finish the operation.  The enum is then used by the subroutine to jump back
>>>> + * to where it was and resume executing where it left off.
>>>> + */
>>>> +enum xfs_delattr_state {
>>>> +				      /* Zero is uninitalized */
>>>> +	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
>>>> +	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
>>>> +};
>>>> +
>>>> +/*
>>>> + * Defines for xfs_delattr_context.flags
>>>> + */
>>>> +#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
>>>> +
>>>> +/*
>>>> + * Context used for keeping track of delayed attribute operations
>>>> + */
>>>> +struct xfs_delattr_context {
>>>> +	struct xfs_da_args      *da_args;
>>>> +	struct xfs_da_state     *da_state;
>>>> +	struct xfs_da_state_blk *blk;
>>>> +	unsigned int            flags;
>>>> +	enum xfs_delattr_state  dela_state;
>>>> +};
>>>> +
>>>>    /*========================================================================
>>>>     * Function prototypes for the kernel.
>>>>     *========================================================================*/
>>>> @@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
>>>>    int xfs_attr_set_args(struct xfs_da_args *args);
>>>>    int xfs_has_attr(struct xfs_da_args *args);
>>>>    int xfs_attr_remove_args(struct xfs_da_args *args);
>>>> +int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>>>    bool xfs_attr_namecheck(const void *name, size_t length);
>>>>    #endif	/* __XFS_ATTR_H__ */
>>>> -- 
>>>> 2.7.4
>>>>
>>>
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-16  3:17         ` Allison Collins
@ 2020-04-16 10:58           ` Brian Foster
  2020-04-16 22:41             ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-16 10:58 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs, dchinner

On Wed, Apr 15, 2020 at 08:17:07PM -0700, Allison Collins wrote:
> 
> 
> On 4/15/20 4:46 AM, Brian Foster wrote:
> > On Tue, Apr 14, 2020 at 02:35:43PM -0700, Allison Collins wrote:
> > > 
> > > 
> > > On 4/13/20 5:30 AM, Brian Foster wrote:
> > > > On Fri, Apr 03, 2020 at 03:12:27PM -0700, Allison Collins wrote:
> > > > > This patch modifies the attr remove routines to be delay ready. This
> > > > > means they no longer roll or commit transactions, but instead return
> > > > > -EAGAIN to have the calling routine roll and refresh the transaction. In
> > > > > this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
> > > > > uses a sort of state machine like switch to keep track of where it was
> > > > > when EAGAIN was returned. xfs_attr_node_removename has also been
> > > > > modified to use the switch, and a new version of xfs_attr_remove_args
> > > > > consists of a simple loop to refresh the transaction until the operation
> > > > > is completed.
> > > > > 
> > > > > Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> > > > > counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
> > > > > helper) and then __xfs_attr_rmtval_remove. We will rename
> > > > > __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> > > > > done.
> > > > > 
> > > > > This patch also adds a new struct xfs_delattr_context, which we will use
> > > > > to keep track of the current state of an attribute operation. The new
> > > > > xfs_delattr_state enum is used to track various operations that are in
> > > > > progress so that we know not to repeat them, and resume where we left
> > > > > off before EAGAIN was returned to cycle out the transaction. Other
> > > > > members take the place of local variables that need to retain their
> > > > > values across multiple function recalls.
> > > > > 
> > > > > Below is a state machine diagram for attr remove operations. The
> > > > > XFS_DAS_* states indicate places where the function would return
> > > > > -EAGAIN, and then immediately resume from after being recalled by the
> > > > > calling function.  States marked as a "subroutine state" indicate that
> > > > > they belong to a subroutine, and so the calling function needs to pass
> > > > > them back to that subroutine to allow it to finish where it left off.
> > > > > But they otherwise do not have a role in the calling function other than
> > > > > just passing through.
> > > > > 
> > > > >    xfs_attr_remove_iter()
> > > > >            XFS_DAS_RM_SHRINK     ─┐
> > > > >            (subroutine state)     │
> > > > >                                   │
> > > > >            XFS_DAS_RMTVAL_REMOVE ─┤
> > > > >            (subroutine state)     │
> > > > >                                   └─>xfs_attr_node_removename()
> > > > >                                                    │
> > > > >                                                    v
> > > > >                                            need to remove
> > > > >                                      ┌─n──  rmt blocks?
> > > > >                                      │             │
> > > > >                                      │             y
> > > > >                                      │             │
> > > > >                                      │             v
> > > > >                                      │  ┌─>XFS_DAS_RMTVAL_REMOVE
> > > > >                                      │  │          │
> > > > >                                      │  │          v
> > > > >                                      │  └──y── more blks
> > > > >                                      │         to remove?
> > > > >                                      │             │
> > > > >                                      │             n
> > > > >                                      │             │
> > > > >                                      │             v
> > > > >                                      │         need to
> > > > >                                      └─────> shrink tree? ─n─┐
> > > > >                                                    │         │
> > > > >                                                    y         │
> > > > >                                                    │         │
> > > > >                                                    v         │
> > > > >                                            XFS_DAS_RM_SHRINK │
> > > > >                                                    │         │
> > > > >                                                    v         │
> > > > >                                                   done <─────┘
> > > > > 
> > > > > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > > > > ---
> > > > 
> > > > All in all this is starting to look much more simple to me, at least in
> > > > the remove path. ;P There's only a few states and the markers that are
> > > > introduced are fairly straightforward, etc. Comments to follow..
> > > > 
> > > > >    fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
> > > > >    fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
> > > > >    2 files changed, 168 insertions(+), 38 deletions(-)
> > > > > 
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > > > index d735570..f700976 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > > > @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
> > > > >     */
> > > > >    STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
> > > > >    STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> > > > > -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
> > > > > +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
> > > > >    STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > > > >    /*
> > > > > @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > > > >     */
> > > > >    STATIC int xfs_attr_node_get(xfs_da_args_t *args);
> > > > >    STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> > > > > -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
> > > > > +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
> > > > >    STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
> > > > >    				 struct xfs_da_state **state);
> > > > >    STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
> > > > >    STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
> > > > > +STATIC void
> > > > > +xfs_delattr_context_init(
> > > > > +	struct xfs_delattr_context	*dac,
> > > > > +	struct xfs_da_args		*args)
> > > > > +{
> > > > > +	memset(dac, 0, sizeof(struct xfs_delattr_context));
> > > > > +	dac->da_args = args;
> > > > > +}
> > > > > +
> > > > >    int
> > > > >    xfs_inode_hasattr(
> > > > >    	struct xfs_inode	*ip)
> > > > > @@ -356,20 +365,66 @@ xfs_has_attr(
> > > > >     */
> > > > >    int
> > > > >    xfs_attr_remove_args(
> > > > > -	struct xfs_da_args      *args)
> > > > > +	struct xfs_da_args	*args)
> > > > >    {
> > > > > +	int			error = 0;
> > > > > +	struct			xfs_delattr_context dac;
> > > > > +
> > > > > +	xfs_delattr_context_init(&dac, args);
> > > > > +
> > > > > +	do {
> > > > > +		error = xfs_attr_remove_iter(&dac);
> > > > > +		if (error != -EAGAIN)
> > > > > +			break;
> > > > > +
> > > > > +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> > > > > +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> > > > > +			error = xfs_defer_finish(&args->trans);
> > > > > +			if (error)
> > > > > +				break;
> > > > > +		}
> > > > > +
> > > > > +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > > > +		if (error)
> > > > > +			break;
> > > > > +	} while (true);
> > > > > +
> > > > > +	return error;
> > > > > +}
> > > > > +
> > > > > +/*
> > > > > + * Remove the attribute specified in @args.
> > > > > + *
> > > > > + * This function may return -EAGAIN to signal that the transaction needs to be
> > > > > + * rolled.  Callers should continue calling this function until they receive a
> > > > > + * return value other than -EAGAIN.
> > > > > + */
> > > > > +int
> > > > > +xfs_attr_remove_iter(
> > > > > +	struct xfs_delattr_context *dac)
> > > > > +{
> > > > > +	struct xfs_da_args	*args = dac->da_args;
> > > > >    	struct xfs_inode	*dp = args->dp;
> > > > >    	int			error;
> > > > > +	/* State machine switch */
> > > > > +	switch (dac->dela_state) {
> > > > > +	case XFS_DAS_RM_SHRINK:
> > > > > +	case XFS_DAS_RMTVAL_REMOVE:
> > > > > +		return xfs_attr_node_removename(dac);
> > > > > +	default:
> > > > > +		break;
> > > > > +	}
> > > > > +
> > > > 
> > > > Hmm.. so we're duplicating the call instead of using labels..?
> > > 
> > > Yes, this was a suggestion made during v7.  I suspect Dave may have been
> > > wanting to simplify things by escaping the use of labels.  At least in so
> > > far as the remove path is concerned.  Though he may not have realized this
> > > would create a duplication call?  I will cc him here; the conditions for
> > > calling xfs_attr_node_removename are: the below if/else sequence exhausts
> > > with no successes, and defaults into the else case (ie: the entry
> > > condition), OR one of the above states is set (which is a re-entry
> > > condition)
> > > 
> > 
> > Ok.
> > 
> > > 
> > > I'm
> > > > wondering if this can be elegantly combined with the if/else branches
> > > > below, particularly since node format is the only situation that seems
> > > > to require a roll here.
> > > > 
> > > > >    	if (!xfs_inode_hasattr(dp)) {
> > > > >    		error = -ENOATTR;
> > > > >    	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
> > > > >    		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
> > > > >    		error = xfs_attr_shortform_remove(args);
> > > > >    	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > > > -		error = xfs_attr_leaf_removename(args);
> > > > > +		error = xfs_attr_leaf_removename(dac);
> > > > >    	} else {
> > > > > -		error = xfs_attr_node_removename(args);
> > > > > +		error = xfs_attr_node_removename(dac);
> > > > >    	}
> > > > >    	return error;
> > > 
> > > If we want to try and combine this into if/elses with no duplication, I
> > > believe the simplest arrangement would look something like this:
> > > 
> > > 
> > > int
> > > xfs_attr_remove_iter(
> > > 	struct xfs_delattr_context *dac)
> > > {
> > > 	struct xfs_da_args	*args = dac->da_args;
> > > 	struct xfs_inode	*dp = args->dp;
> > > 
> > > 	if (dac->dela_state != XFS_DAS_RM_SHRINK &&
> > > 	    dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
> > > 		if (!xfs_inode_hasattr(dp)) {
> > > 			return -ENOATTR;
> > > 		} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
> > > 			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
> > > 			return xfs_attr_shortform_remove(args);
> > > 		} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > 			return xfs_attr_leaf_removename(dac);
> > > 		}
> > > 	}
> > > 
> > > 	return xfs_attr_node_removename(dac);
> > > }
> > > 
> > > Let me know what folks think of that.  I'm not always clear on where people
> > > stand with aesthetics. (IE, is it better to have a duplicate call if it gets
> > > rid of a label?  Is the solution with the least amount of LOC always
> > > preferable?)  This area seems simple enough maybe we can get it ironed out
> > > here with out another version.
> > > 
> > > IMHO I think the above code sort of obfuscates that the code flow is really
> > > just one if/else switch with one function that has the statemachine
> > > behavior.  But its not bad either if that's what people prefer.  I'd like to
> > > find something every can be sort of happy with.  :-)
> > > 
> > 
> > If you want my .02, some combination of the above is cleanest from an
> > aesthetic pov:
> > 
> > {
> > 	...
> > 	if (RM_SHRINK || RMTVAL_REMOVE)
> > 		goto node;
> > 
> > 	if (!hasattr)
> > 		return -ENOATTR;
> > 	else if (local)
> > 		return shortform_remove();
> > 	else if (oneblock)
> > 		return leaf_removename();
> > 
> > node:
> > 	return node_removename();
> > }
> > 
> > I find that easiest to read at a glance, but I don't feel terribly
> > strongly about it I guess.
> 
> I am entirely fine with that if everyone else is :-)
> 
> > 
> > > > > @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
> > > > >     */
> > > > >    STATIC int
> > > > >    xfs_attr_leaf_removename(
> > > > > -	struct xfs_da_args	*args)
> > > > > +	struct xfs_delattr_context	*dac)
> > > > >    {
> > > > > -	struct xfs_inode	*dp;
> > > > > -	struct xfs_buf		*bp;
> > > > > -	int			error, forkoff;
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_inode		*dp;
> > > > > +	struct xfs_buf			*bp;
> > > > > +	int				error, forkoff;
> > > > >    	trace_xfs_attr_leaf_removename(args);
> > > > > @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
> > > > >    		/* bp is gone due to xfs_da_shrink_inode */
> > > > >    		if (error)
> > > > >    			return error;
> > > > > -		error = xfs_defer_finish(&args->trans);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > +
> > > > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > 
> > > > There's no -EAGAIN return here, is this an exit path for the remove?
> > > I think so, maybe I can remove this and the other one you pointed out in
> > > patch 12 along with the other unneeded transaction handling.
> > > 
> > > > 
> > > > >    	}
> > > > >    	return 0;
> > > > >    }
> > > > > @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
> > > > >     */
> > > > >    STATIC int
> > > > >    xfs_attr_node_shrink(
> > > > > -	struct xfs_da_args	*args,
> > > > > -	struct xfs_da_state     *state)
> > > > > +	struct xfs_delattr_context	*dac,
> > > > > +	struct xfs_da_state		*state)
> > > > >    {
> > > > > -	struct xfs_inode	*dp = args->dp;
> > > > > -	int			error, forkoff;
> > > > > -	struct xfs_buf		*bp;
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_inode		*dp = args->dp;
> > > > > +	int				error, forkoff;
> > > > > +	struct xfs_buf			*bp;
> > > > >    	/*
> > > > >    	 * Have to get rid of the copy of this dabuf in the state.
> > > > > @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
> > > > >    		if (error)
> > > > >    			return error;
> > > > > -		error = xfs_defer_finish(&args->trans);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > 
> > > > Same question here.
> > > > 
> > > > >    	} else
> > > > >    		xfs_trans_brelse(args->trans, bp);
> > > > > @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
> > > > >    /*
> > > > >     * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
> > > > > - * the blocks are valid.  Any remote blocks will be marked incomplete.
> > > > > + * the blocks are valid.  Any remote blocks will be marked incomplete and
> > > > > + * invalidated.
> > > > >     */
> > > > >    STATIC
> > > > >    int xfs_attr_node_removename_setup(
> > > > > -	struct xfs_da_args	*args,
> > > > > -	struct xfs_da_state	**state)
> > > > > +	struct xfs_delattr_context	*dac,
> > > > > +	struct xfs_da_state		**state)
> > > > >    {
> > > > > +	struct xfs_da_args	*args = dac->da_args;
> > > > >    	int			error;
> > > > >    	struct xfs_da_state_blk	*blk;
> > > > > @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
> > > > >    	ASSERT(blk->bp != NULL);
> > > > >    	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> > > > > +	/*
> > > > > +	 * Store blk and state in the context incase we need to cycle out the
> > > > > +	 * transaction
> > > > > +	 */
> > > > > +	dac->blk = blk;
> > > > > +	dac->da_state = *state;
> > > > > +
> > > > >    	if (args->rmtblkno > 0) {
> > > > >    		error = xfs_attr_leaf_mark_incomplete(args, *state);
> > > > >    		if (error)
> > > > >    			return error;
> > > > > +
> > > > > +		error = xfs_attr_rmtval_invalidate(args);
> > > > > +		if (error)
> > > > > +			return error;
> > > > 
> > > > Seems like this moves code, which should probably happen in a separate
> > > > patch.
> > > Ok, this pairs with the  xfs_attr_rmtval_remove to __xfs_attr_rmtval_remove
> > > below.  Basically xfs_attr_rmtval_remove is the combination of
> > > xfs_attr_rmtval_invalidate and __xfs_attr_rmtval_remove. So thats why we see
> > > xfs_attr_rmtval_remove going away and xfs_attr_rmtval_invalidate +
> > > __xfs_attr_rmtval_remove coming in.
> > > 
> > > How about a patch that pulls xfs_attr_rmtval_invalidate out of
> > > xfs_attr_rmtval_remove and into the calling functions?  I think that might
> > > be more clear.
> > > 
> > 
> > Yes, separate patch please. I think that if the earlier refactoring
> > parts of the series are split out properly (i.e., no dependencies on
> > subsequent patches) and reviewed, perhaps we can start getting some of
> > those patches merged while the latter bits are worked out.
> Ok then, will do
> 
> > 
> > > > 
> > > > >    	}
> > > > >    	return 0;
> > > > > @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
> > > > >    {
> > > > >    	int			error = 0;
> > > > > -	error = xfs_attr_rmtval_remove(args);
> > > > > +	/*
> > > > > +	 * May return -EAGAIN to request that the caller recall this function
> > > > > +	 */
> > > > > +	error = __xfs_attr_rmtval_remove(args);
> > > > >    	if (error)
> > > > >    		return error;
> > > > > @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
> > > > >     * This will involve walking down the Btree, and may involve joining
> > > > >     * leaf nodes and even joining intermediate nodes up to and including
> > > > >     * the root node (a special case of an intermediate node).
> > > > > + *
> > > > > + * This routine is meant to function as either an inline or 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_node_removename(
> > > > > -	struct xfs_da_args	*args)
> > > > > +	struct xfs_delattr_context	*dac)
> > > > >    {
> > > > > +	struct xfs_da_args	*args = dac->da_args;
> > > > >    	struct xfs_da_state	*state;
> > > > >    	struct xfs_da_state_blk	*blk;
> > > > >    	int			retval, error;
> > > > >    	struct xfs_inode	*dp = args->dp;
> > > > >    	trace_xfs_attr_node_removename(args);
> > > > > +	state = dac->da_state;
> > > > > +	blk = dac->blk;
> > > > > +
> > > > > +	/* State machine switch */
> > > > > +	switch (dac->dela_state) {
> > > > > +	case XFS_DAS_RMTVAL_REMOVE:
> > > > > +		goto das_rmtval_remove;
> > > > > +	case XFS_DAS_RM_SHRINK:
> > > > > +		goto das_rm_shrink;
> > > > > +	default:
> > > > > +		break;
> > > > > +	}
> > > > > -	error = xfs_attr_node_removename_setup(args, &state);
> > > > > +	error = xfs_attr_node_removename_setup(dac, &state);
> > > > >    	if (error)
> > > > >    		goto out;
> > > > > @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
> > > > >    	 * This is done before we remove the attribute so that we don't
> > > > >    	 * overflow the maximum size of a transaction and/or hit a deadlock.
> > > > >    	 */
> > > > > +
> > > > > +das_rmtval_remove:
> > > > > +
> > > > 
> > > > I wonder if we need this label just to protect the setup. Perhaps if we
> > > > had something like:
> > > > 
> > > > 	/* set up the remove only once... */
> > > > 	if (dela_state == 0)
> > > > 		error = xfs_attr_node_removename_setup(...);
> > > > 
> > > > ... we could reduce another state.
> > > > 
> > > > We could also accomplish the same thing with an explicit state to
> > > > indicate the setup already occurred or a new dac flag, though I'm not
> > > > sure a flag is appropriate if it would only be used here.
> > > > 
> > > > Brian
> > > 
> > > Mmmm, dela_state == 0 will conflict a bit when we get into fully delayed
> > > attrs.  Basically when this is getting called from the delayed operations
> > > path, it sets dela_state to a new XFS_DAS_INIT. Because we have to set up
> > > args mid fight, we need the extra state to not do that twice.
> > > 
> > 
> > Can we address that when the conflict is introduced?
> Sure, i was just looking ahead, but focus should stay here
> 
> > 
> > > But even without getting into that right away, what you're proposing only
> > > gets rid of the label.  It doesnt get rid of the state.  We still have to
> > > set the state to not be zero (or what ever the initial value is).  So we
> > > still need the unique value of  XFS_DAS_RMTVAL_REMOVE
> > > 
> > 
> > Yeah, I was partly thinking of the setup call being tied to a flag
> > rather than a state. That way the logic is something like the typical:
> > 
> > 	if (!setup)
> > 		do_setup();
> > 	...
> > 
> > ... and it's one less bit of code tied into the state machine. All in
> > all, it's more that having a label right at the top of a function like
> > that kind of looks like it's asking for some form of simplification.
> Ok, this is similar to the discussion going on in the next patch.  To be
> clear, if we add a flag, we need to keep it in the delay_attr_context so
> that it persists across re-calls.  We dont want multiple functions shareing
> the same setup flag, so really we're talking about a new flag scheme like
> XFS_DAC_SETUP_* for any function that needs a set up flag? Is that what you
> had in mind?
> 

Yep, pretty much. It's not clear to me if we can reuse one general INIT
flag across operations or need several such flags, but I'm not sure I
understand why we couldn't reuse the existing dac->flags at least..

Brian

> > 
> > > Really what you would need here in order to do what you are describeing is
> > > dela_state != XFS_DAS_RMTVAL_REMOVE.  If I assume to simplify away to the
> > > lease amount of LOC we get this:
> > > 
> > > 
> > > STATIC int
> > > xfs_attr_node_removename(
> > >          struct xfs_delattr_context      *dac)
> > > {
> > >          struct xfs_da_args      *args = dac->da_args;
> > >          struct xfs_da_state     *state;
> > >          struct xfs_da_state_blk *blk;
> > >          int                     retval, error;
> > >          struct xfs_inode        *dp = args->dp;
> > > 
> > >          trace_xfs_attr_node_removename(args);
> > >          state = dac->da_state;
> > >          blk = dac->blk;
> > > 
> > >          if (dac->dela_state == XFS_DAS_RM_SHRINK) {
> > >                  goto das_rm_shrink;
> > >          } else if (dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
> > >                  error = xfs_attr_node_removename_setup(dac, &state);
> > >                  if (error)
> > >                          goto out;
> > >          }
> > > 
> > >          /*
> > >           * If there is an out-of-line value, de-allocate the blocks.
> > >           * This is done before we remove the attribute so that we don't
> > >           * overflow the maximum size of a transaction and/or hit a deadlock.
> > >           */
> > > 
> > >          if (args->rmtblkno > 0) {
> > >                  error = xfs_attr_node_removename_rmt(args, state);
> > >                  if (error) {
> > >                          if (error == -EAGAIN)
> > >                                  dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
> > >                          return error;
> > >                  }
> > >          }
> > > 
> > > .....
> > > 
> > > 
> > > Let me know what folks think of this.  Again, I think I like the switches
> > > and the labels just because it makes it more clear where the jump points
> > > are, even if its more LOC.  But again, this isnt bad either if this is more
> > > preferable to folks.  If there's another arrangment that is preferable, let
> > > me know, it's not difficult to run it through the test cases to make sure
> > > it's functional.  It may be a faster way to hash out what people want to
> > > see.
> > > 
> > 
> > I prefer to see the state management stuff as boilerplate as possible.
> > The above pattern of creating separate reentry calls to the same
> > functions is not nearly as clear to me, particularly in this instance
> > where we have multiple branches of reentry logic (as opposed to the
> > earlier example of only one).
> > 
> > IOW, I agree that the jumps are preferable and more intuitive. I'm just
> > trying to be reductive by considering what could be factored out vs.
> > trying to fundamentally rework the approach or aggressively reduce LOC
> > or anything like that. IMO, simplicity of the code is usually top
> > priority.
> > 
> > Brian
> 
> Ok, lets firm up what we want this setup flag to look like, and then we can
> simplify the current label and goto scheme :-)
> 
> Thank you!!
> Allison
> 
> 
> > 
> > > Thank you again for all the reviewing!!!
> > > 
> > > Allison
> > > 
> > > > 
> > > > >    	if (args->rmtblkno > 0) {
> > > > >    		error = xfs_attr_node_removename_rmt(args, state);
> > > > > -		if (error)
> > > > > -			goto out;
> > > > > +		if (error) {
> > > > > +			if (error == -EAGAIN)
> > > > > +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
> > > > > +			return error;
> > > > > +		}
> > > > >    	}
> > > > >    	/*
> > > > > @@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
> > > > >    		error = xfs_da3_join(state);
> > > > >    		if (error)
> > > > >    			goto out;
> > > > > -		error = xfs_defer_finish(&args->trans);
> > > > > -		if (error)
> > > > > -			goto out;
> > > > > -		/*
> > > > > -		 * Commit the Btree join operation and start a new trans.
> > > > > -		 */
> > > > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > > > -		if (error)
> > > > > -			goto out;
> > > > > +
> > > > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +		dac->dela_state = XFS_DAS_RM_SHRINK;
> > > > > +		return -EAGAIN;
> > > > >    	}
> > > > > +das_rm_shrink:
> > > > > +	dac->dela_state = XFS_DAS_RM_SHRINK;
> > > > > +
> > > > >    	/*
> > > > >    	 * If the result is small enough, push it all into the inode.
> > > > >    	 */
> > > > >    	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> > > > > -		error = xfs_attr_node_shrink(args, state);
> > > > > +		error = xfs_attr_node_shrink(dac, state);
> > > > >    	error = 0;
> > > > >    out:
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > > > > index 66575b8..0e8ae1a 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr.h
> > > > > +++ b/fs/xfs/libxfs/xfs_attr.h
> > > > > @@ -74,6 +74,43 @@ struct xfs_attr_list_context {
> > > > >    };
> > > > > +/*
> > > > > + * ========================================================================
> > > > > + * Structure used to pass context around among the delayed routines.
> > > > > + * ========================================================================
> > > > > + */
> > > > > +
> > > > > +/*
> > > > > + * Enum values for xfs_delattr_context.da_state
> > > > > + *
> > > > > + * These values are used by delayed attribute operations to keep track  of where
> > > > > + * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
> > > > > + * calling function to roll the transaction, and then recall the subroutine to
> > > > > + * finish the operation.  The enum is then used by the subroutine to jump back
> > > > > + * to where it was and resume executing where it left off.
> > > > > + */
> > > > > +enum xfs_delattr_state {
> > > > > +				      /* Zero is uninitalized */
> > > > > +	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
> > > > > +	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
> > > > > +};
> > > > > +
> > > > > +/*
> > > > > + * Defines for xfs_delattr_context.flags
> > > > > + */
> > > > > +#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
> > > > > +
> > > > > +/*
> > > > > + * Context used for keeping track of delayed attribute operations
> > > > > + */
> > > > > +struct xfs_delattr_context {
> > > > > +	struct xfs_da_args      *da_args;
> > > > > +	struct xfs_da_state     *da_state;
> > > > > +	struct xfs_da_state_blk *blk;
> > > > > +	unsigned int            flags;
> > > > > +	enum xfs_delattr_state  dela_state;
> > > > > +};
> > > > > +
> > > > >    /*========================================================================
> > > > >     * Function prototypes for the kernel.
> > > > >     *========================================================================*/
> > > > > @@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
> > > > >    int xfs_attr_set_args(struct xfs_da_args *args);
> > > > >    int xfs_has_attr(struct xfs_da_args *args);
> > > > >    int xfs_attr_remove_args(struct xfs_da_args *args);
> > > > > +int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
> > > > >    bool xfs_attr_namecheck(const void *name, size_t length);
> > > > >    #endif	/* __XFS_ATTR_H__ */
> > > > > -- 
> > > > > 2.7.4
> > > > > 
> > > > 
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-15 22:08     ` Allison Collins
@ 2020-04-16 11:01       ` Brian Foster
  2020-04-16 22:54         ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-16 11:01 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Wed, Apr 15, 2020 at 03:08:11PM -0700, Allison Collins wrote:
> 
> 
> On 4/13/20 6:40 AM, Brian Foster wrote:
> > On Fri, Apr 03, 2020 at 03:12:28PM -0700, Allison Collins wrote:
> > > This patch modifies the attr set routines to be delay ready. This means
> > > they no longer roll or commit transactions, but instead return -EAGAIN
> > > to have the calling routine roll and refresh the transaction.  In this
> > > series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
> > > state machine like switch to keep track of where it was when EAGAIN was
> > > returned.
> > > 
> > > Two new helper functions have been added: xfs_attr_rmtval_set_init and
> > > xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
> > > xfs_attr_rmtval_set, but they store the current block in the delay attr
> > > context to allow the caller to roll the transaction between allocations.
> > > This helps to simplify and consolidate code used by
> > > xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
> > > now become a simple loop to refresh the transaction until the operation
> > > is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
> > > removed.
> > > 
> > > Below is a state machine diagram for attr set operations. The XFS_DAS_*
> > > states indicate places where the function would return -EAGAIN, and then
> > > immediately resume from after being recalled by the calling function.
> > > States marked as a "subroutine state" indicate that they belong to a
> > > subroutine, and so the calling function needs to pass them back to that
> > > subroutine to allow it to finish where it left off.  But they otherwise
> > > do not have a role in the calling function other than just passing
> > > through.
> > > 
> > >   xfs_attr_set_iter()
> > >                   │
> > >                   v
> > >             need to upgrade
> > >            from sf to leaf? ──n─┐
> > >                   │             │
> > >                   y             │
> > >                   │             │
> > >                   V             │
> > >            XFS_DAS_ADD_LEAF     │
> > >                   │             │
> > >                   v             │
> > >    ┌──────n── fork has   <──────┘
> > >    │         only 1 blk?
> > >    │              │
> > >    │              y
> > >    │              │
> > >    │              v
> > >    │     xfs_attr_leaf_try_add()
> > >    │              │
> > >    │              v
> > >    │          had enough
> > >    ├──────n──   space?
> > >    │              │
> > >    │              y
> > >    │              │
> > >    │              v
> > >    │      XFS_DAS_FOUND_LBLK  ──┐
> > >    │                            │
> > >    │      XFS_DAS_FLIP_LFLAG  ──┤
> > >    │      (subroutine state)    │
> > >    │                            │
> > >    │      XFS_DAS_ALLOC_LEAF  ──┤
> > >    │      (subroutine state)    │
> > >    │                            └─>xfs_attr_leaf_addname()
> > >    │                                              │
> > >    │                                              v
> > >    │                                ┌─────n──  need to
> > >    │                                │        alloc blks?
> > >    │                                │             │
> > >    │                                │             y
> > >    │                                │             │
> > >    │                                │             v
> > >    │                                │  ┌─>XFS_DAS_ALLOC_LEAF
> > >    │                                │  │          │
> > >    │                                │  │          v
> > >    │                                │  └──y── need to alloc
> > >    │                                │         more blocks?
> > >    │                                │             │
> > >    │                                │             n
> > >    │                                │             │
> > >    │                                │             v
> > >    │                                │          was this
> > >    │                                └────────> a rename? ──n─┐
> > >    │                                              │          │
> > >    │                                              y          │
> > >    │                                              │          │
> > >    │                                              v          │
> > >    │                                        flip incomplete  │
> > >    │                                            flag         │
> > >    │                                              │          │
> > >    │                                              v          │
> > >    │                                      XFS_DAS_FLIP_LFLAG │
> > >    │                                              │          │
> > >    │                                              v          │
> > >    │                                            remove       │
> > >    │                        XFS_DAS_RM_LBLK ─> old name      │
> > >    │                                 ^            │          │
> > >    │                                 │            v          │
> > >    │                                 └──────y── more to      │
> > >    │                                            remove       │
> > >    │                                              │          │
> > >    │                                              n          │
> > >    │                                              │          │
> > >    │                                              v          │
> > >    │                                             done <──────┘
> > >    └────> XFS_DAS_LEAF_TO_NODE ─┐
> > >                                 │
> > >           XFS_DAS_FOUND_NBLK  ──┤
> > >           (subroutine state)    │
> > >                                 │
> > >           XFS_DAS_ALLOC_NODE  ──┤
> > >           (subroutine state)    │
> > >                                 │
> > >           XFS_DAS_FLIP_NFLAG  ──┤
> > >           (subroutine state)    │
> > >                                 │
> > >                                 └─>xfs_attr_node_addname()
> > >                                                   │
> > >                                                   v
> > >                                           find space to store
> > >                                          attr. Split if needed
> > >                                                   │
> > >                                                   v
> > >                                           XFS_DAS_FOUND_NBLK
> > >                                                   │
> > >                                                   v
> > >                                     ┌─────n──  need to
> > >                                     │        alloc blks?
> > >                                     │             │
> > >                                     │             y
> > >                                     │             │
> > >                                     │             v
> > >                                     │  ┌─>XFS_DAS_ALLOC_NODE
> > >                                     │  │          │
> > >                                     │  │          v
> > >                                     │  └──y── need to alloc
> > >                                     │         more blocks?
> > >                                     │             │
> > >                                     │             n
> > >                                     │             │
> > >                                     │             v
> > >                                     │          was this
> > >                                     └────────> a rename? ──n─┐
> > >                                                   │          │
> > >                                                   y          │
> > >                                                   │          │
> > >                                                   v          │
> > >                                             flip incomplete  │
> > >                                                 flag         │
> > >                                                   │          │
> > >                                                   v          │
> > >                                           XFS_DAS_FLIP_NFLAG │
> > >                                                   │          │
> > >                                                   v          │
> > >                                                 remove       │
> > >                             XFS_DAS_RM_NBLK ─> old name      │
> > >                                      ^            │          │
> > >                                      │            v          │
> > >                                      └──────y── more to      │
> > >                                                 remove       │
> > >                                                   │          │
> > >                                                   n          │
> > >                                                   │          │
> > >                                                   v          │
> > >                                                  done <──────┘
> > > 
> > > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > > ---
> > 
> > Only a cursory pass given the previous feedback...
> > 
> > >   fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
> > >   fs/xfs/libxfs/xfs_attr.h        |  16 ++
> > >   fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
> > >   fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
> > >   fs/xfs/libxfs/xfs_attr_remote.h |   4 +
> > >   fs/xfs/xfs_attr_inactive.c      |   1 +
> > >   fs/xfs/xfs_trace.h              |   1 -
> > >   7 files changed, 351 insertions(+), 167 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index f700976..c160b7a 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > @@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
> > >    * Internal routines when attribute list is one block.
> > >    */
> > >   STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
> > > -STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> > > +STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
> > >   STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
> > >   STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > > @@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > >    * Internal routines when attribute list is more than one block.
> > >    */
> > >   STATIC int xfs_attr_node_get(xfs_da_args_t *args);
> > > -STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> > > +STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
> > >   STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
> > >   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
> > >   				 struct xfs_da_state **state);
> > >   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 void
> > >   xfs_delattr_context_init(
> > > @@ -227,8 +228,11 @@ xfs_attr_is_shortform(
> > >   /*
> > >    * Attempts to set an attr in shortform, or converts the tree to leaf form if
> > > - * there is not enough room.  If the attr is set, the transaction is committed
> > > - * and set to NULL.
> > > + * there is not enough room.  This function is meant to operate as a helper
> > > + * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
> > > + * that the calling function should roll the transaction, and then proceed to
> > > + * add the attr in leaf form.  This subroutine does not expect to be recalled
> > > + * again like the other delayed attr routines do.
> > >    */
> > >   STATIC int
> > >   xfs_attr_set_shortform(
> > > @@ -236,16 +240,16 @@ xfs_attr_set_shortform(
> > >   	struct xfs_buf		**leaf_bp)
> > >   {
> > >   	struct xfs_inode	*dp = args->dp;
> > > -	int			error, error2 = 0;
> > > +	int			error = 0;
> > >   	/*
> > >   	 * Try to add the attr to the attribute list in the inode.
> > >   	 */
> > >   	error = xfs_attr_try_sf_addname(dp, args);
> > > +
> > > +	/* Should only be 0, -EEXIST or ENOSPC */
> > >   	if (error != -ENOSPC) {
> > > -		error2 = xfs_trans_commit(args->trans);
> > > -		args->trans = NULL;
> > > -		return error ? error : error2;
> > > +		return error;
> > >   	}
> > >   	/*
> > >   	 * It won't fit in the shortform, transform to a leaf block.  GROT:
> > > @@ -258,18 +262,10 @@ xfs_attr_set_shortform(
> > >   	/*
> > >   	 * 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. Once we're done rolling the transaction we
> > > -	 * can release the hold and add the attr to the leaf.
> > > +	 * with the write verifier.
> > >   	 */
> > >   	xfs_trans_bhold(args->trans, *leaf_bp);
> > > -	error = xfs_defer_finish(&args->trans);
> > > -	xfs_trans_bhold_release(args->trans, *leaf_bp);
> > > -	if (error) {
> > > -		xfs_trans_brelse(args->trans, *leaf_bp);
> > > -		return error;
> > > -	}
> > > -
> > > -	return 0;
> > > +	return -EAGAIN;
> > >   }
> > >   /*
> > > @@ -279,9 +275,83 @@ int
> > >   xfs_attr_set_args(
> > >   	struct xfs_da_args	*args)
> > >   {
> > > -	struct xfs_inode	*dp = args->dp;
> > > -	struct xfs_buf          *leaf_bp = NULL;
> > > -	int			error = 0;
> > > +	struct xfs_buf			*leaf_bp = NULL;
> > > +	int				error = 0;
> > > +	struct xfs_delattr_context	dac;
> > > +
> > > +	xfs_delattr_context_init(&dac, args);
> > > +
> > > +	do {
> > > +		error = xfs_attr_set_iter(&dac, &leaf_bp);
> > > +		if (error != -EAGAIN)
> > > +			break;
> > > +
> > > +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> > > +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> > > +			error = xfs_defer_finish(&args->trans);
> > > +			if (error)
> > > +				break;
> > > +		}
> > > +
> > > +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > +		if (error)
> > > +			break;
> > > +
> > > +		if (leaf_bp) {
> > > +			xfs_trans_bjoin(args->trans, leaf_bp);
> > > +			xfs_trans_bhold(args->trans, leaf_bp);
> > > +		}
> > > +
> > > +	} while (true);
> > > +
> > > +	return error;
> > > +}
> > > +
> > > +/*
> > > + * 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.
> > > + */
> > > +int
> > > +xfs_attr_set_iter(
> > > +	struct xfs_delattr_context	*dac,
> > > +	struct xfs_buf			**leaf_bp)
> > > +{
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_inode		*dp = args->dp;
> > > +	int				error = 0;
> > > +	int				sf_size;
> > > +
> > > +	/* State machine switch */
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_ADD_LEAF:
> > > +		goto das_add_leaf;
> > > +	case XFS_DAS_ALLOC_LEAF:
> > > +	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:
> > > +	case XFS_DAS_LEAF_TO_NODE:
> > > +		goto das_node;
> > > +	default:
> > > +		break;
> > > +	}
> > > +
> > > +	/*
> > > +	 * New inodes may not have an attribute fork yet. So set the attribute
> > > +	 * fork appropriately
> > > +	 */
> > > +	if (XFS_IFORK_Q((args->dp)) == 0) {
> > > +		sf_size = sizeof(struct xfs_attr_sf_hdr) +
> > > +		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
> > > +		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
> > > +		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
> > > +		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
> > > +	}
> > 
> > Is this hunk moved from somewhere? If so, we should probably handle that
> > in a separate patch. I think we really want these last couple of patches
> > to introduce the state/markers and not much else.
> Oh, this hunk is new, not moved, I believe it's been here since I picked up
> the series quite a while ago.  It actually has more to do with parent
> pointers than delayed atts.  Basically when we try to add the parent pointer
> during a create, the inode isnt fully constructed yet, so we have add the
> fork here. I will put this in a separate patch and move it further up the
> set.
> 

Ok.

> > 
> > >   	/*
> > >   	 * If the attribute list is already in leaf format, jump straight to
> > > @@ -292,40 +362,53 @@ xfs_attr_set_args(
> > >   	if (xfs_attr_is_shortform(dp)) {
> > >   		/*
> > > -		 * If the attr was successfully set in shortform, the
> > > -		 * transaction is committed and set to NULL.  Otherwise, is it
> > > -		 * converted from shortform to leaf, and the transaction is
> > > -		 * retained.
> > > +		 * 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 || !args->trans)
> > > -			return error;
> > > +		error = xfs_attr_set_shortform(args, leaf_bp);
> > > +		if (error == -EAGAIN) {
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			dac->dela_state = XFS_DAS_ADD_LEAF;
> > > +		}
> > > +		return error;
> > 
> > Similar to the previous patch, I wonder if we need the explicit states
> > that are otherwise handled by existing inode state. For example, if the
> > above returns -EAGAIN, xfs_attr_is_shortform() is no longer true on
> > reentry, right? If that's the case for the other conversions, it seems
> > like we might only need one state (XFS_DAS_FOUND_LBLK) for this
> > function.
> Ok, it looks like I can get away with out XFS_DAS_ADD_LEAF and
> XFS_DAS_LEAF_TO_NODE.
> 
> This is actually a little similar to how it was done when I was trying to
> use flags instead of the state enums a long time ago.  At the time, I got
> the impression people were concerned about the complexity and
> maintainability.  So we went back to the enums because the explicite jumping
> made it clear where the re-entry was to resume, and reduced the risk that a
> person may mistakenly introduce a change that disrupts the reentry flow.
> Though perhaps the current arrangement of refactoring has made that less
> concerning?
> 

Perhaps. I think it just depends on context. It might not make sense to
try and implement such a reentry model down in the node code where we
have N different non-deterministic states to deal with; as opposed to
here where we're looking at one of a handful of high level formats and
the behavior is very predictable: try to add to the current format, else
convert to the next.

> > 
> > BTW, that general approach might be more clear if we lifted the format
> > conversions into this level from down in the format specific add
> > handlers. The goal would be to make the high level flow look something
> > like:
> > 
> > 	if (shortform) {
> > 		error = sf_try_add();
> > 		if (error == -ENOSPC) {
> > 			shortform_to_leaf(...);
> > 			...
> > 			return -EAGAIN;
> > 		}
> Well, actually this piece was hoisted out into the helper function in patch
> 13.  So pulling this up is sort of like letting go of patch 13. :-)
> 
> The history is: initially I had tried to reduce indentation here in v6 by
> taking advantage of the _add_leaf label. Because I think in v5 we were
> trying to unnest where some of the jump points were.  So in v6 I had:
> "if(!shortform) goto leaf;" Which un-nests this shortform code.
> 
> Then in the v7 review I think Dave suggested I should add the helper and
> invert the check. So now we have "if(shortform()){ call_helper(); handle
> -EAGAIN; }"
> 
> So pulling up is a bit of a circle now.  But still functional if people
> prefer it as it was.  Just need to keep track of the history so we avoid
> going around again. :-)
> 

I don't think you need to drop that patch necessarily, but perhaps keep
the conversion part to a separate helper and then introduce a similar
patch for other formats..? In any event, that was just a followup
thought so it might be reasonable to defer it to the next version and
for now just see what it looks like to rely on the format states.

> 
> > 	} else if (xfs_bmap_one_block(...)) {
> Mmm.... cant jump into the leaf logic right away.  We need to handle
> releasing leaf_bp which looks like it got lost in the pseudo code?  IIRC
> there is a race condition with the AIL that is resolved by holding the leaf
> buffer across the transaction roll.  So we need to check for that and
> release it upon reentry.
> 

That sounds more like an implementation detail. leaf_bp is set when we
convert from shortform, so isn't this the next state either way?

> 
> > 		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> > 		if (error == -ENOSPC) {
> > 			leaf_to_node(...);
> > 			return -EAGAIN;
> > 		}
> Ok, so lift ENOSPC handler from xfs_attr_leaf_try_add
> 
> > 
> > 		... state stuff for leaf add ...
> > 	} else {
> > 		error = xfs_attr_node_addname(dac);
> > 	}
> > 
> > Hm? Of course, something like that should be incorporated via
> > independent refactoring patches.
> I think what this becomes right now is:
> 
> Drop patch 13
> Add new patch xfs: Lift ENOSPC handler from xfs_attr_leaf_try_add
> 
> ?
> 

Re: above, I don't think the patch needs to necessarily go away. Feel
free to table this for now, just something to think about...

> > 
> > >   	}
> > > -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > -		error = xfs_attr_leaf_addname(args);
> > > -		if (error != -ENOSPC)
> > > -			return error;
> > > +das_add_leaf:
> > > -		/*
> > > -		 * Commit that transaction so that the node_addname()
> > > -		 * call can manage its own transactions.
> > > -		 */
> > > -		error = xfs_defer_finish(&args->trans);
> > > -		if (error)
> > > -			return error;
> > > +	/*
> > > +	 * After a shortform to leaf conversion, we need to hold the leaf and
> > > +	 * cylce out the transaction.  When we get back, we need to release
> > > +	 * the leaf.
> > > +	 */
> > > +	if (*leaf_bp != NULL) {
> > > +		xfs_trans_brelse(args->trans, *leaf_bp);
> > > +		*leaf_bp = NULL;
> > > +	}
> > > -		/*
> > > -		 * Commit the current trans (including the inode) and
> > > -		 * start a new one.
> > > -		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > -		if (error)
> > > +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > +		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> > > +		switch (error) {
> > > +		case -ENOSPC:
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> > > +			return -EAGAIN;
> > > +		case 0:
> > > +			dac->dela_state = XFS_DAS_FOUND_LBLK;
> > > +			return -EAGAIN;
> > > +		default:
> > >   			return error;
> > > -
> > > +		}
> > > +das_leaf:
> > > +		error = xfs_attr_leaf_addname(dac);
> > > +		if (error == -ENOSPC) {
> > > +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> > > +			return -EAGAIN;
> > > +		}
> > > +		return error;
> > >   	}
> > > -
> > > -	error = xfs_attr_node_addname(args);
> > > +das_node:
> > > +	error = xfs_attr_node_addname(dac);
> > >   	return error;
> > >   }
> > > @@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
> > >    *
> > >    * This leaf block cannot have a "remote" value, we only call this routine
> > >    * if bmap_one_block() says there is only one block (ie: no remote blks).
> > > + *
> > > + * 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_leaf_addname(
> > > -	struct xfs_da_args	*args)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	int			error, forkoff;
> > > -	struct xfs_buf		*bp = NULL;
> > > -	struct xfs_inode	*dp = args->dp;
> > > -
> > > -	trace_xfs_attr_leaf_addname(args);
> > > -
> > > -	error = xfs_attr_leaf_try_add(args, bp);
> > > -	if (error)
> > > -		return error;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_buf			*bp = NULL;
> > > +	int				error, forkoff;
> > > +	struct xfs_inode		*dp = args->dp;
> > > -	/*
> > > -	 * Commit the transaction that added the attr name so that
> > > -	 * later routines can manage their own transactions.
> > > -	 */
> > > -	error = xfs_trans_roll_inode(&args->trans, dp);
> > > -	if (error)
> > > -		return error;
> > > +	/* State machine switch */
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_FLIP_LFLAG:
> > > +		goto das_flip_flag;
> > > +	case XFS_DAS_ALLOC_LEAF:
> > > +		goto das_alloc_leaf;
> > > +	case XFS_DAS_RM_LBLK:
> > > +		goto das_rm_lblk;
> > > +	default:
> > > +		break;
> > > +	}
> > >   	/*
> > >   	 * If there was an out-of-line value, allocate the blocks we
> > > @@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
> > >   	 * maximum size of a transaction and/or hit a deadlock.
> > >   	 */
> > >   	if (args->rmtblkno > 0) {
> > > -		error = xfs_attr_rmtval_set(args);
> > > +
> > > +		/* Open coded xfs_attr_rmtval_set without trans handling */
> > > +		error = xfs_attr_rmtval_set_init(dac);
> > > +		if (error)
> > > +			return error;
> > > +
> > > +		/*
> > > +		 * Roll through the "value", allocating blocks on disk as
> > > +		 * required.
> > > +		 */
> > > +das_alloc_leaf:
> > 
> > If we filter out the setup above, it seems like this state could be
> > reduced to check for ->blkcnt > 0.
> By filter out the set up, you mean to introduce the setup flag like you
> mentioned earlier? For example:
> 	if(flags & setup == 0){
> 		setup();
> 		flags |= setup;
> 	}
> 
> To be clear, if we did that, we're talking about adding an "int setup_flags"
> to the dac. And then defineing a XFS_DAC_SETUP_* scheme. Like a
> XFS_DAC_SETUP_ADD_NAME, and an XFS_DAC_SETUP_NODE_REMVE_NAME and so on.
> Because we cant have multiple functions sharing the same set up flag if they
> ever call each others.
> 
> Is that what you mean to imply?  Otherwise I may not be clear on what you
> mean by filtering out the set up.
> 

Well we already have a flag scheme for the defer thing, right? Is there
any reason we couldn't define a similar DAC_DEFER_INIT and let each
operation (add/remove) use it as appropriate? Note again that I'm just
trying to think about potential simplifications. If there are isolated
functions that can be made clearly/simply idempotent (like init/setup
helpers tend to be), then doing so might be helpful. I.e., if
xfs_attr_rmtval_set_init() could do something like:

{
	if (dax->flags & DAC_RMTVAL_SET_INIT)
		return;

	dax->flags |= DAC_RMTVAL_SET_INIT;
	...
}

... and that removes an execution state, then that might be a win. If it
requires more complexity than that, then perhaps the suggestion is not
worthwile. ;P

> > 
> > > +		while (dac->blkcnt > 0) {
> > > +			error = xfs_attr_rmtval_set_blk(dac);
> > > +			if (error)
> > > +				return error;
> > > +
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			dac->dela_state = XFS_DAS_ALLOC_LEAF;
> > > +			return -EAGAIN;
> > > +		}
> > > +
> > > +		error = xfs_attr_rmtval_set_value(args);
> > >   		if (error)
> > >   			return error;
> > >   	}
> > > @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
> > >   		error = xfs_attr3_leaf_flipflags(args);
> > >   		if (error)
> > >   			return error;
> > > -		/*
> > > -		 * Commit the flag value change and start the next trans in
> > > -		 * series.
> > > -		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > -		if (error)
> > > -			return error;
> > > -
> > > +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> > > +		return -EAGAIN;
> > > +das_flip_flag:
> > >   		/*
> > >   		 * Dismantle the "old" attribute/value pair by removing
> > >   		 * a "remote" value (if it exists).
> > >   		 */
> > >   		xfs_attr_restore_rmt_blk(args);
> > > +		xfs_attr_rmtval_invalidate(args);
> > > +das_rm_lblk:
> > >   		if (args->rmtblkno) {
> > > -			error = xfs_attr_rmtval_remove(args);
> > > +			error = __xfs_attr_rmtval_remove(args);
> > > +
> > > +			if (error == -EAGAIN) {
> > > +				dac->dela_state = XFS_DAS_RM_LBLK;
> > > +				return -EAGAIN;
> > > +			}
> > > +
> > 
> > This whole function looks like it could use more refactoring to split
> > out the rename case.
> Hmm, how about we take every thing inside the "if (args->op_flags &
> XFS_DA_OP_RENAME) {"  and put it in a xfs_attr_leaf_rename() helper? Then
> pull the helper into the calling function?
> 

That would help. I'm not sure what the optimal breakdown is off the top
of my head tbh. This function is kind of a logical beast between dealing
with remote blocks and rename and combinations of the two, so I'd have
to stare at that one some more. In general the goal should be to try and
avoid jumping in the middle of logic branches as much as possible so
there's a nice and visible distinction between the state code and the
functional code.

Brian

> > 
> > >   			if (error)
> > >   				return error;
> > >   		}
> > > @@ -799,15 +910,11 @@ xfs_attr_leaf_addname(
> > >   		/*
> > >   		 * If the result is small enough, shrink it all into the inode.
> > >   		 */
> > > -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
> > > +		forkoff = xfs_attr_shortform_allfit(bp, dp);
> > > +		if (forkoff)
> > >   			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
> > > -			/* bp is gone due to xfs_da_shrink_inode */
> > > -			if (error)
> > > -				return error;
> > > -			error = xfs_defer_finish(&args->trans);
> > > -			if (error)
> > > -				return error;
> > > -		}
> > > +
> > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > >   	} else if (args->rmtblkno > 0) {
> > >   		/*
> > > @@ -967,16 +1074,23 @@ xfs_attr_node_hasname(
> > >    *
> > >    * "Remote" attribute values confuse the issue and atomic rename operations
> > >    * add a whole extra layer of confusion on top of that.
> > > + *
> > > + * 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_node_addname(
> > > -	struct xfs_da_args	*args)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	struct xfs_da_state	*state;
> > > -	struct xfs_da_state_blk	*blk;
> > > -	struct xfs_inode	*dp;
> > > -	struct xfs_mount	*mp;
> > > -	int			retval, error;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_da_state		*state = NULL;
> > > +	struct xfs_da_state_blk		*blk;
> > > +	struct xfs_inode		*dp;
> > > +	struct xfs_mount		*mp;
> > > +	int				retval = 0;
> > > +	int				error = 0;
> > >   	trace_xfs_attr_node_addname(args);
> > > @@ -985,7 +1099,21 @@ xfs_attr_node_addname(
> > >   	 */
> > >   	dp = args->dp;
> > >   	mp = dp->i_mount;
> > > -restart:
> > > +
> > > +	/* State machine switch */
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_FLIP_NFLAG:
> > > +		goto das_flip_flag;
> > > +	case XFS_DAS_FOUND_NBLK:
> > > +		goto das_found_nblk;
> > > +	case XFS_DAS_ALLOC_NODE:
> > > +		goto das_alloc_node;
> > > +	case XFS_DAS_RM_NBLK:
> > > +		goto das_rm_nblk;
> > > +	default:
> > > +		break;
> > > +	}
> > > +
> > >   	/*
> > >   	 * Search to see if name already exists, and get back a pointer
> > >   	 * to where it should go.
> > > @@ -1031,19 +1159,13 @@ xfs_attr_node_addname(
> > >   			error = xfs_attr3_leaf_to_node(args);
> > >   			if (error)
> > >   				goto out;
> > > -			error = xfs_defer_finish(&args->trans);
> > > -			if (error)
> > > -				goto out;
> > >   			/*
> > > -			 * Commit the node conversion and start the next
> > > -			 * trans in the chain.
> > > +			 * Restart routine from the top.  No need to set  the
> > > +			 * state
> > >   			 */
> > > -			error = xfs_trans_roll_inode(&args->trans, dp);
> > > -			if (error)
> > > -				goto out;
> > > -
> > > -			goto restart;
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			return -EAGAIN;
> > >   		}
> > >   		/*
> > > @@ -1055,9 +1177,7 @@ xfs_attr_node_addname(
> > >   		error = xfs_da3_split(state);
> > >   		if (error)
> > >   			goto out;
> > > -		error = xfs_defer_finish(&args->trans);
> > > -		if (error)
> > > -			goto out;
> > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > >   	} else {
> > >   		/*
> > >   		 * Addition succeeded, update Btree hashvals.
> > > @@ -1072,13 +1192,9 @@ xfs_attr_node_addname(
> > >   	xfs_da_state_free(state);
> > >   	state = NULL;
> > > -	/*
> > > -	 * Commit the leaf addition or btree split and start the next
> > > -	 * trans in the chain.
> > > -	 */
> > > -	error = xfs_trans_roll_inode(&args->trans, dp);
> > > -	if (error)
> > > -		goto out;
> > > +	dac->dela_state = XFS_DAS_FOUND_NBLK;
> > > +	return -EAGAIN;
> > > +das_found_nblk:
> > 
> > Same deal here. Any time we have this return -EAGAIN followed by a label
> > pattern I think we're going to want to think about refactoring things
> > more first to avoid dumping it in the middle of some unnecessarily large
> > function.
> Ok, similar pattern here too then?  Everything in "if (args->op_flags &
> XFS_DA_OP_RENAME) {" goes in a new xfs_attr_node_rename() helper?  Then
> hoist upwards?
> 
> Thanks for the reviewing!!
> Allison
> 
> > 
> > Brian
> > 
> > >   	/*
> > >   	 * If there was an out-of-line value, allocate the blocks we
> > > @@ -1087,7 +1203,27 @@ xfs_attr_node_addname(
> > >   	 * maximum size of a transaction and/or hit a deadlock.
> > >   	 */
> > >   	if (args->rmtblkno > 0) {
> > > -		error = xfs_attr_rmtval_set(args);
> > > +		/* Open coded xfs_attr_rmtval_set without trans handling */
> > > +		error = xfs_attr_rmtval_set_init(dac);
> > > +		if (error)
> > > +			return error;
> > > +
> > > +		/*
> > > +		 * Roll through the "value", allocating blocks on disk as
> > > +		 * required.
> > > +		 */
> > > +das_alloc_node:
> > > +		while (dac->blkcnt > 0) {
> > > +			error = xfs_attr_rmtval_set_blk(dac);
> > > +			if (error)
> > > +				return error;
> > > +
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			dac->dela_state = XFS_DAS_ALLOC_NODE;
> > > +			return -EAGAIN;
> > > +		}
> > > +
> > > +		error = xfs_attr_rmtval_set_value(args);
> > >   		if (error)
> > >   			return error;
> > >   	}
> > > @@ -1110,18 +1246,26 @@ xfs_attr_node_addname(
> > >   		 * Commit the flag value change and start the next trans in
> > >   		 * series
> > >   		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > -		if (error)
> > > -			goto out;
> > > -
> > > +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> > > +		return -EAGAIN;
> > > +das_flip_flag:
> > >   		/*
> > >   		 * Dismantle the "old" attribute/value pair by removing
> > >   		 * a "remote" value (if it exists).
> > >   		 */
> > >   		xfs_attr_restore_rmt_blk(args);
> > > +		xfs_attr_rmtval_invalidate(args);
> > > +
> > > +das_rm_nblk:
> > >   		if (args->rmtblkno) {
> > > -			error = xfs_attr_rmtval_remove(args);
> > > +			error = __xfs_attr_rmtval_remove(args);
> > > +
> > > +			if (error == -EAGAIN) {
> > > +				dac->dela_state = XFS_DAS_RM_NBLK;
> > > +				return -EAGAIN;
> > > +			}
> > > +
> > >   			if (error)
> > >   				return error;
> > >   		}
> > > @@ -1139,7 +1283,6 @@ xfs_attr_node_addname(
> > >   		error = xfs_da3_node_lookup_int(state, &retval);
> > >   		if (error)
> > >   			goto out;
> > > -
> > >   		/*
> > >   		 * Remove the name and update the hashvals in the tree.
> > >   		 */
> > > @@ -1147,7 +1290,6 @@ xfs_attr_node_addname(
> > >   		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> > >   		error = xfs_attr3_leaf_remove(blk->bp, args);
> > >   		xfs_da3_fixhashpath(state, &state->path);
> > > -
> > >   		/*
> > >   		 * Check to see if the tree needs to be collapsed.
> > >   		 */
> > > @@ -1155,11 +1297,9 @@ xfs_attr_node_addname(
> > >   			error = xfs_da3_join(state);
> > >   			if (error)
> > >   				goto out;
> > > -			error = xfs_defer_finish(&args->trans);
> > > -			if (error)
> > > -				goto out;
> > > -		}
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +		}
> > >   	} else if (args->rmtblkno > 0) {
> > >   		/*
> > >   		 * Added a "remote" value, just clear the incomplete flag.
> > > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > > index 0e8ae1a..67af9d1 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.h
> > > +++ b/fs/xfs/libxfs/xfs_attr.h
> > > @@ -93,6 +93,16 @@ enum xfs_delattr_state {
> > >   				      /* Zero is uninitalized */
> > >   	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
> > >   	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
> > > +	XFS_DAS_ADD_LEAF,	      /* We are adding a leaf attr */
> > > +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
> > > +	XFS_DAS_LEAF_TO_NODE,	      /* Converted leaf to node */
> > > +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
> > > +	XFS_DAS_ALLOC_LEAF,	      /* We are allocating leaf blocks */
> > > +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
> > > +	XFS_DAS_RM_LBLK,	      /* A rename is removing leaf blocks */
> > > +	XFS_DAS_ALLOC_NODE,	      /* We are allocating node blocks */
> > > +	XFS_DAS_FLIP_NFLAG,	      /* Flipped node INCOMPLETE attr flag */
> > > +	XFS_DAS_RM_NBLK,	      /* A rename is removing node blocks */
> > >   };
> > >   /*
> > > @@ -105,8 +115,13 @@ enum xfs_delattr_state {
> > >    */
> > >   struct xfs_delattr_context {
> > >   	struct xfs_da_args      *da_args;
> > > +	struct xfs_bmbt_irec	map;
> > > +	struct xfs_buf		*leaf_bp;
> > > +	xfs_fileoff_t		lfileoff;
> > >   	struct xfs_da_state     *da_state;
> > >   	struct xfs_da_state_blk *blk;
> > > +	xfs_dablk_t		lblkno;
> > > +	int			blkcnt;
> > >   	unsigned int            flags;
> > >   	enum xfs_delattr_state  dela_state;
> > >   };
> > > @@ -126,6 +141,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
> > >   int xfs_attr_get(struct xfs_da_args *args);
> > >   int xfs_attr_set(struct xfs_da_args *args);
> > >   int xfs_attr_set_args(struct xfs_da_args *args);
> > > +int xfs_attr_set_iter(struct xfs_delattr_context *dac, struct xfs_buf **leaf_bp);
> > >   int xfs_has_attr(struct xfs_da_args *args);
> > >   int xfs_attr_remove_args(struct xfs_da_args *args);
> > >   int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
> > > diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> > > index f55402b..4d15f45 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> > > +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> > > @@ -19,6 +19,7 @@
> > >   #include "xfs_bmap_btree.h"
> > >   #include "xfs_bmap.h"
> > >   #include "xfs_attr_sf.h"
> > > +#include "xfs_attr.h"
> > >   #include "xfs_attr_remote.h"
> > >   #include "xfs_attr.h"
> > >   #include "xfs_attr_leaf.h"
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > > index fd4be9d..9607fd2 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > > @@ -443,7 +443,7 @@ xfs_attr_rmtval_get(
> > >    * Find a "hole" in the attribute address space large enough for us to drop the
> > >    * new attribute's value into
> > >    */
> > > -STATIC int
> > > +int
> > >   xfs_attr_rmt_find_hole(
> > >   	struct xfs_da_args	*args)
> > >   {
> > > @@ -470,7 +470,7 @@ xfs_attr_rmt_find_hole(
> > >   	return 0;
> > >   }
> > > -STATIC int
> > > +int
> > >   xfs_attr_rmtval_set_value(
> > >   	struct xfs_da_args	*args)
> > >   {
> > > @@ -630,6 +630,71 @@ xfs_attr_rmtval_set(
> > >   }
> > >   /*
> > > + * Find a hole for the attr and store it in the delayed attr context.  This
> > > + * initializes the context to roll through allocating an attr extent for a
> > > + * delayed attr operation
> > > + */
> > > +int
> > > +xfs_attr_rmtval_set_init(
> > > +	struct xfs_delattr_context	*dac)
> > > +{
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_bmbt_irec		*map = &dac->map;
> > > +	int error;
> > > +
> > > +	dac->lblkno = 0;
> > > +	dac->lfileoff = 0;
> > > +	dac->blkcnt = 0;
> > > +	args->rmtblkcnt = 0;
> > > +	args->rmtblkno = 0;
> > > +	memset(map, 0, sizeof(struct xfs_bmbt_irec));
> > > +
> > > +	error = xfs_attr_rmt_find_hole(args);
> > > +	if (error)
> > > +		return error;
> > > +
> > > +	dac->blkcnt = args->rmtblkcnt;
> > > +	dac->lblkno = args->rmtblkno;
> > > +
> > > +	return error;
> > > +}
> > > +
> > > +/*
> > > + * Write one block of the value associated with an attribute into the
> > > + * out-of-line buffer that we have defined for it. This is similar to a subset
> > > + * of xfs_attr_rmtval_set, but records the current block to the delayed attr
> > > + * context, and leaves transaction handling to the caller.
> > > + */
> > > +int
> > > +xfs_attr_rmtval_set_blk(
> > > +	struct xfs_delattr_context	*dac)
> > > +{
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_inode		*dp = args->dp;
> > > +	struct xfs_bmbt_irec		*map = &dac->map;
> > > +	int nmap;
> > > +	int error;
> > > +
> > > +	nmap = 1;
> > > +	error = xfs_bmapi_write(args->trans, dp,
> > > +		  (xfs_fileoff_t)dac->lblkno,
> > > +		  dac->blkcnt, XFS_BMAPI_ATTRFORK,
> > > +		  args->total, map, &nmap);
> > > +	if (error)
> > > +		return error;
> > > +
> > > +	ASSERT(nmap == 1);
> > > +	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
> > > +	       (map->br_startblock != HOLESTARTBLOCK));
> > > +
> > > +	/* roll attribute extent map forwards */
> > > +	dac->lblkno += map->br_blockcount;
> > > +	dac->blkcnt -= map->br_blockcount;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/*
> > >    * Remove the value associated with an attribute by deleting the
> > >    * out-of-line buffer that it is stored on.
> > >    */
> > > @@ -671,48 +736,6 @@ xfs_attr_rmtval_invalidate(
> > >   }
> > >   /*
> > > - * Remove the value associated with an attribute by deleting the
> > > - * out-of-line buffer that it is stored on.
> > > - */
> > > -int
> > > -xfs_attr_rmtval_remove(
> > > -	struct xfs_da_args      *args)
> > > -{
> > > -	xfs_dablk_t		lblkno;
> > > -	int			blkcnt;
> > > -	int			error = 0;
> > > -	int			done = 0;
> > > -
> > > -	trace_xfs_attr_rmtval_remove(args);
> > > -
> > > -	error = xfs_attr_rmtval_invalidate(args);
> > > -	if (error)
> > > -		return error;
> > > -	/*
> > > -	 * Keep de-allocating extents until the remote-value region is gone.
> > > -	 */
> > > -	lblkno = args->rmtblkno;
> > > -	blkcnt = args->rmtblkcnt;
> > > -	while (!done) {
> > > -		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
> > > -				    XFS_BMAPI_ATTRFORK, 1, &done);
> > > -		if (error)
> > > -			return error;
> > > -		error = xfs_defer_finish(&args->trans);
> > > -		if (error)
> > > -			return error;
> > > -
> > > -		/*
> > > -		 * Close out trans and start the next one in the chain.
> > > -		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > -		if (error)
> > > -			return error;
> > > -	}
> > > -	return 0;
> > > -}
> > > -
> > > -/*
> > >    * Remove the value associated with an attribute by deleting the out-of-line
> > >    * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
> > >    * transaction and recall the function
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> > > index ee3337b..482dff9 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.h
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> > > @@ -15,4 +15,8 @@ int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
> > >   		xfs_buf_flags_t incore_flags);
> > >   int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
> > >   int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
> > > +int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
> > > +int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
> > > +int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
> > > +int xfs_attr_rmtval_set_init(struct xfs_delattr_context *dac);
> > >   #endif /* __XFS_ATTR_REMOTE_H__ */
> > > diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
> > > index c42f90e..3e8cec5 100644
> > > --- a/fs/xfs/xfs_attr_inactive.c
> > > +++ b/fs/xfs/xfs_attr_inactive.c
> > > @@ -15,6 +15,7 @@
> > >   #include "xfs_da_format.h"
> > >   #include "xfs_da_btree.h"
> > >   #include "xfs_inode.h"
> > > +#include "xfs_attr.h"
> > >   #include "xfs_attr_remote.h"
> > >   #include "xfs_trans.h"
> > >   #include "xfs_bmap.h"
> > > diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> > > index a4323a6..26dc8bf 100644
> > > --- a/fs/xfs/xfs_trace.h
> > > +++ b/fs/xfs/xfs_trace.h
> > > @@ -1784,7 +1784,6 @@ DEFINE_ATTR_EVENT(xfs_attr_refillstate);
> > >   DEFINE_ATTR_EVENT(xfs_attr_rmtval_get);
> > >   DEFINE_ATTR_EVENT(xfs_attr_rmtval_set);
> > > -DEFINE_ATTR_EVENT(xfs_attr_rmtval_remove);
> > >   #define DEFINE_DA_EVENT(name) \
> > >   DEFINE_EVENT(xfs_da_class, name, \
> > > -- 
> > > 2.7.4
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-16 10:58           ` Brian Foster
@ 2020-04-16 22:41             ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-16 22:41 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs, dchinner



On 4/16/20 3:58 AM, Brian Foster wrote:
> On Wed, Apr 15, 2020 at 08:17:07PM -0700, Allison Collins wrote:
>>
>>
>> On 4/15/20 4:46 AM, Brian Foster wrote:
>>> On Tue, Apr 14, 2020 at 02:35:43PM -0700, Allison Collins wrote:
>>>>
>>>>
>>>> On 4/13/20 5:30 AM, Brian Foster wrote:
>>>>> On Fri, Apr 03, 2020 at 03:12:27PM -0700, Allison Collins wrote:
>>>>>> This patch modifies the attr remove routines to be delay ready. This
>>>>>> means they no longer roll or commit transactions, but instead return
>>>>>> -EAGAIN to have the calling routine roll and refresh the transaction. In
>>>>>> this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
>>>>>> uses a sort of state machine like switch to keep track of where it was
>>>>>> when EAGAIN was returned. xfs_attr_node_removename has also been
>>>>>> modified to use the switch, and a new version of xfs_attr_remove_args
>>>>>> consists of a simple loop to refresh the transaction until the operation
>>>>>> is completed.
>>>>>>
>>>>>> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
>>>>>> counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
>>>>>> helper) and then __xfs_attr_rmtval_remove. We will rename
>>>>>> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
>>>>>> done.
>>>>>>
>>>>>> This patch also adds a new struct xfs_delattr_context, which we will use
>>>>>> to keep track of the current state of an attribute operation. The new
>>>>>> xfs_delattr_state enum is used to track various operations that are in
>>>>>> progress so that we know not to repeat them, and resume where we left
>>>>>> off before EAGAIN was returned to cycle out the transaction. Other
>>>>>> members take the place of local variables that need to retain their
>>>>>> values across multiple function recalls.
>>>>>>
>>>>>> Below is a state machine diagram for attr remove operations. The
>>>>>> XFS_DAS_* states indicate places where the function would return
>>>>>> -EAGAIN, and then immediately resume from after being recalled by the
>>>>>> calling function.  States marked as a "subroutine state" indicate that
>>>>>> they belong to a subroutine, and so the calling function needs to pass
>>>>>> them back to that subroutine to allow it to finish where it left off.
>>>>>> But they otherwise do not have a role in the calling function other than
>>>>>> just passing through.
>>>>>>
>>>>>>     xfs_attr_remove_iter()
>>>>>>             XFS_DAS_RM_SHRINK     ─┐
>>>>>>             (subroutine state)     │
>>>>>>                                    │
>>>>>>             XFS_DAS_RMTVAL_REMOVE ─┤
>>>>>>             (subroutine state)     │
>>>>>>                                    └─>xfs_attr_node_removename()
>>>>>>                                                     │
>>>>>>                                                     v
>>>>>>                                             need to remove
>>>>>>                                       ┌─n──  rmt blocks?
>>>>>>                                       │             │
>>>>>>                                       │             y
>>>>>>                                       │             │
>>>>>>                                       │             v
>>>>>>                                       │  ┌─>XFS_DAS_RMTVAL_REMOVE
>>>>>>                                       │  │          │
>>>>>>                                       │  │          v
>>>>>>                                       │  └──y── more blks
>>>>>>                                       │         to remove?
>>>>>>                                       │             │
>>>>>>                                       │             n
>>>>>>                                       │             │
>>>>>>                                       │             v
>>>>>>                                       │         need to
>>>>>>                                       └─────> shrink tree? ─n─┐
>>>>>>                                                     │         │
>>>>>>                                                     y         │
>>>>>>                                                     │         │
>>>>>>                                                     v         │
>>>>>>                                             XFS_DAS_RM_SHRINK │
>>>>>>                                                     │         │
>>>>>>                                                     v         │
>>>>>>                                                    done <─────┘
>>>>>>
>>>>>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>>>>>> ---
>>>>>
>>>>> All in all this is starting to look much more simple to me, at least in
>>>>> the remove path. ;P There's only a few states and the markers that are
>>>>> introduced are fairly straightforward, etc. Comments to follow..
>>>>>
>>>>>>     fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
>>>>>>     fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
>>>>>>     2 files changed, 168 insertions(+), 38 deletions(-)
>>>>>>
>>>>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>>>>> index d735570..f700976 100644
>>>>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>>>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>>>>> @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>>>>>>      */
>>>>>>     STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>>>>>>     STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
>>>>>> -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
>>>>>> +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>>>>>>     STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>>>>>     /*
>>>>>> @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>>>>>      */
>>>>>>     STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>>>>>>     STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
>>>>>> -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
>>>>>> +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>>>>>>     STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>>>>>>     				 struct xfs_da_state **state);
>>>>>>     STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
>>>>>>     STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
>>>>>> +STATIC void
>>>>>> +xfs_delattr_context_init(
>>>>>> +	struct xfs_delattr_context	*dac,
>>>>>> +	struct xfs_da_args		*args)
>>>>>> +{
>>>>>> +	memset(dac, 0, sizeof(struct xfs_delattr_context));
>>>>>> +	dac->da_args = args;
>>>>>> +}
>>>>>> +
>>>>>>     int
>>>>>>     xfs_inode_hasattr(
>>>>>>     	struct xfs_inode	*ip)
>>>>>> @@ -356,20 +365,66 @@ xfs_has_attr(
>>>>>>      */
>>>>>>     int
>>>>>>     xfs_attr_remove_args(
>>>>>> -	struct xfs_da_args      *args)
>>>>>> +	struct xfs_da_args	*args)
>>>>>>     {
>>>>>> +	int			error = 0;
>>>>>> +	struct			xfs_delattr_context dac;
>>>>>> +
>>>>>> +	xfs_delattr_context_init(&dac, args);
>>>>>> +
>>>>>> +	do {
>>>>>> +		error = xfs_attr_remove_iter(&dac);
>>>>>> +		if (error != -EAGAIN)
>>>>>> +			break;
>>>>>> +
>>>>>> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
>>>>>> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
>>>>>> +			error = xfs_defer_finish(&args->trans);
>>>>>> +			if (error)
>>>>>> +				break;
>>>>>> +		}
>>>>>> +
>>>>>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>>>> +		if (error)
>>>>>> +			break;
>>>>>> +	} while (true);
>>>>>> +
>>>>>> +	return error;
>>>>>> +}
>>>>>> +
>>>>>> +/*
>>>>>> + * Remove the attribute specified in @args.
>>>>>> + *
>>>>>> + * This function may return -EAGAIN to signal that the transaction needs to be
>>>>>> + * rolled.  Callers should continue calling this function until they receive a
>>>>>> + * return value other than -EAGAIN.
>>>>>> + */
>>>>>> +int
>>>>>> +xfs_attr_remove_iter(
>>>>>> +	struct xfs_delattr_context *dac)
>>>>>> +{
>>>>>> +	struct xfs_da_args	*args = dac->da_args;
>>>>>>     	struct xfs_inode	*dp = args->dp;
>>>>>>     	int			error;
>>>>>> +	/* State machine switch */
>>>>>> +	switch (dac->dela_state) {
>>>>>> +	case XFS_DAS_RM_SHRINK:
>>>>>> +	case XFS_DAS_RMTVAL_REMOVE:
>>>>>> +		return xfs_attr_node_removename(dac);
>>>>>> +	default:
>>>>>> +		break;
>>>>>> +	}
>>>>>> +
>>>>>
>>>>> Hmm.. so we're duplicating the call instead of using labels..?
>>>>
>>>> Yes, this was a suggestion made during v7.  I suspect Dave may have been
>>>> wanting to simplify things by escaping the use of labels.  At least in so
>>>> far as the remove path is concerned.  Though he may not have realized this
>>>> would create a duplication call?  I will cc him here; the conditions for
>>>> calling xfs_attr_node_removename are: the below if/else sequence exhausts
>>>> with no successes, and defaults into the else case (ie: the entry
>>>> condition), OR one of the above states is set (which is a re-entry
>>>> condition)
>>>>
>>>
>>> Ok.
>>>
>>>>
>>>> I'm
>>>>> wondering if this can be elegantly combined with the if/else branches
>>>>> below, particularly since node format is the only situation that seems
>>>>> to require a roll here.
>>>>>
>>>>>>     	if (!xfs_inode_hasattr(dp)) {
>>>>>>     		error = -ENOATTR;
>>>>>>     	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>>>>>>     		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>>>>>>     		error = xfs_attr_shortform_remove(args);
>>>>>>     	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>>>> -		error = xfs_attr_leaf_removename(args);
>>>>>> +		error = xfs_attr_leaf_removename(dac);
>>>>>>     	} else {
>>>>>> -		error = xfs_attr_node_removename(args);
>>>>>> +		error = xfs_attr_node_removename(dac);
>>>>>>     	}
>>>>>>     	return error;
>>>>
>>>> If we want to try and combine this into if/elses with no duplication, I
>>>> believe the simplest arrangement would look something like this:
>>>>
>>>>
>>>> int
>>>> xfs_attr_remove_iter(
>>>> 	struct xfs_delattr_context *dac)
>>>> {
>>>> 	struct xfs_da_args	*args = dac->da_args;
>>>> 	struct xfs_inode	*dp = args->dp;
>>>>
>>>> 	if (dac->dela_state != XFS_DAS_RM_SHRINK &&
>>>> 	    dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
>>>> 		if (!xfs_inode_hasattr(dp)) {
>>>> 			return -ENOATTR;
>>>> 		} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>>>> 			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>>>> 			return xfs_attr_shortform_remove(args);
>>>> 		} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>> 			return xfs_attr_leaf_removename(dac);
>>>> 		}
>>>> 	}
>>>>
>>>> 	return xfs_attr_node_removename(dac);
>>>> }
>>>>
>>>> Let me know what folks think of that.  I'm not always clear on where people
>>>> stand with aesthetics. (IE, is it better to have a duplicate call if it gets
>>>> rid of a label?  Is the solution with the least amount of LOC always
>>>> preferable?)  This area seems simple enough maybe we can get it ironed out
>>>> here with out another version.
>>>>
>>>> IMHO I think the above code sort of obfuscates that the code flow is really
>>>> just one if/else switch with one function that has the statemachine
>>>> behavior.  But its not bad either if that's what people prefer.  I'd like to
>>>> find something every can be sort of happy with.  :-)
>>>>
>>>
>>> If you want my .02, some combination of the above is cleanest from an
>>> aesthetic pov:
>>>
>>> {
>>> 	...
>>> 	if (RM_SHRINK || RMTVAL_REMOVE)
>>> 		goto node;
>>>
>>> 	if (!hasattr)
>>> 		return -ENOATTR;
>>> 	else if (local)
>>> 		return shortform_remove();
>>> 	else if (oneblock)
>>> 		return leaf_removename();
>>>
>>> node:
>>> 	return node_removename();
>>> }
>>>
>>> I find that easiest to read at a glance, but I don't feel terribly
>>> strongly about it I guess.
>>
>> I am entirely fine with that if everyone else is :-)
>>
>>>
>>>>>> @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
>>>>>>      */
>>>>>>     STATIC int
>>>>>>     xfs_attr_leaf_removename(
>>>>>> -	struct xfs_da_args	*args)
>>>>>> +	struct xfs_delattr_context	*dac)
>>>>>>     {
>>>>>> -	struct xfs_inode	*dp;
>>>>>> -	struct xfs_buf		*bp;
>>>>>> -	int			error, forkoff;
>>>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>>>> +	struct xfs_inode		*dp;
>>>>>> +	struct xfs_buf			*bp;
>>>>>> +	int				error, forkoff;
>>>>>>     	trace_xfs_attr_leaf_removename(args);
>>>>>> @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
>>>>>>     		/* bp is gone due to xfs_da_shrink_inode */
>>>>>>     		if (error)
>>>>>>     			return error;
>>>>>> -		error = xfs_defer_finish(&args->trans);
>>>>>> -		if (error)
>>>>>> -			return error;
>>>>>> +
>>>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>>
>>>>> There's no -EAGAIN return here, is this an exit path for the remove?
>>>> I think so, maybe I can remove this and the other one you pointed out in
>>>> patch 12 along with the other unneeded transaction handling.
>>>>
>>>>>
>>>>>>     	}
>>>>>>     	return 0;
>>>>>>     }
>>>>>> @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
>>>>>>      */
>>>>>>     STATIC int
>>>>>>     xfs_attr_node_shrink(
>>>>>> -	struct xfs_da_args	*args,
>>>>>> -	struct xfs_da_state     *state)
>>>>>> +	struct xfs_delattr_context	*dac,
>>>>>> +	struct xfs_da_state		*state)
>>>>>>     {
>>>>>> -	struct xfs_inode	*dp = args->dp;
>>>>>> -	int			error, forkoff;
>>>>>> -	struct xfs_buf		*bp;
>>>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>>>> +	struct xfs_inode		*dp = args->dp;
>>>>>> +	int				error, forkoff;
>>>>>> +	struct xfs_buf			*bp;
>>>>>>     	/*
>>>>>>     	 * Have to get rid of the copy of this dabuf in the state.
>>>>>> @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
>>>>>>     		if (error)
>>>>>>     			return error;
>>>>>> -		error = xfs_defer_finish(&args->trans);
>>>>>> -		if (error)
>>>>>> -			return error;
>>>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>>
>>>>> Same question here.
>>>>>
>>>>>>     	} else
>>>>>>     		xfs_trans_brelse(args->trans, bp);
>>>>>> @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
>>>>>>     /*
>>>>>>      * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
>>>>>> - * the blocks are valid.  Any remote blocks will be marked incomplete.
>>>>>> + * the blocks are valid.  Any remote blocks will be marked incomplete and
>>>>>> + * invalidated.
>>>>>>      */
>>>>>>     STATIC
>>>>>>     int xfs_attr_node_removename_setup(
>>>>>> -	struct xfs_da_args	*args,
>>>>>> -	struct xfs_da_state	**state)
>>>>>> +	struct xfs_delattr_context	*dac,
>>>>>> +	struct xfs_da_state		**state)
>>>>>>     {
>>>>>> +	struct xfs_da_args	*args = dac->da_args;
>>>>>>     	int			error;
>>>>>>     	struct xfs_da_state_blk	*blk;
>>>>>> @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
>>>>>>     	ASSERT(blk->bp != NULL);
>>>>>>     	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>>>>> +	/*
>>>>>> +	 * Store blk and state in the context incase we need to cycle out the
>>>>>> +	 * transaction
>>>>>> +	 */
>>>>>> +	dac->blk = blk;
>>>>>> +	dac->da_state = *state;
>>>>>> +
>>>>>>     	if (args->rmtblkno > 0) {
>>>>>>     		error = xfs_attr_leaf_mark_incomplete(args, *state);
>>>>>>     		if (error)
>>>>>>     			return error;
>>>>>> +
>>>>>> +		error = xfs_attr_rmtval_invalidate(args);
>>>>>> +		if (error)
>>>>>> +			return error;
>>>>>
>>>>> Seems like this moves code, which should probably happen in a separate
>>>>> patch.
>>>> Ok, this pairs with the  xfs_attr_rmtval_remove to __xfs_attr_rmtval_remove
>>>> below.  Basically xfs_attr_rmtval_remove is the combination of
>>>> xfs_attr_rmtval_invalidate and __xfs_attr_rmtval_remove. So thats why we see
>>>> xfs_attr_rmtval_remove going away and xfs_attr_rmtval_invalidate +
>>>> __xfs_attr_rmtval_remove coming in.
>>>>
>>>> How about a patch that pulls xfs_attr_rmtval_invalidate out of
>>>> xfs_attr_rmtval_remove and into the calling functions?  I think that might
>>>> be more clear.
>>>>
>>>
>>> Yes, separate patch please. I think that if the earlier refactoring
>>> parts of the series are split out properly (i.e., no dependencies on
>>> subsequent patches) and reviewed, perhaps we can start getting some of
>>> those patches merged while the latter bits are worked out.
>> Ok then, will do
>>
>>>
>>>>>
>>>>>>     	}
>>>>>>     	return 0;
>>>>>> @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
>>>>>>     {
>>>>>>     	int			error = 0;
>>>>>> -	error = xfs_attr_rmtval_remove(args);
>>>>>> +	/*
>>>>>> +	 * May return -EAGAIN to request that the caller recall this function
>>>>>> +	 */
>>>>>> +	error = __xfs_attr_rmtval_remove(args);
>>>>>>     	if (error)
>>>>>>     		return error;
>>>>>> @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
>>>>>>      * This will involve walking down the Btree, and may involve joining
>>>>>>      * leaf nodes and even joining intermediate nodes up to and including
>>>>>>      * the root node (a special case of an intermediate node).
>>>>>> + *
>>>>>> + * This routine is meant to function as either an inline or 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_node_removename(
>>>>>> -	struct xfs_da_args	*args)
>>>>>> +	struct xfs_delattr_context	*dac)
>>>>>>     {
>>>>>> +	struct xfs_da_args	*args = dac->da_args;
>>>>>>     	struct xfs_da_state	*state;
>>>>>>     	struct xfs_da_state_blk	*blk;
>>>>>>     	int			retval, error;
>>>>>>     	struct xfs_inode	*dp = args->dp;
>>>>>>     	trace_xfs_attr_node_removename(args);
>>>>>> +	state = dac->da_state;
>>>>>> +	blk = dac->blk;
>>>>>> +
>>>>>> +	/* State machine switch */
>>>>>> +	switch (dac->dela_state) {
>>>>>> +	case XFS_DAS_RMTVAL_REMOVE:
>>>>>> +		goto das_rmtval_remove;
>>>>>> +	case XFS_DAS_RM_SHRINK:
>>>>>> +		goto das_rm_shrink;
>>>>>> +	default:
>>>>>> +		break;
>>>>>> +	}
>>>>>> -	error = xfs_attr_node_removename_setup(args, &state);
>>>>>> +	error = xfs_attr_node_removename_setup(dac, &state);
>>>>>>     	if (error)
>>>>>>     		goto out;
>>>>>> @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
>>>>>>     	 * This is done before we remove the attribute so that we don't
>>>>>>     	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>>>>>     	 */
>>>>>> +
>>>>>> +das_rmtval_remove:
>>>>>> +
>>>>>
>>>>> I wonder if we need this label just to protect the setup. Perhaps if we
>>>>> had something like:
>>>>>
>>>>> 	/* set up the remove only once... */
>>>>> 	if (dela_state == 0)
>>>>> 		error = xfs_attr_node_removename_setup(...);
>>>>>
>>>>> ... we could reduce another state.
>>>>>
>>>>> We could also accomplish the same thing with an explicit state to
>>>>> indicate the setup already occurred or a new dac flag, though I'm not
>>>>> sure a flag is appropriate if it would only be used here.
>>>>>
>>>>> Brian
>>>>
>>>> Mmmm, dela_state == 0 will conflict a bit when we get into fully delayed
>>>> attrs.  Basically when this is getting called from the delayed operations
>>>> path, it sets dela_state to a new XFS_DAS_INIT. Because we have to set up
>>>> args mid fight, we need the extra state to not do that twice.
>>>>
>>>
>>> Can we address that when the conflict is introduced?
>> Sure, i was just looking ahead, but focus should stay here
>>
>>>
>>>> But even without getting into that right away, what you're proposing only
>>>> gets rid of the label.  It doesnt get rid of the state.  We still have to
>>>> set the state to not be zero (or what ever the initial value is).  So we
>>>> still need the unique value of  XFS_DAS_RMTVAL_REMOVE
>>>>
>>>
>>> Yeah, I was partly thinking of the setup call being tied to a flag
>>> rather than a state. That way the logic is something like the typical:
>>>
>>> 	if (!setup)
>>> 		do_setup();
>>> 	...
>>>
>>> ... and it's one less bit of code tied into the state machine. All in
>>> all, it's more that having a label right at the top of a function like
>>> that kind of looks like it's asking for some form of simplification.
>> Ok, this is similar to the discussion going on in the next patch.  To be
>> clear, if we add a flag, we need to keep it in the delay_attr_context so
>> that it persists across re-calls.  We dont want multiple functions shareing
>> the same setup flag, so really we're talking about a new flag scheme like
>> XFS_DAC_SETUP_* for any function that needs a set up flag? Is that what you
>> had in mind?
>>
> 
> Yep, pretty much. It's not clear to me if we can reuse one general INIT
> flag across operations or need several such flags, but I'm not sure I
> understand why we couldn't reuse the existing dac->flags at least..
> 
> Brian

Ok, I think I likely answered this in the discussion for the next patch. 
  I forgot I had already added a flag member to the delay attr context, 
I will move forward with an init flag scheme.  Thanks again for all the 
reviewing!

Allison

> 
>>>
>>>> Really what you would need here in order to do what you are describeing is
>>>> dela_state != XFS_DAS_RMTVAL_REMOVE.  If I assume to simplify away to the
>>>> lease amount of LOC we get this:
>>>>
>>>>
>>>> STATIC int
>>>> xfs_attr_node_removename(
>>>>           struct xfs_delattr_context      *dac)
>>>> {
>>>>           struct xfs_da_args      *args = dac->da_args;
>>>>           struct xfs_da_state     *state;
>>>>           struct xfs_da_state_blk *blk;
>>>>           int                     retval, error;
>>>>           struct xfs_inode        *dp = args->dp;
>>>>
>>>>           trace_xfs_attr_node_removename(args);
>>>>           state = dac->da_state;
>>>>           blk = dac->blk;
>>>>
>>>>           if (dac->dela_state == XFS_DAS_RM_SHRINK) {
>>>>                   goto das_rm_shrink;
>>>>           } else if (dac->dela_state != XFS_DAS_RMTVAL_REMOVE) {
>>>>                   error = xfs_attr_node_removename_setup(dac, &state);
>>>>                   if (error)
>>>>                           goto out;
>>>>           }
>>>>
>>>>           /*
>>>>            * If there is an out-of-line value, de-allocate the blocks.
>>>>            * This is done before we remove the attribute so that we don't
>>>>            * overflow the maximum size of a transaction and/or hit a deadlock.
>>>>            */
>>>>
>>>>           if (args->rmtblkno > 0) {
>>>>                   error = xfs_attr_node_removename_rmt(args, state);
>>>>                   if (error) {
>>>>                           if (error == -EAGAIN)
>>>>                                   dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
>>>>                           return error;
>>>>                   }
>>>>           }
>>>>
>>>> .....
>>>>
>>>>
>>>> Let me know what folks think of this.  Again, I think I like the switches
>>>> and the labels just because it makes it more clear where the jump points
>>>> are, even if its more LOC.  But again, this isnt bad either if this is more
>>>> preferable to folks.  If there's another arrangment that is preferable, let
>>>> me know, it's not difficult to run it through the test cases to make sure
>>>> it's functional.  It may be a faster way to hash out what people want to
>>>> see.
>>>>
>>>
>>> I prefer to see the state management stuff as boilerplate as possible.
>>> The above pattern of creating separate reentry calls to the same
>>> functions is not nearly as clear to me, particularly in this instance
>>> where we have multiple branches of reentry logic (as opposed to the
>>> earlier example of only one).
>>>
>>> IOW, I agree that the jumps are preferable and more intuitive. I'm just
>>> trying to be reductive by considering what could be factored out vs.
>>> trying to fundamentally rework the approach or aggressively reduce LOC
>>> or anything like that. IMO, simplicity of the code is usually top
>>> priority.
>>>
>>> Brian
>>
>> Ok, lets firm up what we want this setup flag to look like, and then we can
>> simplify the current label and goto scheme :-)
>>
>> Thank you!!
>> Allison
>>
>>
>>>
>>>> Thank you again for all the reviewing!!!
>>>>
>>>> Allison
>>>>
>>>>>
>>>>>>     	if (args->rmtblkno > 0) {
>>>>>>     		error = xfs_attr_node_removename_rmt(args, state);
>>>>>> -		if (error)
>>>>>> -			goto out;
>>>>>> +		if (error) {
>>>>>> +			if (error == -EAGAIN)
>>>>>> +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
>>>>>> +			return error;
>>>>>> +		}
>>>>>>     	}
>>>>>>     	/*
>>>>>> @@ -1291,22 +1385,20 @@ xfs_attr_node_removename(
>>>>>>     		error = xfs_da3_join(state);
>>>>>>     		if (error)
>>>>>>     			goto out;
>>>>>> -		error = xfs_defer_finish(&args->trans);
>>>>>> -		if (error)
>>>>>> -			goto out;
>>>>>> -		/*
>>>>>> -		 * Commit the Btree join operation and start a new trans.
>>>>>> -		 */
>>>>>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>>>>>> -		if (error)
>>>>>> -			goto out;
>>>>>> +
>>>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>>> +		dac->dela_state = XFS_DAS_RM_SHRINK;
>>>>>> +		return -EAGAIN;
>>>>>>     	}
>>>>>> +das_rm_shrink:
>>>>>> +	dac->dela_state = XFS_DAS_RM_SHRINK;
>>>>>> +
>>>>>>     	/*
>>>>>>     	 * If the result is small enough, push it all into the inode.
>>>>>>     	 */
>>>>>>     	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>>>>>> -		error = xfs_attr_node_shrink(args, state);
>>>>>> +		error = xfs_attr_node_shrink(dac, state);
>>>>>>     	error = 0;
>>>>>>     out:
>>>>>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>>>>>> index 66575b8..0e8ae1a 100644
>>>>>> --- a/fs/xfs/libxfs/xfs_attr.h
>>>>>> +++ b/fs/xfs/libxfs/xfs_attr.h
>>>>>> @@ -74,6 +74,43 @@ struct xfs_attr_list_context {
>>>>>>     };
>>>>>> +/*
>>>>>> + * ========================================================================
>>>>>> + * Structure used to pass context around among the delayed routines.
>>>>>> + * ========================================================================
>>>>>> + */
>>>>>> +
>>>>>> +/*
>>>>>> + * Enum values for xfs_delattr_context.da_state
>>>>>> + *
>>>>>> + * These values are used by delayed attribute operations to keep track  of where
>>>>>> + * they were before they returned -EAGAIN.  A return code of -EAGAIN signals the
>>>>>> + * calling function to roll the transaction, and then recall the subroutine to
>>>>>> + * finish the operation.  The enum is then used by the subroutine to jump back
>>>>>> + * to where it was and resume executing where it left off.
>>>>>> + */
>>>>>> +enum xfs_delattr_state {
>>>>>> +				      /* Zero is uninitalized */
>>>>>> +	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
>>>>>> +	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
>>>>>> +};
>>>>>> +
>>>>>> +/*
>>>>>> + * Defines for xfs_delattr_context.flags
>>>>>> + */
>>>>>> +#define XFS_DAC_DEFER_FINISH    0x1 /* indicates to finish the transaction */
>>>>>> +
>>>>>> +/*
>>>>>> + * Context used for keeping track of delayed attribute operations
>>>>>> + */
>>>>>> +struct xfs_delattr_context {
>>>>>> +	struct xfs_da_args      *da_args;
>>>>>> +	struct xfs_da_state     *da_state;
>>>>>> +	struct xfs_da_state_blk *blk;
>>>>>> +	unsigned int            flags;
>>>>>> +	enum xfs_delattr_state  dela_state;
>>>>>> +};
>>>>>> +
>>>>>>     /*========================================================================
>>>>>>      * Function prototypes for the kernel.
>>>>>>      *========================================================================*/
>>>>>> @@ -91,6 +128,7 @@ int xfs_attr_set(struct xfs_da_args *args);
>>>>>>     int xfs_attr_set_args(struct xfs_da_args *args);
>>>>>>     int xfs_has_attr(struct xfs_da_args *args);
>>>>>>     int xfs_attr_remove_args(struct xfs_da_args *args);
>>>>>> +int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>>>>>     bool xfs_attr_namecheck(const void *name, size_t length);
>>>>>>     #endif	/* __XFS_ATTR_H__ */
>>>>>> -- 
>>>>>> 2.7.4
>>>>>>
>>>>>
>>>>
>>>
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-16 11:01       ` Brian Foster
@ 2020-04-16 22:54         ` Allison Collins
  2020-04-17 10:36           ` Brian Foster
  0 siblings, 1 reply; 70+ messages in thread
From: Allison Collins @ 2020-04-16 22:54 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/16/20 4:01 AM, Brian Foster wrote:
> On Wed, Apr 15, 2020 at 03:08:11PM -0700, Allison Collins wrote:
>>
>>
>> On 4/13/20 6:40 AM, Brian Foster wrote:
>>> On Fri, Apr 03, 2020 at 03:12:28PM -0700, Allison Collins wrote:
>>>> This patch modifies the attr set routines to be delay ready. This means
>>>> they no longer roll or commit transactions, but instead return -EAGAIN
>>>> to have the calling routine roll and refresh the transaction.  In this
>>>> series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
>>>> state machine like switch to keep track of where it was when EAGAIN was
>>>> returned.
>>>>
>>>> Two new helper functions have been added: xfs_attr_rmtval_set_init and
>>>> xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
>>>> xfs_attr_rmtval_set, but they store the current block in the delay attr
>>>> context to allow the caller to roll the transaction between allocations.
>>>> This helps to simplify and consolidate code used by
>>>> xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
>>>> now become a simple loop to refresh the transaction until the operation
>>>> is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
>>>> removed.
>>>>
>>>> Below is a state machine diagram for attr set operations. The XFS_DAS_*
>>>> states indicate places where the function would return -EAGAIN, and then
>>>> immediately resume from after being recalled by the calling function.
>>>> States marked as a "subroutine state" indicate that they belong to a
>>>> subroutine, and so the calling function needs to pass them back to that
>>>> subroutine to allow it to finish where it left off.  But they otherwise
>>>> do not have a role in the calling function other than just passing
>>>> through.
>>>>
>>>>    xfs_attr_set_iter()
>>>>                    │
>>>>                    v
>>>>              need to upgrade
>>>>             from sf to leaf? ──n─┐
>>>>                    │             │
>>>>                    y             │
>>>>                    │             │
>>>>                    V             │
>>>>             XFS_DAS_ADD_LEAF     │
>>>>                    │             │
>>>>                    v             │
>>>>     ┌──────n── fork has   <──────┘
>>>>     │         only 1 blk?
>>>>     │              │
>>>>     │              y
>>>>     │              │
>>>>     │              v
>>>>     │     xfs_attr_leaf_try_add()
>>>>     │              │
>>>>     │              v
>>>>     │          had enough
>>>>     ├──────n──   space?
>>>>     │              │
>>>>     │              y
>>>>     │              │
>>>>     │              v
>>>>     │      XFS_DAS_FOUND_LBLK  ──┐
>>>>     │                            │
>>>>     │      XFS_DAS_FLIP_LFLAG  ──┤
>>>>     │      (subroutine state)    │
>>>>     │                            │
>>>>     │      XFS_DAS_ALLOC_LEAF  ──┤
>>>>     │      (subroutine state)    │
>>>>     │                            └─>xfs_attr_leaf_addname()
>>>>     │                                              │
>>>>     │                                              v
>>>>     │                                ┌─────n──  need to
>>>>     │                                │        alloc blks?
>>>>     │                                │             │
>>>>     │                                │             y
>>>>     │                                │             │
>>>>     │                                │             v
>>>>     │                                │  ┌─>XFS_DAS_ALLOC_LEAF
>>>>     │                                │  │          │
>>>>     │                                │  │          v
>>>>     │                                │  └──y── need to alloc
>>>>     │                                │         more blocks?
>>>>     │                                │             │
>>>>     │                                │             n
>>>>     │                                │             │
>>>>     │                                │             v
>>>>     │                                │          was this
>>>>     │                                └────────> a rename? ──n─┐
>>>>     │                                              │          │
>>>>     │                                              y          │
>>>>     │                                              │          │
>>>>     │                                              v          │
>>>>     │                                        flip incomplete  │
>>>>     │                                            flag         │
>>>>     │                                              │          │
>>>>     │                                              v          │
>>>>     │                                      XFS_DAS_FLIP_LFLAG │
>>>>     │                                              │          │
>>>>     │                                              v          │
>>>>     │                                            remove       │
>>>>     │                        XFS_DAS_RM_LBLK ─> old name      │
>>>>     │                                 ^            │          │
>>>>     │                                 │            v          │
>>>>     │                                 └──────y── more to      │
>>>>     │                                            remove       │
>>>>     │                                              │          │
>>>>     │                                              n          │
>>>>     │                                              │          │
>>>>     │                                              v          │
>>>>     │                                             done <──────┘
>>>>     └────> XFS_DAS_LEAF_TO_NODE ─┐
>>>>                                  │
>>>>            XFS_DAS_FOUND_NBLK  ──┤
>>>>            (subroutine state)    │
>>>>                                  │
>>>>            XFS_DAS_ALLOC_NODE  ──┤
>>>>            (subroutine state)    │
>>>>                                  │
>>>>            XFS_DAS_FLIP_NFLAG  ──┤
>>>>            (subroutine state)    │
>>>>                                  │
>>>>                                  └─>xfs_attr_node_addname()
>>>>                                                    │
>>>>                                                    v
>>>>                                            find space to store
>>>>                                           attr. Split if needed
>>>>                                                    │
>>>>                                                    v
>>>>                                            XFS_DAS_FOUND_NBLK
>>>>                                                    │
>>>>                                                    v
>>>>                                      ┌─────n──  need to
>>>>                                      │        alloc blks?
>>>>                                      │             │
>>>>                                      │             y
>>>>                                      │             │
>>>>                                      │             v
>>>>                                      │  ┌─>XFS_DAS_ALLOC_NODE
>>>>                                      │  │          │
>>>>                                      │  │          v
>>>>                                      │  └──y── need to alloc
>>>>                                      │         more blocks?
>>>>                                      │             │
>>>>                                      │             n
>>>>                                      │             │
>>>>                                      │             v
>>>>                                      │          was this
>>>>                                      └────────> a rename? ──n─┐
>>>>                                                    │          │
>>>>                                                    y          │
>>>>                                                    │          │
>>>>                                                    v          │
>>>>                                              flip incomplete  │
>>>>                                                  flag         │
>>>>                                                    │          │
>>>>                                                    v          │
>>>>                                            XFS_DAS_FLIP_NFLAG │
>>>>                                                    │          │
>>>>                                                    v          │
>>>>                                                  remove       │
>>>>                              XFS_DAS_RM_NBLK ─> old name      │
>>>>                                       ^            │          │
>>>>                                       │            v          │
>>>>                                       └──────y── more to      │
>>>>                                                  remove       │
>>>>                                                    │          │
>>>>                                                    n          │
>>>>                                                    │          │
>>>>                                                    v          │
>>>>                                                   done <──────┘
>>>>
>>>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>>>> ---
>>>
>>> Only a cursory pass given the previous feedback...
>>>
>>>>    fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
>>>>    fs/xfs/libxfs/xfs_attr.h        |  16 ++
>>>>    fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
>>>>    fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
>>>>    fs/xfs/libxfs/xfs_attr_remote.h |   4 +
>>>>    fs/xfs/xfs_attr_inactive.c      |   1 +
>>>>    fs/xfs/xfs_trace.h              |   1 -
>>>>    7 files changed, 351 insertions(+), 167 deletions(-)
>>>>
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>>> index f700976..c160b7a 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>>> @@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>>>>     * Internal routines when attribute list is one block.
>>>>     */
>>>>    STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>>>> -STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
>>>> +STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
>>>>    STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>>>>    STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>>> @@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>>>     * Internal routines when attribute list is more than one block.
>>>>     */
>>>>    STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>>>> -STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
>>>> +STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
>>>>    STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>>>>    STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>>>>    				 struct xfs_da_state **state);
>>>>    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 void
>>>>    xfs_delattr_context_init(
>>>> @@ -227,8 +228,11 @@ xfs_attr_is_shortform(
>>>>    /*
>>>>     * Attempts to set an attr in shortform, or converts the tree to leaf form if
>>>> - * there is not enough room.  If the attr is set, the transaction is committed
>>>> - * and set to NULL.
>>>> + * there is not enough room.  This function is meant to operate as a helper
>>>> + * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
>>>> + * that the calling function should roll the transaction, and then proceed to
>>>> + * add the attr in leaf form.  This subroutine does not expect to be recalled
>>>> + * again like the other delayed attr routines do.
>>>>     */
>>>>    STATIC int
>>>>    xfs_attr_set_shortform(
>>>> @@ -236,16 +240,16 @@ xfs_attr_set_shortform(
>>>>    	struct xfs_buf		**leaf_bp)
>>>>    {
>>>>    	struct xfs_inode	*dp = args->dp;
>>>> -	int			error, error2 = 0;
>>>> +	int			error = 0;
>>>>    	/*
>>>>    	 * Try to add the attr to the attribute list in the inode.
>>>>    	 */
>>>>    	error = xfs_attr_try_sf_addname(dp, args);
>>>> +
>>>> +	/* Should only be 0, -EEXIST or ENOSPC */
>>>>    	if (error != -ENOSPC) {
>>>> -		error2 = xfs_trans_commit(args->trans);
>>>> -		args->trans = NULL;
>>>> -		return error ? error : error2;
>>>> +		return error;
>>>>    	}
>>>>    	/*
>>>>    	 * It won't fit in the shortform, transform to a leaf block.  GROT:
>>>> @@ -258,18 +262,10 @@ xfs_attr_set_shortform(
>>>>    	/*
>>>>    	 * 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. Once we're done rolling the transaction we
>>>> -	 * can release the hold and add the attr to the leaf.
>>>> +	 * with the write verifier.
>>>>    	 */
>>>>    	xfs_trans_bhold(args->trans, *leaf_bp);
>>>> -	error = xfs_defer_finish(&args->trans);
>>>> -	xfs_trans_bhold_release(args->trans, *leaf_bp);
>>>> -	if (error) {
>>>> -		xfs_trans_brelse(args->trans, *leaf_bp);
>>>> -		return error;
>>>> -	}
>>>> -
>>>> -	return 0;
>>>> +	return -EAGAIN;
>>>>    }
>>>>    /*
>>>> @@ -279,9 +275,83 @@ int
>>>>    xfs_attr_set_args(
>>>>    	struct xfs_da_args	*args)
>>>>    {
>>>> -	struct xfs_inode	*dp = args->dp;
>>>> -	struct xfs_buf          *leaf_bp = NULL;
>>>> -	int			error = 0;
>>>> +	struct xfs_buf			*leaf_bp = NULL;
>>>> +	int				error = 0;
>>>> +	struct xfs_delattr_context	dac;
>>>> +
>>>> +	xfs_delattr_context_init(&dac, args);
>>>> +
>>>> +	do {
>>>> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
>>>> +		if (error != -EAGAIN)
>>>> +			break;
>>>> +
>>>> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
>>>> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
>>>> +			error = xfs_defer_finish(&args->trans);
>>>> +			if (error)
>>>> +				break;
>>>> +		}
>>>> +
>>>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>> +		if (error)
>>>> +			break;
>>>> +
>>>> +		if (leaf_bp) {
>>>> +			xfs_trans_bjoin(args->trans, leaf_bp);
>>>> +			xfs_trans_bhold(args->trans, leaf_bp);
>>>> +		}
>>>> +
>>>> +	} while (true);
>>>> +
>>>> +	return error;
>>>> +}
>>>> +
>>>> +/*
>>>> + * 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.
>>>> + */
>>>> +int
>>>> +xfs_attr_set_iter(
>>>> +	struct xfs_delattr_context	*dac,
>>>> +	struct xfs_buf			**leaf_bp)
>>>> +{
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_inode		*dp = args->dp;
>>>> +	int				error = 0;
>>>> +	int				sf_size;
>>>> +
>>>> +	/* State machine switch */
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_ADD_LEAF:
>>>> +		goto das_add_leaf;
>>>> +	case XFS_DAS_ALLOC_LEAF:
>>>> +	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:
>>>> +	case XFS_DAS_LEAF_TO_NODE:
>>>> +		goto das_node;
>>>> +	default:
>>>> +		break;
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * New inodes may not have an attribute fork yet. So set the attribute
>>>> +	 * fork appropriately
>>>> +	 */
>>>> +	if (XFS_IFORK_Q((args->dp)) == 0) {
>>>> +		sf_size = sizeof(struct xfs_attr_sf_hdr) +
>>>> +		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
>>>> +		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
>>>> +		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
>>>> +		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
>>>> +	}
>>>
>>> Is this hunk moved from somewhere? If so, we should probably handle that
>>> in a separate patch. I think we really want these last couple of patches
>>> to introduce the state/markers and not much else.
>> Oh, this hunk is new, not moved, I believe it's been here since I picked up
>> the series quite a while ago.  It actually has more to do with parent
>> pointers than delayed atts.  Basically when we try to add the parent pointer
>> during a create, the inode isnt fully constructed yet, so we have add the
>> fork here. I will put this in a separate patch and move it further up the
>> set.
>>
> 
> Ok.
> 
>>>
>>>>    	/*
>>>>    	 * If the attribute list is already in leaf format, jump straight to
>>>> @@ -292,40 +362,53 @@ xfs_attr_set_args(
>>>>    	if (xfs_attr_is_shortform(dp)) {
>>>>    		/*
>>>> -		 * If the attr was successfully set in shortform, the
>>>> -		 * transaction is committed and set to NULL.  Otherwise, is it
>>>> -		 * converted from shortform to leaf, and the transaction is
>>>> -		 * retained.
>>>> +		 * 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 || !args->trans)
>>>> -			return error;
>>>> +		error = xfs_attr_set_shortform(args, leaf_bp);
>>>> +		if (error == -EAGAIN) {
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +			dac->dela_state = XFS_DAS_ADD_LEAF;
>>>> +		}
>>>> +		return error;
>>>
>>> Similar to the previous patch, I wonder if we need the explicit states
>>> that are otherwise handled by existing inode state. For example, if the
>>> above returns -EAGAIN, xfs_attr_is_shortform() is no longer true on
>>> reentry, right? If that's the case for the other conversions, it seems
>>> like we might only need one state (XFS_DAS_FOUND_LBLK) for this
>>> function.
>> Ok, it looks like I can get away with out XFS_DAS_ADD_LEAF and
>> XFS_DAS_LEAF_TO_NODE.
>>
>> This is actually a little similar to how it was done when I was trying to
>> use flags instead of the state enums a long time ago.  At the time, I got
>> the impression people were concerned about the complexity and
>> maintainability.  So we went back to the enums because the explicite jumping
>> made it clear where the re-entry was to resume, and reduced the risk that a
>> person may mistakenly introduce a change that disrupts the reentry flow.
>> Though perhaps the current arrangement of refactoring has made that less
>> concerning?
>>
> 
> Perhaps. I think it just depends on context. It might not make sense to
> try and implement such a reentry model down in the node code where we
> have N different non-deterministic states to deal with; as opposed to
> here where we're looking at one of a handful of high level formats and
> the behavior is very predictable: try to add to the current format, else
> convert to the next.
Alrighty then, that sounds reasonable.  I try my best to recall history 
of the decisions just as a sort of guide of where we've been and where 
to go next.

> 
>>>
>>> BTW, that general approach might be more clear if we lifted the format
>>> conversions into this level from down in the format specific add
>>> handlers. The goal would be to make the high level flow look something
>>> like:
>>>
>>> 	if (shortform) {
>>> 		error = sf_try_add();
>>> 		if (error == -ENOSPC) {
>>> 			shortform_to_leaf(...);
>>> 			...
>>> 			return -EAGAIN;
>>> 		}
>> Well, actually this piece was hoisted out into the helper function in patch
>> 13.  So pulling this up is sort of like letting go of patch 13. :-)
>>
>> The history is: initially I had tried to reduce indentation here in v6 by
>> taking advantage of the _add_leaf label. Because I think in v5 we were
>> trying to unnest where some of the jump points were.  So in v6 I had:
>> "if(!shortform) goto leaf;" Which un-nests this shortform code.
>>
>> Then in the v7 review I think Dave suggested I should add the helper and
>> invert the check. So now we have "if(shortform()){ call_helper(); handle
>> -EAGAIN; }"
>>
>> So pulling up is a bit of a circle now.  But still functional if people
>> prefer it as it was.  Just need to keep track of the history so we avoid
>> going around again. :-)
>>
> 
> I don't think you need to drop that patch necessarily, but perhaps keep
> the conversion part to a separate helper and then introduce a similar
> patch for other formats..? In any event, that was just a followup
> thought so it might be reasonable to defer it to the next version and
> for now just see what it looks like to rely on the format states.
> 
>>
>>> 	} else if (xfs_bmap_one_block(...)) {
>> Mmm.... cant jump into the leaf logic right away.  We need to handle
>> releasing leaf_bp which looks like it got lost in the pseudo code?  IIRC
>> there is a race condition with the AIL that is resolved by holding the leaf
>> buffer across the transaction roll.  So we need to check for that and
>> release it upon reentry.
>>
> 
> That sounds more like an implementation detail. leaf_bp is set when we
> convert from shortform, so isn't this the next state either way?
Sort of, I think when we convert out of shortform, and we dont fit in a 
block, we skip over the leaf logic.  But the leaf_bp release should 
happen either way, so it cant belong to either state.

It is an implementation detail, it just sort of breaks up the if/else 
logic in this example.  So I just meant to point it out so that there 
isnt confusion as to why the next patch may not look quite like the 
pseudo code. Or unless I'm overlooking something?

> 
>>
>>> 		error = xfs_attr_leaf_try_add(args, *leaf_bp);
>>> 		if (error == -ENOSPC) {
>>> 			leaf_to_node(...);
>>> 			return -EAGAIN;
>>> 		}
>> Ok, so lift ENOSPC handler from xfs_attr_leaf_try_add
>>
>>>
>>> 		... state stuff for leaf add ...
>>> 	} else {
>>> 		error = xfs_attr_node_addname(dac);
>>> 	}
>>>
>>> Hm? Of course, something like that should be incorporated via
>>> independent refactoring patches.
>> I think what this becomes right now is:
>>
>> Drop patch 13
>> Add new patch xfs: Lift ENOSPC handler from xfs_attr_leaf_try_add
>>
>> ?
>>
> 
> Re: above, I don't think the patch needs to necessarily go away. Feel
> free to table this for now, just something to think about...
Ok, I may fiddle around with some other arrangements or table it if I 
dont see anything particuarly elegant

> 
>>>
>>>>    	}
>>>> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>> -		error = xfs_attr_leaf_addname(args);
>>>> -		if (error != -ENOSPC)
>>>> -			return error;
>>>> +das_add_leaf:
>>>> -		/*
>>>> -		 * Commit that transaction so that the node_addname()
>>>> -		 * call can manage its own transactions.
>>>> -		 */
>>>> -		error = xfs_defer_finish(&args->trans);
>>>> -		if (error)
>>>> -			return error;
>>>> +	/*
>>>> +	 * After a shortform to leaf conversion, we need to hold the leaf and
>>>> +	 * cylce out the transaction.  When we get back, we need to release
>>>> +	 * the leaf.
>>>> +	 */
>>>> +	if (*leaf_bp != NULL) {
>>>> +		xfs_trans_brelse(args->trans, *leaf_bp);
>>>> +		*leaf_bp = NULL;
>>>> +	}
>>>> -		/*
>>>> -		 * Commit the current trans (including the inode) and
>>>> -		 * start a new one.
>>>> -		 */
>>>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -		if (error)
>>>> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>> +		error = xfs_attr_leaf_try_add(args, *leaf_bp);
>>>> +		switch (error) {
>>>> +		case -ENOSPC:
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
>>>> +			return -EAGAIN;
>>>> +		case 0:
>>>> +			dac->dela_state = XFS_DAS_FOUND_LBLK;
>>>> +			return -EAGAIN;
>>>> +		default:
>>>>    			return error;
>>>> -
>>>> +		}
>>>> +das_leaf:
>>>> +		error = xfs_attr_leaf_addname(dac);
>>>> +		if (error == -ENOSPC) {
>>>> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
>>>> +			return -EAGAIN;
>>>> +		}
>>>> +		return error;
>>>>    	}
>>>> -
>>>> -	error = xfs_attr_node_addname(args);
>>>> +das_node:
>>>> +	error = xfs_attr_node_addname(dac);
>>>>    	return error;
>>>>    }
>>>> @@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
>>>>     *
>>>>     * This leaf block cannot have a "remote" value, we only call this routine
>>>>     * if bmap_one_block() says there is only one block (ie: no remote blks).
>>>> + *
>>>> + * 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_leaf_addname(
>>>> -	struct xfs_da_args	*args)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> -	int			error, forkoff;
>>>> -	struct xfs_buf		*bp = NULL;
>>>> -	struct xfs_inode	*dp = args->dp;
>>>> -
>>>> -	trace_xfs_attr_leaf_addname(args);
>>>> -
>>>> -	error = xfs_attr_leaf_try_add(args, bp);
>>>> -	if (error)
>>>> -		return error;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_buf			*bp = NULL;
>>>> +	int				error, forkoff;
>>>> +	struct xfs_inode		*dp = args->dp;
>>>> -	/*
>>>> -	 * Commit the transaction that added the attr name so that
>>>> -	 * later routines can manage their own transactions.
>>>> -	 */
>>>> -	error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -	if (error)
>>>> -		return error;
>>>> +	/* State machine switch */
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_FLIP_LFLAG:
>>>> +		goto das_flip_flag;
>>>> +	case XFS_DAS_ALLOC_LEAF:
>>>> +		goto das_alloc_leaf;
>>>> +	case XFS_DAS_RM_LBLK:
>>>> +		goto das_rm_lblk;
>>>> +	default:
>>>> +		break;
>>>> +	}
>>>>    	/*
>>>>    	 * If there was an out-of-line value, allocate the blocks we
>>>> @@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
>>>>    	 * maximum size of a transaction and/or hit a deadlock.
>>>>    	 */
>>>>    	if (args->rmtblkno > 0) {
>>>> -		error = xfs_attr_rmtval_set(args);
>>>> +
>>>> +		/* Open coded xfs_attr_rmtval_set without trans handling */
>>>> +		error = xfs_attr_rmtval_set_init(dac);
>>>> +		if (error)
>>>> +			return error;
>>>> +
>>>> +		/*
>>>> +		 * Roll through the "value", allocating blocks on disk as
>>>> +		 * required.
>>>> +		 */
>>>> +das_alloc_leaf:
>>>
>>> If we filter out the setup above, it seems like this state could be
>>> reduced to check for ->blkcnt > 0.
>> By filter out the set up, you mean to introduce the setup flag like you
>> mentioned earlier? For example:
>> 	if(flags & setup == 0){
>> 		setup();
>> 		flags |= setup;
>> 	}
>>
>> To be clear, if we did that, we're talking about adding an "int setup_flags"
>> to the dac. And then defineing a XFS_DAC_SETUP_* scheme. Like a
>> XFS_DAC_SETUP_ADD_NAME, and an XFS_DAC_SETUP_NODE_REMVE_NAME and so on.
>> Because we cant have multiple functions sharing the same set up flag if they
>> ever call each others.
>>
>> Is that what you mean to imply?  Otherwise I may not be clear on what you
>> mean by filtering out the set up.
>>
> 
> Well we already have a flag scheme for the defer thing, right? Is there
> any reason we couldn't define a similar DAC_DEFER_INIT and let each
> operation (add/remove) use it as appropriate? 
You're right, I momentarily forgot I had added the flag memmber to hoist 
the defer finish out.  Yes, I think using it here would be appropriate. :-)

Note again that I'm just
> trying to think about potential simplifications. If there are isolated
> functions that can be made clearly/simply idempotent (like init/setup
> helpers tend to be), then doing so might be helpful. I.e., if
> xfs_attr_rmtval_set_init() could do something like:
> 
> {
> 	if (dax->flags & DAC_RMTVAL_SET_INIT)
> 		return;
> 
> 	dax->flags |= DAC_RMTVAL_SET_INIT;
> 	...
> }
This seems reasonable, sorry for the earlier confusion.  On a side note 
though, a bit ago Dave had suggested we select a different acronym 
because "dac" is too easy to mix up with other existing schemes like dax 
;-)  I'm not super concerned about it right now because it's purely an 
asthetic thing, but eventuallly we probably should come up with 
something. :-)

> 
> ... and that removes an execution state, then that might be a win. If it
> requires more complexity than that, then perhaps the suggestion is not
> worthwile. ;P
I dont think it will be too complicated, and may be of use in the next 
phase of the series.  I will move forward with an init flag scheme in v9

> 
>>>
>>>> +		while (dac->blkcnt > 0) {
>>>> +			error = xfs_attr_rmtval_set_blk(dac);
>>>> +			if (error)
>>>> +				return error;
>>>> +
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +			dac->dela_state = XFS_DAS_ALLOC_LEAF;
>>>> +			return -EAGAIN;
>>>> +		}
>>>> +
>>>> +		error = xfs_attr_rmtval_set_value(args);
>>>>    		if (error)
>>>>    			return error;
>>>>    	}
>>>> @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
>>>>    		error = xfs_attr3_leaf_flipflags(args);
>>>>    		if (error)
>>>>    			return error;
>>>> -		/*
>>>> -		 * Commit the flag value change and start the next trans in
>>>> -		 * series.
>>>> -		 */
>>>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>> -		if (error)
>>>> -			return error;
>>>> -
>>>> +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
>>>> +		return -EAGAIN;
>>>> +das_flip_flag:
>>>>    		/*
>>>>    		 * Dismantle the "old" attribute/value pair by removing
>>>>    		 * a "remote" value (if it exists).
>>>>    		 */
>>>>    		xfs_attr_restore_rmt_blk(args);
>>>> +		xfs_attr_rmtval_invalidate(args);
>>>> +das_rm_lblk:
>>>>    		if (args->rmtblkno) {
>>>> -			error = xfs_attr_rmtval_remove(args);
>>>> +			error = __xfs_attr_rmtval_remove(args);
>>>> +
>>>> +			if (error == -EAGAIN) {
>>>> +				dac->dela_state = XFS_DAS_RM_LBLK;
>>>> +				return -EAGAIN;
>>>> +			}
>>>> +
>>>
>>> This whole function looks like it could use more refactoring to split
>>> out the rename case.
>> Hmm, how about we take every thing inside the "if (args->op_flags &
>> XFS_DA_OP_RENAME) {"  and put it in a xfs_attr_leaf_rename() helper? Then
>> pull the helper into the calling function?
>>
> 
> That would help. I'm not sure what the optimal breakdown is off the top
> of my head tbh. This function is kind of a logical beast between dealing
> with remote blocks and rename and combinations of the two, so I'd have
> to stare at that one some more. 
No worries, I think some versions just have to become exploratory 
efforts for that reason.

In general the goal should be to try and
> avoid jumping in the middle of logic branches as much as possible so
> there's a nice and visible distinction between the state code and the
> functional code.
> 
> Brian

Ok, I think the *_rename() helpers will help with the unnesting here. 
I'll put it together and see what folks think.

Thank you thank you thank you, for all your time and pateince with this, 
I know its super complicated and I really appreciate all the reviews!

Allison
> 
>>>
>>>>    			if (error)
>>>>    				return error;
>>>>    		}
>>>> @@ -799,15 +910,11 @@ xfs_attr_leaf_addname(
>>>>    		/*
>>>>    		 * If the result is small enough, shrink it all into the inode.
>>>>    		 */
>>>> -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
>>>> +		forkoff = xfs_attr_shortform_allfit(bp, dp);
>>>> +		if (forkoff)
>>>>    			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
>>>> -			/* bp is gone due to xfs_da_shrink_inode */
>>>> -			if (error)
>>>> -				return error;
>>>> -			error = xfs_defer_finish(&args->trans);
>>>> -			if (error)
>>>> -				return error;
>>>> -		}
>>>> +
>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>    	} else if (args->rmtblkno > 0) {
>>>>    		/*
>>>> @@ -967,16 +1074,23 @@ xfs_attr_node_hasname(
>>>>     *
>>>>     * "Remote" attribute values confuse the issue and atomic rename operations
>>>>     * add a whole extra layer of confusion on top of that.
>>>> + *
>>>> + * 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_node_addname(
>>>> -	struct xfs_da_args	*args)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> -	struct xfs_da_state	*state;
>>>> -	struct xfs_da_state_blk	*blk;
>>>> -	struct xfs_inode	*dp;
>>>> -	struct xfs_mount	*mp;
>>>> -	int			retval, error;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_da_state		*state = NULL;
>>>> +	struct xfs_da_state_blk		*blk;
>>>> +	struct xfs_inode		*dp;
>>>> +	struct xfs_mount		*mp;
>>>> +	int				retval = 0;
>>>> +	int				error = 0;
>>>>    	trace_xfs_attr_node_addname(args);
>>>> @@ -985,7 +1099,21 @@ xfs_attr_node_addname(
>>>>    	 */
>>>>    	dp = args->dp;
>>>>    	mp = dp->i_mount;
>>>> -restart:
>>>> +
>>>> +	/* State machine switch */
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_FLIP_NFLAG:
>>>> +		goto das_flip_flag;
>>>> +	case XFS_DAS_FOUND_NBLK:
>>>> +		goto das_found_nblk;
>>>> +	case XFS_DAS_ALLOC_NODE:
>>>> +		goto das_alloc_node;
>>>> +	case XFS_DAS_RM_NBLK:
>>>> +		goto das_rm_nblk;
>>>> +	default:
>>>> +		break;
>>>> +	}
>>>> +
>>>>    	/*
>>>>    	 * Search to see if name already exists, and get back a pointer
>>>>    	 * to where it should go.
>>>> @@ -1031,19 +1159,13 @@ xfs_attr_node_addname(
>>>>    			error = xfs_attr3_leaf_to_node(args);
>>>>    			if (error)
>>>>    				goto out;
>>>> -			error = xfs_defer_finish(&args->trans);
>>>> -			if (error)
>>>> -				goto out;
>>>>    			/*
>>>> -			 * Commit the node conversion and start the next
>>>> -			 * trans in the chain.
>>>> +			 * Restart routine from the top.  No need to set  the
>>>> +			 * state
>>>>    			 */
>>>> -			error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -			if (error)
>>>> -				goto out;
>>>> -
>>>> -			goto restart;
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +			return -EAGAIN;
>>>>    		}
>>>>    		/*
>>>> @@ -1055,9 +1177,7 @@ xfs_attr_node_addname(
>>>>    		error = xfs_da3_split(state);
>>>>    		if (error)
>>>>    			goto out;
>>>> -		error = xfs_defer_finish(&args->trans);
>>>> -		if (error)
>>>> -			goto out;
>>>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>    	} else {
>>>>    		/*
>>>>    		 * Addition succeeded, update Btree hashvals.
>>>> @@ -1072,13 +1192,9 @@ xfs_attr_node_addname(
>>>>    	xfs_da_state_free(state);
>>>>    	state = NULL;
>>>> -	/*
>>>> -	 * Commit the leaf addition or btree split and start the next
>>>> -	 * trans in the chain.
>>>> -	 */
>>>> -	error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -	if (error)
>>>> -		goto out;
>>>> +	dac->dela_state = XFS_DAS_FOUND_NBLK;
>>>> +	return -EAGAIN;
>>>> +das_found_nblk:
>>>
>>> Same deal here. Any time we have this return -EAGAIN followed by a label
>>> pattern I think we're going to want to think about refactoring things
>>> more first to avoid dumping it in the middle of some unnecessarily large
>>> function.
>> Ok, similar pattern here too then?  Everything in "if (args->op_flags &
>> XFS_DA_OP_RENAME) {" goes in a new xfs_attr_node_rename() helper?  Then
>> hoist upwards?
>>
>> Thanks for the reviewing!!
>> Allison
>>
>>>
>>> Brian
>>>
>>>>    	/*
>>>>    	 * If there was an out-of-line value, allocate the blocks we
>>>> @@ -1087,7 +1203,27 @@ xfs_attr_node_addname(
>>>>    	 * maximum size of a transaction and/or hit a deadlock.
>>>>    	 */
>>>>    	if (args->rmtblkno > 0) {
>>>> -		error = xfs_attr_rmtval_set(args);
>>>> +		/* Open coded xfs_attr_rmtval_set without trans handling */
>>>> +		error = xfs_attr_rmtval_set_init(dac);
>>>> +		if (error)
>>>> +			return error;
>>>> +
>>>> +		/*
>>>> +		 * Roll through the "value", allocating blocks on disk as
>>>> +		 * required.
>>>> +		 */
>>>> +das_alloc_node:
>>>> +		while (dac->blkcnt > 0) {
>>>> +			error = xfs_attr_rmtval_set_blk(dac);
>>>> +			if (error)
>>>> +				return error;
>>>> +
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +			dac->dela_state = XFS_DAS_ALLOC_NODE;
>>>> +			return -EAGAIN;
>>>> +		}
>>>> +
>>>> +		error = xfs_attr_rmtval_set_value(args);
>>>>    		if (error)
>>>>    			return error;
>>>>    	}
>>>> @@ -1110,18 +1246,26 @@ xfs_attr_node_addname(
>>>>    		 * Commit the flag value change and start the next trans in
>>>>    		 * series
>>>>    		 */
>>>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>> -		if (error)
>>>> -			goto out;
>>>> -
>>>> +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
>>>> +		return -EAGAIN;
>>>> +das_flip_flag:
>>>>    		/*
>>>>    		 * Dismantle the "old" attribute/value pair by removing
>>>>    		 * a "remote" value (if it exists).
>>>>    		 */
>>>>    		xfs_attr_restore_rmt_blk(args);
>>>> +		xfs_attr_rmtval_invalidate(args);
>>>> +
>>>> +das_rm_nblk:
>>>>    		if (args->rmtblkno) {
>>>> -			error = xfs_attr_rmtval_remove(args);
>>>> +			error = __xfs_attr_rmtval_remove(args);
>>>> +
>>>> +			if (error == -EAGAIN) {
>>>> +				dac->dela_state = XFS_DAS_RM_NBLK;
>>>> +				return -EAGAIN;
>>>> +			}
>>>> +
>>>>    			if (error)
>>>>    				return error;
>>>>    		}
>>>> @@ -1139,7 +1283,6 @@ xfs_attr_node_addname(
>>>>    		error = xfs_da3_node_lookup_int(state, &retval);
>>>>    		if (error)
>>>>    			goto out;
>>>> -
>>>>    		/*
>>>>    		 * Remove the name and update the hashvals in the tree.
>>>>    		 */
>>>> @@ -1147,7 +1290,6 @@ xfs_attr_node_addname(
>>>>    		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>>>    		error = xfs_attr3_leaf_remove(blk->bp, args);
>>>>    		xfs_da3_fixhashpath(state, &state->path);
>>>> -
>>>>    		/*
>>>>    		 * Check to see if the tree needs to be collapsed.
>>>>    		 */
>>>> @@ -1155,11 +1297,9 @@ xfs_attr_node_addname(
>>>>    			error = xfs_da3_join(state);
>>>>    			if (error)
>>>>    				goto out;
>>>> -			error = xfs_defer_finish(&args->trans);
>>>> -			if (error)
>>>> -				goto out;
>>>> -		}
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +		}
>>>>    	} else if (args->rmtblkno > 0) {
>>>>    		/*
>>>>    		 * Added a "remote" value, just clear the incomplete flag.
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>>>> index 0e8ae1a..67af9d1 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.h
>>>> +++ b/fs/xfs/libxfs/xfs_attr.h
>>>> @@ -93,6 +93,16 @@ enum xfs_delattr_state {
>>>>    				      /* Zero is uninitalized */
>>>>    	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
>>>>    	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
>>>> +	XFS_DAS_ADD_LEAF,	      /* We are adding a leaf attr */
>>>> +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
>>>> +	XFS_DAS_LEAF_TO_NODE,	      /* Converted leaf to node */
>>>> +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
>>>> +	XFS_DAS_ALLOC_LEAF,	      /* We are allocating leaf blocks */
>>>> +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
>>>> +	XFS_DAS_RM_LBLK,	      /* A rename is removing leaf blocks */
>>>> +	XFS_DAS_ALLOC_NODE,	      /* We are allocating node blocks */
>>>> +	XFS_DAS_FLIP_NFLAG,	      /* Flipped node INCOMPLETE attr flag */
>>>> +	XFS_DAS_RM_NBLK,	      /* A rename is removing node blocks */
>>>>    };
>>>>    /*
>>>> @@ -105,8 +115,13 @@ enum xfs_delattr_state {
>>>>     */
>>>>    struct xfs_delattr_context {
>>>>    	struct xfs_da_args      *da_args;
>>>> +	struct xfs_bmbt_irec	map;
>>>> +	struct xfs_buf		*leaf_bp;
>>>> +	xfs_fileoff_t		lfileoff;
>>>>    	struct xfs_da_state     *da_state;
>>>>    	struct xfs_da_state_blk *blk;
>>>> +	xfs_dablk_t		lblkno;
>>>> +	int			blkcnt;
>>>>    	unsigned int            flags;
>>>>    	enum xfs_delattr_state  dela_state;
>>>>    };
>>>> @@ -126,6 +141,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>>>>    int xfs_attr_get(struct xfs_da_args *args);
>>>>    int xfs_attr_set(struct xfs_da_args *args);
>>>>    int xfs_attr_set_args(struct xfs_da_args *args);
>>>> +int xfs_attr_set_iter(struct xfs_delattr_context *dac, struct xfs_buf **leaf_bp);
>>>>    int xfs_has_attr(struct xfs_da_args *args);
>>>>    int xfs_attr_remove_args(struct xfs_da_args *args);
>>>>    int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>>> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
>>>> index f55402b..4d15f45 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
>>>> @@ -19,6 +19,7 @@
>>>>    #include "xfs_bmap_btree.h"
>>>>    #include "xfs_bmap.h"
>>>>    #include "xfs_attr_sf.h"
>>>> +#include "xfs_attr.h"
>>>>    #include "xfs_attr_remote.h"
>>>>    #include "xfs_attr.h"
>>>>    #include "xfs_attr_leaf.h"
>>>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>>>> index fd4be9d..9607fd2 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>>>> @@ -443,7 +443,7 @@ xfs_attr_rmtval_get(
>>>>     * Find a "hole" in the attribute address space large enough for us to drop the
>>>>     * new attribute's value into
>>>>     */
>>>> -STATIC int
>>>> +int
>>>>    xfs_attr_rmt_find_hole(
>>>>    	struct xfs_da_args	*args)
>>>>    {
>>>> @@ -470,7 +470,7 @@ xfs_attr_rmt_find_hole(
>>>>    	return 0;
>>>>    }
>>>> -STATIC int
>>>> +int
>>>>    xfs_attr_rmtval_set_value(
>>>>    	struct xfs_da_args	*args)
>>>>    {
>>>> @@ -630,6 +630,71 @@ xfs_attr_rmtval_set(
>>>>    }
>>>>    /*
>>>> + * Find a hole for the attr and store it in the delayed attr context.  This
>>>> + * initializes the context to roll through allocating an attr extent for a
>>>> + * delayed attr operation
>>>> + */
>>>> +int
>>>> +xfs_attr_rmtval_set_init(
>>>> +	struct xfs_delattr_context	*dac)
>>>> +{
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_bmbt_irec		*map = &dac->map;
>>>> +	int error;
>>>> +
>>>> +	dac->lblkno = 0;
>>>> +	dac->lfileoff = 0;
>>>> +	dac->blkcnt = 0;
>>>> +	args->rmtblkcnt = 0;
>>>> +	args->rmtblkno = 0;
>>>> +	memset(map, 0, sizeof(struct xfs_bmbt_irec));
>>>> +
>>>> +	error = xfs_attr_rmt_find_hole(args);
>>>> +	if (error)
>>>> +		return error;
>>>> +
>>>> +	dac->blkcnt = args->rmtblkcnt;
>>>> +	dac->lblkno = args->rmtblkno;
>>>> +
>>>> +	return error;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write one block of the value associated with an attribute into the
>>>> + * out-of-line buffer that we have defined for it. This is similar to a subset
>>>> + * of xfs_attr_rmtval_set, but records the current block to the delayed attr
>>>> + * context, and leaves transaction handling to the caller.
>>>> + */
>>>> +int
>>>> +xfs_attr_rmtval_set_blk(
>>>> +	struct xfs_delattr_context	*dac)
>>>> +{
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_inode		*dp = args->dp;
>>>> +	struct xfs_bmbt_irec		*map = &dac->map;
>>>> +	int nmap;
>>>> +	int error;
>>>> +
>>>> +	nmap = 1;
>>>> +	error = xfs_bmapi_write(args->trans, dp,
>>>> +		  (xfs_fileoff_t)dac->lblkno,
>>>> +		  dac->blkcnt, XFS_BMAPI_ATTRFORK,
>>>> +		  args->total, map, &nmap);
>>>> +	if (error)
>>>> +		return error;
>>>> +
>>>> +	ASSERT(nmap == 1);
>>>> +	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
>>>> +	       (map->br_startblock != HOLESTARTBLOCK));
>>>> +
>>>> +	/* roll attribute extent map forwards */
>>>> +	dac->lblkno += map->br_blockcount;
>>>> +	dac->blkcnt -= map->br_blockcount;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/*
>>>>     * Remove the value associated with an attribute by deleting the
>>>>     * out-of-line buffer that it is stored on.
>>>>     */
>>>> @@ -671,48 +736,6 @@ xfs_attr_rmtval_invalidate(
>>>>    }
>>>>    /*
>>>> - * Remove the value associated with an attribute by deleting the
>>>> - * out-of-line buffer that it is stored on.
>>>> - */
>>>> -int
>>>> -xfs_attr_rmtval_remove(
>>>> -	struct xfs_da_args      *args)
>>>> -{
>>>> -	xfs_dablk_t		lblkno;
>>>> -	int			blkcnt;
>>>> -	int			error = 0;
>>>> -	int			done = 0;
>>>> -
>>>> -	trace_xfs_attr_rmtval_remove(args);
>>>> -
>>>> -	error = xfs_attr_rmtval_invalidate(args);
>>>> -	if (error)
>>>> -		return error;
>>>> -	/*
>>>> -	 * Keep de-allocating extents until the remote-value region is gone.
>>>> -	 */
>>>> -	lblkno = args->rmtblkno;
>>>> -	blkcnt = args->rmtblkcnt;
>>>> -	while (!done) {
>>>> -		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
>>>> -				    XFS_BMAPI_ATTRFORK, 1, &done);
>>>> -		if (error)
>>>> -			return error;
>>>> -		error = xfs_defer_finish(&args->trans);
>>>> -		if (error)
>>>> -			return error;
>>>> -
>>>> -		/*
>>>> -		 * Close out trans and start the next one in the chain.
>>>> -		 */
>>>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>> -		if (error)
>>>> -			return error;
>>>> -	}
>>>> -	return 0;
>>>> -}
>>>> -
>>>> -/*
>>>>     * Remove the value associated with an attribute by deleting the out-of-line
>>>>     * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
>>>>     * transaction and recall the function
>>>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
>>>> index ee3337b..482dff9 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr_remote.h
>>>> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
>>>> @@ -15,4 +15,8 @@ int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
>>>>    		xfs_buf_flags_t incore_flags);
>>>>    int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
>>>>    int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
>>>> +int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
>>>> +int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
>>>> +int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
>>>> +int xfs_attr_rmtval_set_init(struct xfs_delattr_context *dac);
>>>>    #endif /* __XFS_ATTR_REMOTE_H__ */
>>>> diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
>>>> index c42f90e..3e8cec5 100644
>>>> --- a/fs/xfs/xfs_attr_inactive.c
>>>> +++ b/fs/xfs/xfs_attr_inactive.c
>>>> @@ -15,6 +15,7 @@
>>>>    #include "xfs_da_format.h"
>>>>    #include "xfs_da_btree.h"
>>>>    #include "xfs_inode.h"
>>>> +#include "xfs_attr.h"
>>>>    #include "xfs_attr_remote.h"
>>>>    #include "xfs_trans.h"
>>>>    #include "xfs_bmap.h"
>>>> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
>>>> index a4323a6..26dc8bf 100644
>>>> --- a/fs/xfs/xfs_trace.h
>>>> +++ b/fs/xfs/xfs_trace.h
>>>> @@ -1784,7 +1784,6 @@ DEFINE_ATTR_EVENT(xfs_attr_refillstate);
>>>>    DEFINE_ATTR_EVENT(xfs_attr_rmtval_get);
>>>>    DEFINE_ATTR_EVENT(xfs_attr_rmtval_set);
>>>> -DEFINE_ATTR_EVENT(xfs_attr_rmtval_remove);
>>>>    #define DEFINE_DA_EVENT(name) \
>>>>    DEFINE_EVENT(xfs_da_class, name, \
>>>> -- 
>>>> 2.7.4
>>>>
>>>
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-16 22:54         ` Allison Collins
@ 2020-04-17 10:36           ` Brian Foster
  0 siblings, 0 replies; 70+ messages in thread
From: Brian Foster @ 2020-04-17 10:36 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Thu, Apr 16, 2020 at 03:54:50PM -0700, Allison Collins wrote:
> 
> 
> On 4/16/20 4:01 AM, Brian Foster wrote:
> > On Wed, Apr 15, 2020 at 03:08:11PM -0700, Allison Collins wrote:
> > > 
> > > 
> > > On 4/13/20 6:40 AM, Brian Foster wrote:
> > > > On Fri, Apr 03, 2020 at 03:12:28PM -0700, Allison Collins wrote:
> > > > > This patch modifies the attr set routines to be delay ready. This means
> > > > > they no longer roll or commit transactions, but instead return -EAGAIN
> > > > > to have the calling routine roll and refresh the transaction.  In this
> > > > > series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
> > > > > state machine like switch to keep track of where it was when EAGAIN was
> > > > > returned.
> > > > > 
> > > > > Two new helper functions have been added: xfs_attr_rmtval_set_init and
> > > > > xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
> > > > > xfs_attr_rmtval_set, but they store the current block in the delay attr
> > > > > context to allow the caller to roll the transaction between allocations.
> > > > > This helps to simplify and consolidate code used by
> > > > > xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
> > > > > now become a simple loop to refresh the transaction until the operation
> > > > > is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
> > > > > removed.
> > > > > 
> > > > > Below is a state machine diagram for attr set operations. The XFS_DAS_*
> > > > > states indicate places where the function would return -EAGAIN, and then
> > > > > immediately resume from after being recalled by the calling function.
> > > > > States marked as a "subroutine state" indicate that they belong to a
> > > > > subroutine, and so the calling function needs to pass them back to that
> > > > > subroutine to allow it to finish where it left off.  But they otherwise
> > > > > do not have a role in the calling function other than just passing
> > > > > through.
> > > > > 
> > > > >    xfs_attr_set_iter()
> > > > >                    │
> > > > >                    v
> > > > >              need to upgrade
> > > > >             from sf to leaf? ──n─┐
> > > > >                    │             │
> > > > >                    y             │
> > > > >                    │             │
> > > > >                    V             │
> > > > >             XFS_DAS_ADD_LEAF     │
> > > > >                    │             │
> > > > >                    v             │
> > > > >     ┌──────n── fork has   <──────┘
> > > > >     │         only 1 blk?
> > > > >     │              │
> > > > >     │              y
> > > > >     │              │
> > > > >     │              v
> > > > >     │     xfs_attr_leaf_try_add()
> > > > >     │              │
> > > > >     │              v
> > > > >     │          had enough
> > > > >     ├──────n──   space?
> > > > >     │              │
> > > > >     │              y
> > > > >     │              │
> > > > >     │              v
> > > > >     │      XFS_DAS_FOUND_LBLK  ──┐
> > > > >     │                            │
> > > > >     │      XFS_DAS_FLIP_LFLAG  ──┤
> > > > >     │      (subroutine state)    │
> > > > >     │                            │
> > > > >     │      XFS_DAS_ALLOC_LEAF  ──┤
> > > > >     │      (subroutine state)    │
> > > > >     │                            └─>xfs_attr_leaf_addname()
> > > > >     │                                              │
> > > > >     │                                              v
> > > > >     │                                ┌─────n──  need to
> > > > >     │                                │        alloc blks?
> > > > >     │                                │             │
> > > > >     │                                │             y
> > > > >     │                                │             │
> > > > >     │                                │             v
> > > > >     │                                │  ┌─>XFS_DAS_ALLOC_LEAF
> > > > >     │                                │  │          │
> > > > >     │                                │  │          v
> > > > >     │                                │  └──y── need to alloc
> > > > >     │                                │         more blocks?
> > > > >     │                                │             │
> > > > >     │                                │             n
> > > > >     │                                │             │
> > > > >     │                                │             v
> > > > >     │                                │          was this
> > > > >     │                                └────────> a rename? ──n─┐
> > > > >     │                                              │          │
> > > > >     │                                              y          │
> > > > >     │                                              │          │
> > > > >     │                                              v          │
> > > > >     │                                        flip incomplete  │
> > > > >     │                                            flag         │
> > > > >     │                                              │          │
> > > > >     │                                              v          │
> > > > >     │                                      XFS_DAS_FLIP_LFLAG │
> > > > >     │                                              │          │
> > > > >     │                                              v          │
> > > > >     │                                            remove       │
> > > > >     │                        XFS_DAS_RM_LBLK ─> old name      │
> > > > >     │                                 ^            │          │
> > > > >     │                                 │            v          │
> > > > >     │                                 └──────y── more to      │
> > > > >     │                                            remove       │
> > > > >     │                                              │          │
> > > > >     │                                              n          │
> > > > >     │                                              │          │
> > > > >     │                                              v          │
> > > > >     │                                             done <──────┘
> > > > >     └────> XFS_DAS_LEAF_TO_NODE ─┐
> > > > >                                  │
> > > > >            XFS_DAS_FOUND_NBLK  ──┤
> > > > >            (subroutine state)    │
> > > > >                                  │
> > > > >            XFS_DAS_ALLOC_NODE  ──┤
> > > > >            (subroutine state)    │
> > > > >                                  │
> > > > >            XFS_DAS_FLIP_NFLAG  ──┤
> > > > >            (subroutine state)    │
> > > > >                                  │
> > > > >                                  └─>xfs_attr_node_addname()
> > > > >                                                    │
> > > > >                                                    v
> > > > >                                            find space to store
> > > > >                                           attr. Split if needed
> > > > >                                                    │
> > > > >                                                    v
> > > > >                                            XFS_DAS_FOUND_NBLK
> > > > >                                                    │
> > > > >                                                    v
> > > > >                                      ┌─────n──  need to
> > > > >                                      │        alloc blks?
> > > > >                                      │             │
> > > > >                                      │             y
> > > > >                                      │             │
> > > > >                                      │             v
> > > > >                                      │  ┌─>XFS_DAS_ALLOC_NODE
> > > > >                                      │  │          │
> > > > >                                      │  │          v
> > > > >                                      │  └──y── need to alloc
> > > > >                                      │         more blocks?
> > > > >                                      │             │
> > > > >                                      │             n
> > > > >                                      │             │
> > > > >                                      │             v
> > > > >                                      │          was this
> > > > >                                      └────────> a rename? ──n─┐
> > > > >                                                    │          │
> > > > >                                                    y          │
> > > > >                                                    │          │
> > > > >                                                    v          │
> > > > >                                              flip incomplete  │
> > > > >                                                  flag         │
> > > > >                                                    │          │
> > > > >                                                    v          │
> > > > >                                            XFS_DAS_FLIP_NFLAG │
> > > > >                                                    │          │
> > > > >                                                    v          │
> > > > >                                                  remove       │
> > > > >                              XFS_DAS_RM_NBLK ─> old name      │
> > > > >                                       ^            │          │
> > > > >                                       │            v          │
> > > > >                                       └──────y── more to      │
> > > > >                                                  remove       │
> > > > >                                                    │          │
> > > > >                                                    n          │
> > > > >                                                    │          │
> > > > >                                                    v          │
> > > > >                                                   done <──────┘
> > > > > 
> > > > > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > > > > ---
> > > > 
> > > > Only a cursory pass given the previous feedback...
> > > > 
> > > > >    fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
> > > > >    fs/xfs/libxfs/xfs_attr.h        |  16 ++
> > > > >    fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
> > > > >    fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
> > > > >    fs/xfs/libxfs/xfs_attr_remote.h |   4 +
> > > > >    fs/xfs/xfs_attr_inactive.c      |   1 +
> > > > >    fs/xfs/xfs_trace.h              |   1 -
> > > > >    7 files changed, 351 insertions(+), 167 deletions(-)
> > > > > 
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > > > index f700976..c160b7a 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > > > @@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
> > > > >     * Internal routines when attribute list is one block.
> > > > >     */
> > > > >    STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
> > > > > -STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> > > > > +STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
> > > > >    STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
> > > > >    STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > > > > @@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
> > > > >     * Internal routines when attribute list is more than one block.
> > > > >     */
> > > > >    STATIC int xfs_attr_node_get(xfs_da_args_t *args);
> > > > > -STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> > > > > +STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
> > > > >    STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
> > > > >    STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
> > > > >    				 struct xfs_da_state **state);
> > > > >    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 void
> > > > >    xfs_delattr_context_init(
> > > > > @@ -227,8 +228,11 @@ xfs_attr_is_shortform(
> > > > >    /*
> > > > >     * Attempts to set an attr in shortform, or converts the tree to leaf form if
> > > > > - * there is not enough room.  If the attr is set, the transaction is committed
> > > > > - * and set to NULL.
> > > > > + * there is not enough room.  This function is meant to operate as a helper
> > > > > + * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
> > > > > + * that the calling function should roll the transaction, and then proceed to
> > > > > + * add the attr in leaf form.  This subroutine does not expect to be recalled
> > > > > + * again like the other delayed attr routines do.
> > > > >     */
> > > > >    STATIC int
> > > > >    xfs_attr_set_shortform(
> > > > > @@ -236,16 +240,16 @@ xfs_attr_set_shortform(
> > > > >    	struct xfs_buf		**leaf_bp)
> > > > >    {
> > > > >    	struct xfs_inode	*dp = args->dp;
> > > > > -	int			error, error2 = 0;
> > > > > +	int			error = 0;
> > > > >    	/*
> > > > >    	 * Try to add the attr to the attribute list in the inode.
> > > > >    	 */
> > > > >    	error = xfs_attr_try_sf_addname(dp, args);
> > > > > +
> > > > > +	/* Should only be 0, -EEXIST or ENOSPC */
> > > > >    	if (error != -ENOSPC) {
> > > > > -		error2 = xfs_trans_commit(args->trans);
> > > > > -		args->trans = NULL;
> > > > > -		return error ? error : error2;
> > > > > +		return error;
> > > > >    	}
> > > > >    	/*
> > > > >    	 * It won't fit in the shortform, transform to a leaf block.  GROT:
> > > > > @@ -258,18 +262,10 @@ xfs_attr_set_shortform(
> > > > >    	/*
> > > > >    	 * 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. Once we're done rolling the transaction we
> > > > > -	 * can release the hold and add the attr to the leaf.
> > > > > +	 * with the write verifier.
> > > > >    	 */
> > > > >    	xfs_trans_bhold(args->trans, *leaf_bp);
> > > > > -	error = xfs_defer_finish(&args->trans);
> > > > > -	xfs_trans_bhold_release(args->trans, *leaf_bp);
> > > > > -	if (error) {
> > > > > -		xfs_trans_brelse(args->trans, *leaf_bp);
> > > > > -		return error;
> > > > > -	}
> > > > > -
> > > > > -	return 0;
> > > > > +	return -EAGAIN;
> > > > >    }
> > > > >    /*
> > > > > @@ -279,9 +275,83 @@ int
> > > > >    xfs_attr_set_args(
> > > > >    	struct xfs_da_args	*args)
> > > > >    {
> > > > > -	struct xfs_inode	*dp = args->dp;
> > > > > -	struct xfs_buf          *leaf_bp = NULL;
> > > > > -	int			error = 0;
> > > > > +	struct xfs_buf			*leaf_bp = NULL;
> > > > > +	int				error = 0;
> > > > > +	struct xfs_delattr_context	dac;
> > > > > +
> > > > > +	xfs_delattr_context_init(&dac, args);
> > > > > +
> > > > > +	do {
> > > > > +		error = xfs_attr_set_iter(&dac, &leaf_bp);
> > > > > +		if (error != -EAGAIN)
> > > > > +			break;
> > > > > +
> > > > > +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> > > > > +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> > > > > +			error = xfs_defer_finish(&args->trans);
> > > > > +			if (error)
> > > > > +				break;
> > > > > +		}
> > > > > +
> > > > > +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > > > +		if (error)
> > > > > +			break;
> > > > > +
> > > > > +		if (leaf_bp) {
> > > > > +			xfs_trans_bjoin(args->trans, leaf_bp);
> > > > > +			xfs_trans_bhold(args->trans, leaf_bp);
> > > > > +		}
> > > > > +
> > > > > +	} while (true);
> > > > > +
> > > > > +	return error;
> > > > > +}
> > > > > +
> > > > > +/*
> > > > > + * 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.
> > > > > + */
> > > > > +int
> > > > > +xfs_attr_set_iter(
> > > > > +	struct xfs_delattr_context	*dac,
> > > > > +	struct xfs_buf			**leaf_bp)
> > > > > +{
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_inode		*dp = args->dp;
> > > > > +	int				error = 0;
> > > > > +	int				sf_size;
> > > > > +
> > > > > +	/* State machine switch */
> > > > > +	switch (dac->dela_state) {
> > > > > +	case XFS_DAS_ADD_LEAF:
> > > > > +		goto das_add_leaf;
> > > > > +	case XFS_DAS_ALLOC_LEAF:
> > > > > +	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:
> > > > > +	case XFS_DAS_LEAF_TO_NODE:
> > > > > +		goto das_node;
> > > > > +	default:
> > > > > +		break;
> > > > > +	}
> > > > > +
> > > > > +	/*
> > > > > +	 * New inodes may not have an attribute fork yet. So set the attribute
> > > > > +	 * fork appropriately
> > > > > +	 */
> > > > > +	if (XFS_IFORK_Q((args->dp)) == 0) {
> > > > > +		sf_size = sizeof(struct xfs_attr_sf_hdr) +
> > > > > +		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
> > > > > +		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
> > > > > +		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
> > > > > +		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
> > > > > +	}
> > > > 
> > > > Is this hunk moved from somewhere? If so, we should probably handle that
> > > > in a separate patch. I think we really want these last couple of patches
> > > > to introduce the state/markers and not much else.
> > > Oh, this hunk is new, not moved, I believe it's been here since I picked up
> > > the series quite a while ago.  It actually has more to do with parent
> > > pointers than delayed atts.  Basically when we try to add the parent pointer
> > > during a create, the inode isnt fully constructed yet, so we have add the
> > > fork here. I will put this in a separate patch and move it further up the
> > > set.
> > > 
> > 
> > Ok.
> > 
> > > > 
> > > > >    	/*
> > > > >    	 * If the attribute list is already in leaf format, jump straight to
> > > > > @@ -292,40 +362,53 @@ xfs_attr_set_args(
> > > > >    	if (xfs_attr_is_shortform(dp)) {
> > > > >    		/*
> > > > > -		 * If the attr was successfully set in shortform, the
> > > > > -		 * transaction is committed and set to NULL.  Otherwise, is it
> > > > > -		 * converted from shortform to leaf, and the transaction is
> > > > > -		 * retained.
> > > > > +		 * 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 || !args->trans)
> > > > > -			return error;
> > > > > +		error = xfs_attr_set_shortform(args, leaf_bp);
> > > > > +		if (error == -EAGAIN) {
> > > > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +			dac->dela_state = XFS_DAS_ADD_LEAF;
> > > > > +		}
> > > > > +		return error;
> > > > 
> > > > Similar to the previous patch, I wonder if we need the explicit states
> > > > that are otherwise handled by existing inode state. For example, if the
> > > > above returns -EAGAIN, xfs_attr_is_shortform() is no longer true on
> > > > reentry, right? If that's the case for the other conversions, it seems
> > > > like we might only need one state (XFS_DAS_FOUND_LBLK) for this
> > > > function.
> > > Ok, it looks like I can get away with out XFS_DAS_ADD_LEAF and
> > > XFS_DAS_LEAF_TO_NODE.
> > > 
> > > This is actually a little similar to how it was done when I was trying to
> > > use flags instead of the state enums a long time ago.  At the time, I got
> > > the impression people were concerned about the complexity and
> > > maintainability.  So we went back to the enums because the explicite jumping
> > > made it clear where the re-entry was to resume, and reduced the risk that a
> > > person may mistakenly introduce a change that disrupts the reentry flow.
> > > Though perhaps the current arrangement of refactoring has made that less
> > > concerning?
> > > 
> > 
> > Perhaps. I think it just depends on context. It might not make sense to
> > try and implement such a reentry model down in the node code where we
> > have N different non-deterministic states to deal with; as opposed to
> > here where we're looking at one of a handful of high level formats and
> > the behavior is very predictable: try to add to the current format, else
> > convert to the next.
> Alrighty then, that sounds reasonable.  I try my best to recall history of
> the decisions just as a sort of guide of where we've been and where to go
> next.
> 
> > 
> > > > 
> > > > BTW, that general approach might be more clear if we lifted the format
> > > > conversions into this level from down in the format specific add
> > > > handlers. The goal would be to make the high level flow look something
> > > > like:
> > > > 
> > > > 	if (shortform) {
> > > > 		error = sf_try_add();
> > > > 		if (error == -ENOSPC) {
> > > > 			shortform_to_leaf(...);
> > > > 			...
> > > > 			return -EAGAIN;
> > > > 		}
> > > Well, actually this piece was hoisted out into the helper function in patch
> > > 13.  So pulling this up is sort of like letting go of patch 13. :-)
> > > 
> > > The history is: initially I had tried to reduce indentation here in v6 by
> > > taking advantage of the _add_leaf label. Because I think in v5 we were
> > > trying to unnest where some of the jump points were.  So in v6 I had:
> > > "if(!shortform) goto leaf;" Which un-nests this shortform code.
> > > 
> > > Then in the v7 review I think Dave suggested I should add the helper and
> > > invert the check. So now we have "if(shortform()){ call_helper(); handle
> > > -EAGAIN; }"
> > > 
> > > So pulling up is a bit of a circle now.  But still functional if people
> > > prefer it as it was.  Just need to keep track of the history so we avoid
> > > going around again. :-)
> > > 
> > 
> > I don't think you need to drop that patch necessarily, but perhaps keep
> > the conversion part to a separate helper and then introduce a similar
> > patch for other formats..? In any event, that was just a followup
> > thought so it might be reasonable to defer it to the next version and
> > for now just see what it looks like to rely on the format states.
> > 
> > > 
> > > > 	} else if (xfs_bmap_one_block(...)) {
> > > Mmm.... cant jump into the leaf logic right away.  We need to handle
> > > releasing leaf_bp which looks like it got lost in the pseudo code?  IIRC
> > > there is a race condition with the AIL that is resolved by holding the leaf
> > > buffer across the transaction roll.  So we need to check for that and
> > > release it upon reentry.
> > > 
> > 
> > That sounds more like an implementation detail. leaf_bp is set when we
> > convert from shortform, so isn't this the next state either way?
> Sort of, I think when we convert out of shortform, and we dont fit in a
> block, we skip over the leaf logic.  But the leaf_bp release should happen
> either way, so it cant belong to either state.
> 
> It is an implementation detail, it just sort of breaks up the if/else logic
> in this example.  So I just meant to point it out so that there isnt
> confusion as to why the next patch may not look quite like the pseudo code.
> Or unless I'm overlooking something?
> 

Ok. Perhaps if it's always true that leaf_bp is either NULL or needs to
be released, we could encapsulate that somewhere like at the top of the
function or maybe even in the caller. The latter might be more clear
given the use case. IIRC the purpose of that little trick is simply to
prevent writeback from grabbing hold of an empty leaf between
transactions. It doesn't look like that leaf_bp reference is actually
used for anything else even though the pointer is passed to
leaf_try_add(), but I could be missing something.

> > 
> > > 
> > > > 		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> > > > 		if (error == -ENOSPC) {
> > > > 			leaf_to_node(...);
> > > > 			return -EAGAIN;
> > > > 		}
> > > Ok, so lift ENOSPC handler from xfs_attr_leaf_try_add
> > > 
> > > > 
> > > > 		... state stuff for leaf add ...
> > > > 	} else {
> > > > 		error = xfs_attr_node_addname(dac);
> > > > 	}
> > > > 
> > > > Hm? Of course, something like that should be incorporated via
> > > > independent refactoring patches.
> > > I think what this becomes right now is:
> > > 
> > > Drop patch 13
> > > Add new patch xfs: Lift ENOSPC handler from xfs_attr_leaf_try_add
> > > 
> > > ?
> > > 
> > 
> > Re: above, I don't think the patch needs to necessarily go away. Feel
> > free to table this for now, just something to think about...
> Ok, I may fiddle around with some other arrangements or table it if I dont
> see anything particuarly elegant
> 
> > 
> > > > 
> > > > >    	}
> > > > > -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > > > -		error = xfs_attr_leaf_addname(args);
> > > > > -		if (error != -ENOSPC)
> > > > > -			return error;
> > > > > +das_add_leaf:
> > > > > -		/*
> > > > > -		 * Commit that transaction so that the node_addname()
> > > > > -		 * call can manage its own transactions.
> > > > > -		 */
> > > > > -		error = xfs_defer_finish(&args->trans);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > +	/*
> > > > > +	 * After a shortform to leaf conversion, we need to hold the leaf and
> > > > > +	 * cylce out the transaction.  When we get back, we need to release
> > > > > +	 * the leaf.
> > > > > +	 */
> > > > > +	if (*leaf_bp != NULL) {
> > > > > +		xfs_trans_brelse(args->trans, *leaf_bp);
> > > > > +		*leaf_bp = NULL;
> > > > > +	}
> > > > > -		/*
> > > > > -		 * Commit the current trans (including the inode) and
> > > > > -		 * start a new one.
> > > > > -		 */
> > > > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > > > -		if (error)
> > > > > +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > > > +		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> > > > > +		switch (error) {
> > > > > +		case -ENOSPC:
> > > > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> > > > > +			return -EAGAIN;
> > > > > +		case 0:
> > > > > +			dac->dela_state = XFS_DAS_FOUND_LBLK;
> > > > > +			return -EAGAIN;
> > > > > +		default:
> > > > >    			return error;
> > > > > -
> > > > > +		}
> > > > > +das_leaf:
> > > > > +		error = xfs_attr_leaf_addname(dac);
> > > > > +		if (error == -ENOSPC) {
> > > > > +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> > > > > +			return -EAGAIN;
> > > > > +		}
> > > > > +		return error;
> > > > >    	}
> > > > > -
> > > > > -	error = xfs_attr_node_addname(args);
> > > > > +das_node:
> > > > > +	error = xfs_attr_node_addname(dac);
> > > > >    	return error;
> > > > >    }
> > > > > @@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
> > > > >     *
> > > > >     * This leaf block cannot have a "remote" value, we only call this routine
> > > > >     * if bmap_one_block() says there is only one block (ie: no remote blks).
> > > > > + *
> > > > > + * 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_leaf_addname(
> > > > > -	struct xfs_da_args	*args)
> > > > > +	struct xfs_delattr_context	*dac)
> > > > >    {
> > > > > -	int			error, forkoff;
> > > > > -	struct xfs_buf		*bp = NULL;
> > > > > -	struct xfs_inode	*dp = args->dp;
> > > > > -
> > > > > -	trace_xfs_attr_leaf_addname(args);
> > > > > -
> > > > > -	error = xfs_attr_leaf_try_add(args, bp);
> > > > > -	if (error)
> > > > > -		return error;
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_buf			*bp = NULL;
> > > > > +	int				error, forkoff;
> > > > > +	struct xfs_inode		*dp = args->dp;
> > > > > -	/*
> > > > > -	 * Commit the transaction that added the attr name so that
> > > > > -	 * later routines can manage their own transactions.
> > > > > -	 */
> > > > > -	error = xfs_trans_roll_inode(&args->trans, dp);
> > > > > -	if (error)
> > > > > -		return error;
> > > > > +	/* State machine switch */
> > > > > +	switch (dac->dela_state) {
> > > > > +	case XFS_DAS_FLIP_LFLAG:
> > > > > +		goto das_flip_flag;
> > > > > +	case XFS_DAS_ALLOC_LEAF:
> > > > > +		goto das_alloc_leaf;
> > > > > +	case XFS_DAS_RM_LBLK:
> > > > > +		goto das_rm_lblk;
> > > > > +	default:
> > > > > +		break;
> > > > > +	}
> > > > >    	/*
> > > > >    	 * If there was an out-of-line value, allocate the blocks we
> > > > > @@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
> > > > >    	 * maximum size of a transaction and/or hit a deadlock.
> > > > >    	 */
> > > > >    	if (args->rmtblkno > 0) {
> > > > > -		error = xfs_attr_rmtval_set(args);
> > > > > +
> > > > > +		/* Open coded xfs_attr_rmtval_set without trans handling */
> > > > > +		error = xfs_attr_rmtval_set_init(dac);
> > > > > +		if (error)
> > > > > +			return error;
> > > > > +
> > > > > +		/*
> > > > > +		 * Roll through the "value", allocating blocks on disk as
> > > > > +		 * required.
> > > > > +		 */
> > > > > +das_alloc_leaf:
> > > > 
> > > > If we filter out the setup above, it seems like this state could be
> > > > reduced to check for ->blkcnt > 0.
> > > By filter out the set up, you mean to introduce the setup flag like you
> > > mentioned earlier? For example:
> > > 	if(flags & setup == 0){
> > > 		setup();
> > > 		flags |= setup;
> > > 	}
> > > 
> > > To be clear, if we did that, we're talking about adding an "int setup_flags"
> > > to the dac. And then defineing a XFS_DAC_SETUP_* scheme. Like a
> > > XFS_DAC_SETUP_ADD_NAME, and an XFS_DAC_SETUP_NODE_REMVE_NAME and so on.
> > > Because we cant have multiple functions sharing the same set up flag if they
> > > ever call each others.
> > > 
> > > Is that what you mean to imply?  Otherwise I may not be clear on what you
> > > mean by filtering out the set up.
> > > 
> > 
> > Well we already have a flag scheme for the defer thing, right? Is there
> > any reason we couldn't define a similar DAC_DEFER_INIT and let each
> > operation (add/remove) use it as appropriate?
> You're right, I momentarily forgot I had added the flag memmber to hoist the
> defer finish out.  Yes, I think using it here would be appropriate. :-)
> 
> Note again that I'm just
> > trying to think about potential simplifications. If there are isolated
> > functions that can be made clearly/simply idempotent (like init/setup
> > helpers tend to be), then doing so might be helpful. I.e., if
> > xfs_attr_rmtval_set_init() could do something like:
> > 
> > {
> > 	if (dax->flags & DAC_RMTVAL_SET_INIT)
> > 		return;
> > 
> > 	dax->flags |= DAC_RMTVAL_SET_INIT;
> > 	...
> > }
> This seems reasonable, sorry for the earlier confusion.  On a side note
> though, a bit ago Dave had suggested we select a different acronym because
> "dac" is too easy to mix up with other existing schemes like dax ;-)  I'm
> not super concerned about it right now because it's purely an asthetic
> thing, but eventuallly we probably should come up with something. :-)
> 

I agree. Besides the confusion with DAX, it's not very obvious what DAC
is supposed to mean. Maybe something that reuses the XFS_ATTR_ prefix is
better. Or if not, XATTR_STATE, XATTROP_ (with or without a leading
XFS_) might also be options..? I don't have a strong preference atm.

> > 
> > ... and that removes an execution state, then that might be a win. If it
> > requires more complexity than that, then perhaps the suggestion is not
> > worthwile. ;P
> I dont think it will be too complicated, and may be of use in the next phase
> of the series.  I will move forward with an init flag scheme in v9
> 
> > 
> > > > 
> > > > > +		while (dac->blkcnt > 0) {
> > > > > +			error = xfs_attr_rmtval_set_blk(dac);
> > > > > +			if (error)
> > > > > +				return error;
> > > > > +
> > > > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +			dac->dela_state = XFS_DAS_ALLOC_LEAF;
> > > > > +			return -EAGAIN;
> > > > > +		}
> > > > > +
> > > > > +		error = xfs_attr_rmtval_set_value(args);
> > > > >    		if (error)
> > > > >    			return error;
> > > > >    	}
> > > > > @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
> > > > >    		error = xfs_attr3_leaf_flipflags(args);
> > > > >    		if (error)
> > > > >    			return error;
> > > > > -		/*
> > > > > -		 * Commit the flag value change and start the next trans in
> > > > > -		 * series.
> > > > > -		 */
> > > > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > -
> > > > > +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> > > > > +		return -EAGAIN;
> > > > > +das_flip_flag:
> > > > >    		/*
> > > > >    		 * Dismantle the "old" attribute/value pair by removing
> > > > >    		 * a "remote" value (if it exists).
> > > > >    		 */
> > > > >    		xfs_attr_restore_rmt_blk(args);
> > > > > +		xfs_attr_rmtval_invalidate(args);
> > > > > +das_rm_lblk:
> > > > >    		if (args->rmtblkno) {
> > > > > -			error = xfs_attr_rmtval_remove(args);
> > > > > +			error = __xfs_attr_rmtval_remove(args);
> > > > > +
> > > > > +			if (error == -EAGAIN) {
> > > > > +				dac->dela_state = XFS_DAS_RM_LBLK;
> > > > > +				return -EAGAIN;
> > > > > +			}
> > > > > +
> > > > 
> > > > This whole function looks like it could use more refactoring to split
> > > > out the rename case.
> > > Hmm, how about we take every thing inside the "if (args->op_flags &
> > > XFS_DA_OP_RENAME) {"  and put it in a xfs_attr_leaf_rename() helper? Then
> > > pull the helper into the calling function?
> > > 
> > 
> > That would help. I'm not sure what the optimal breakdown is off the top
> > of my head tbh. This function is kind of a logical beast between dealing
> > with remote blocks and rename and combinations of the two, so I'd have
> > to stare at that one some more.
> No worries, I think some versions just have to become exploratory efforts
> for that reason.
> 

Unfortunately. :P

> In general the goal should be to try and
> > avoid jumping in the middle of logic branches as much as possible so
> > there's a nice and visible distinction between the state code and the
> > functional code.
> > 
> > Brian
> 
> Ok, I think the *_rename() helpers will help with the unnesting here. I'll
> put it together and see what folks think.
> 
> Thank you thank you thank you, for all your time and pateince with this, I
> know its super complicated and I really appreciate all the reviews!
> 

No problem and likewise. I know it can be painful at times to release 42
versions of a patchset. :) I'm hoping we can start to get some of the
refactoring patches merged to shrink this back down to the core
functional bits that are still under development.

Brian

> Allison
> > 
> > > > 
> > > > >    			if (error)
> > > > >    				return error;
> > > > >    		}
> > > > > @@ -799,15 +910,11 @@ xfs_attr_leaf_addname(
> > > > >    		/*
> > > > >    		 * If the result is small enough, shrink it all into the inode.
> > > > >    		 */
> > > > > -		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
> > > > > +		forkoff = xfs_attr_shortform_allfit(bp, dp);
> > > > > +		if (forkoff)
> > > > >    			error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
> > > > > -			/* bp is gone due to xfs_da_shrink_inode */
> > > > > -			if (error)
> > > > > -				return error;
> > > > > -			error = xfs_defer_finish(&args->trans);
> > > > > -			if (error)
> > > > > -				return error;
> > > > > -		}
> > > > > +
> > > > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > >    	} else if (args->rmtblkno > 0) {
> > > > >    		/*
> > > > > @@ -967,16 +1074,23 @@ xfs_attr_node_hasname(
> > > > >     *
> > > > >     * "Remote" attribute values confuse the issue and atomic rename operations
> > > > >     * add a whole extra layer of confusion on top of that.
> > > > > + *
> > > > > + * 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_node_addname(
> > > > > -	struct xfs_da_args	*args)
> > > > > +	struct xfs_delattr_context	*dac)
> > > > >    {
> > > > > -	struct xfs_da_state	*state;
> > > > > -	struct xfs_da_state_blk	*blk;
> > > > > -	struct xfs_inode	*dp;
> > > > > -	struct xfs_mount	*mp;
> > > > > -	int			retval, error;
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_da_state		*state = NULL;
> > > > > +	struct xfs_da_state_blk		*blk;
> > > > > +	struct xfs_inode		*dp;
> > > > > +	struct xfs_mount		*mp;
> > > > > +	int				retval = 0;
> > > > > +	int				error = 0;
> > > > >    	trace_xfs_attr_node_addname(args);
> > > > > @@ -985,7 +1099,21 @@ xfs_attr_node_addname(
> > > > >    	 */
> > > > >    	dp = args->dp;
> > > > >    	mp = dp->i_mount;
> > > > > -restart:
> > > > > +
> > > > > +	/* State machine switch */
> > > > > +	switch (dac->dela_state) {
> > > > > +	case XFS_DAS_FLIP_NFLAG:
> > > > > +		goto das_flip_flag;
> > > > > +	case XFS_DAS_FOUND_NBLK:
> > > > > +		goto das_found_nblk;
> > > > > +	case XFS_DAS_ALLOC_NODE:
> > > > > +		goto das_alloc_node;
> > > > > +	case XFS_DAS_RM_NBLK:
> > > > > +		goto das_rm_nblk;
> > > > > +	default:
> > > > > +		break;
> > > > > +	}
> > > > > +
> > > > >    	/*
> > > > >    	 * Search to see if name already exists, and get back a pointer
> > > > >    	 * to where it should go.
> > > > > @@ -1031,19 +1159,13 @@ xfs_attr_node_addname(
> > > > >    			error = xfs_attr3_leaf_to_node(args);
> > > > >    			if (error)
> > > > >    				goto out;
> > > > > -			error = xfs_defer_finish(&args->trans);
> > > > > -			if (error)
> > > > > -				goto out;
> > > > >    			/*
> > > > > -			 * Commit the node conversion and start the next
> > > > > -			 * trans in the chain.
> > > > > +			 * Restart routine from the top.  No need to set  the
> > > > > +			 * state
> > > > >    			 */
> > > > > -			error = xfs_trans_roll_inode(&args->trans, dp);
> > > > > -			if (error)
> > > > > -				goto out;
> > > > > -
> > > > > -			goto restart;
> > > > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +			return -EAGAIN;
> > > > >    		}
> > > > >    		/*
> > > > > @@ -1055,9 +1177,7 @@ xfs_attr_node_addname(
> > > > >    		error = xfs_da3_split(state);
> > > > >    		if (error)
> > > > >    			goto out;
> > > > > -		error = xfs_defer_finish(&args->trans);
> > > > > -		if (error)
> > > > > -			goto out;
> > > > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > >    	} else {
> > > > >    		/*
> > > > >    		 * Addition succeeded, update Btree hashvals.
> > > > > @@ -1072,13 +1192,9 @@ xfs_attr_node_addname(
> > > > >    	xfs_da_state_free(state);
> > > > >    	state = NULL;
> > > > > -	/*
> > > > > -	 * Commit the leaf addition or btree split and start the next
> > > > > -	 * trans in the chain.
> > > > > -	 */
> > > > > -	error = xfs_trans_roll_inode(&args->trans, dp);
> > > > > -	if (error)
> > > > > -		goto out;
> > > > > +	dac->dela_state = XFS_DAS_FOUND_NBLK;
> > > > > +	return -EAGAIN;
> > > > > +das_found_nblk:
> > > > 
> > > > Same deal here. Any time we have this return -EAGAIN followed by a label
> > > > pattern I think we're going to want to think about refactoring things
> > > > more first to avoid dumping it in the middle of some unnecessarily large
> > > > function.
> > > Ok, similar pattern here too then?  Everything in "if (args->op_flags &
> > > XFS_DA_OP_RENAME) {" goes in a new xfs_attr_node_rename() helper?  Then
> > > hoist upwards?
> > > 
> > > Thanks for the reviewing!!
> > > Allison
> > > 
> > > > 
> > > > Brian
> > > > 
> > > > >    	/*
> > > > >    	 * If there was an out-of-line value, allocate the blocks we
> > > > > @@ -1087,7 +1203,27 @@ xfs_attr_node_addname(
> > > > >    	 * maximum size of a transaction and/or hit a deadlock.
> > > > >    	 */
> > > > >    	if (args->rmtblkno > 0) {
> > > > > -		error = xfs_attr_rmtval_set(args);
> > > > > +		/* Open coded xfs_attr_rmtval_set without trans handling */
> > > > > +		error = xfs_attr_rmtval_set_init(dac);
> > > > > +		if (error)
> > > > > +			return error;
> > > > > +
> > > > > +		/*
> > > > > +		 * Roll through the "value", allocating blocks on disk as
> > > > > +		 * required.
> > > > > +		 */
> > > > > +das_alloc_node:
> > > > > +		while (dac->blkcnt > 0) {
> > > > > +			error = xfs_attr_rmtval_set_blk(dac);
> > > > > +			if (error)
> > > > > +				return error;
> > > > > +
> > > > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +			dac->dela_state = XFS_DAS_ALLOC_NODE;
> > > > > +			return -EAGAIN;
> > > > > +		}
> > > > > +
> > > > > +		error = xfs_attr_rmtval_set_value(args);
> > > > >    		if (error)
> > > > >    			return error;
> > > > >    	}
> > > > > @@ -1110,18 +1246,26 @@ xfs_attr_node_addname(
> > > > >    		 * Commit the flag value change and start the next trans in
> > > > >    		 * series
> > > > >    		 */
> > > > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > > > -		if (error)
> > > > > -			goto out;
> > > > > -
> > > > > +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> > > > > +		return -EAGAIN;
> > > > > +das_flip_flag:
> > > > >    		/*
> > > > >    		 * Dismantle the "old" attribute/value pair by removing
> > > > >    		 * a "remote" value (if it exists).
> > > > >    		 */
> > > > >    		xfs_attr_restore_rmt_blk(args);
> > > > > +		xfs_attr_rmtval_invalidate(args);
> > > > > +
> > > > > +das_rm_nblk:
> > > > >    		if (args->rmtblkno) {
> > > > > -			error = xfs_attr_rmtval_remove(args);
> > > > > +			error = __xfs_attr_rmtval_remove(args);
> > > > > +
> > > > > +			if (error == -EAGAIN) {
> > > > > +				dac->dela_state = XFS_DAS_RM_NBLK;
> > > > > +				return -EAGAIN;
> > > > > +			}
> > > > > +
> > > > >    			if (error)
> > > > >    				return error;
> > > > >    		}
> > > > > @@ -1139,7 +1283,6 @@ xfs_attr_node_addname(
> > > > >    		error = xfs_da3_node_lookup_int(state, &retval);
> > > > >    		if (error)
> > > > >    			goto out;
> > > > > -
> > > > >    		/*
> > > > >    		 * Remove the name and update the hashvals in the tree.
> > > > >    		 */
> > > > > @@ -1147,7 +1290,6 @@ xfs_attr_node_addname(
> > > > >    		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> > > > >    		error = xfs_attr3_leaf_remove(blk->bp, args);
> > > > >    		xfs_da3_fixhashpath(state, &state->path);
> > > > > -
> > > > >    		/*
> > > > >    		 * Check to see if the tree needs to be collapsed.
> > > > >    		 */
> > > > > @@ -1155,11 +1297,9 @@ xfs_attr_node_addname(
> > > > >    			error = xfs_da3_join(state);
> > > > >    			if (error)
> > > > >    				goto out;
> > > > > -			error = xfs_defer_finish(&args->trans);
> > > > > -			if (error)
> > > > > -				goto out;
> > > > > -		}
> > > > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > > +		}
> > > > >    	} else if (args->rmtblkno > 0) {
> > > > >    		/*
> > > > >    		 * Added a "remote" value, just clear the incomplete flag.
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > > > > index 0e8ae1a..67af9d1 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr.h
> > > > > +++ b/fs/xfs/libxfs/xfs_attr.h
> > > > > @@ -93,6 +93,16 @@ enum xfs_delattr_state {
> > > > >    				      /* Zero is uninitalized */
> > > > >    	XFS_DAS_RM_SHRINK	= 1,  /* We are shrinking the tree */
> > > > >    	XFS_DAS_RMTVAL_REMOVE,	      /* We are removing remote value blocks */
> > > > > +	XFS_DAS_ADD_LEAF,	      /* We are adding a leaf attr */
> > > > > +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
> > > > > +	XFS_DAS_LEAF_TO_NODE,	      /* Converted leaf to node */
> > > > > +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
> > > > > +	XFS_DAS_ALLOC_LEAF,	      /* We are allocating leaf blocks */
> > > > > +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
> > > > > +	XFS_DAS_RM_LBLK,	      /* A rename is removing leaf blocks */
> > > > > +	XFS_DAS_ALLOC_NODE,	      /* We are allocating node blocks */
> > > > > +	XFS_DAS_FLIP_NFLAG,	      /* Flipped node INCOMPLETE attr flag */
> > > > > +	XFS_DAS_RM_NBLK,	      /* A rename is removing node blocks */
> > > > >    };
> > > > >    /*
> > > > > @@ -105,8 +115,13 @@ enum xfs_delattr_state {
> > > > >     */
> > > > >    struct xfs_delattr_context {
> > > > >    	struct xfs_da_args      *da_args;
> > > > > +	struct xfs_bmbt_irec	map;
> > > > > +	struct xfs_buf		*leaf_bp;
> > > > > +	xfs_fileoff_t		lfileoff;
> > > > >    	struct xfs_da_state     *da_state;
> > > > >    	struct xfs_da_state_blk *blk;
> > > > > +	xfs_dablk_t		lblkno;
> > > > > +	int			blkcnt;
> > > > >    	unsigned int            flags;
> > > > >    	enum xfs_delattr_state  dela_state;
> > > > >    };
> > > > > @@ -126,6 +141,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
> > > > >    int xfs_attr_get(struct xfs_da_args *args);
> > > > >    int xfs_attr_set(struct xfs_da_args *args);
> > > > >    int xfs_attr_set_args(struct xfs_da_args *args);
> > > > > +int xfs_attr_set_iter(struct xfs_delattr_context *dac, struct xfs_buf **leaf_bp);
> > > > >    int xfs_has_attr(struct xfs_da_args *args);
> > > > >    int xfs_attr_remove_args(struct xfs_da_args *args);
> > > > >    int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> > > > > index f55402b..4d15f45 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> > > > > +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> > > > > @@ -19,6 +19,7 @@
> > > > >    #include "xfs_bmap_btree.h"
> > > > >    #include "xfs_bmap.h"
> > > > >    #include "xfs_attr_sf.h"
> > > > > +#include "xfs_attr.h"
> > > > >    #include "xfs_attr_remote.h"
> > > > >    #include "xfs_attr.h"
> > > > >    #include "xfs_attr_leaf.h"
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > > > > index fd4be9d..9607fd2 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > > > > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > > > > @@ -443,7 +443,7 @@ xfs_attr_rmtval_get(
> > > > >     * Find a "hole" in the attribute address space large enough for us to drop the
> > > > >     * new attribute's value into
> > > > >     */
> > > > > -STATIC int
> > > > > +int
> > > > >    xfs_attr_rmt_find_hole(
> > > > >    	struct xfs_da_args	*args)
> > > > >    {
> > > > > @@ -470,7 +470,7 @@ xfs_attr_rmt_find_hole(
> > > > >    	return 0;
> > > > >    }
> > > > > -STATIC int
> > > > > +int
> > > > >    xfs_attr_rmtval_set_value(
> > > > >    	struct xfs_da_args	*args)
> > > > >    {
> > > > > @@ -630,6 +630,71 @@ xfs_attr_rmtval_set(
> > > > >    }
> > > > >    /*
> > > > > + * Find a hole for the attr and store it in the delayed attr context.  This
> > > > > + * initializes the context to roll through allocating an attr extent for a
> > > > > + * delayed attr operation
> > > > > + */
> > > > > +int
> > > > > +xfs_attr_rmtval_set_init(
> > > > > +	struct xfs_delattr_context	*dac)
> > > > > +{
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_bmbt_irec		*map = &dac->map;
> > > > > +	int error;
> > > > > +
> > > > > +	dac->lblkno = 0;
> > > > > +	dac->lfileoff = 0;
> > > > > +	dac->blkcnt = 0;
> > > > > +	args->rmtblkcnt = 0;
> > > > > +	args->rmtblkno = 0;
> > > > > +	memset(map, 0, sizeof(struct xfs_bmbt_irec));
> > > > > +
> > > > > +	error = xfs_attr_rmt_find_hole(args);
> > > > > +	if (error)
> > > > > +		return error;
> > > > > +
> > > > > +	dac->blkcnt = args->rmtblkcnt;
> > > > > +	dac->lblkno = args->rmtblkno;
> > > > > +
> > > > > +	return error;
> > > > > +}
> > > > > +
> > > > > +/*
> > > > > + * Write one block of the value associated with an attribute into the
> > > > > + * out-of-line buffer that we have defined for it. This is similar to a subset
> > > > > + * of xfs_attr_rmtval_set, but records the current block to the delayed attr
> > > > > + * context, and leaves transaction handling to the caller.
> > > > > + */
> > > > > +int
> > > > > +xfs_attr_rmtval_set_blk(
> > > > > +	struct xfs_delattr_context	*dac)
> > > > > +{
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_inode		*dp = args->dp;
> > > > > +	struct xfs_bmbt_irec		*map = &dac->map;
> > > > > +	int nmap;
> > > > > +	int error;
> > > > > +
> > > > > +	nmap = 1;
> > > > > +	error = xfs_bmapi_write(args->trans, dp,
> > > > > +		  (xfs_fileoff_t)dac->lblkno,
> > > > > +		  dac->blkcnt, XFS_BMAPI_ATTRFORK,
> > > > > +		  args->total, map, &nmap);
> > > > > +	if (error)
> > > > > +		return error;
> > > > > +
> > > > > +	ASSERT(nmap == 1);
> > > > > +	ASSERT((map->br_startblock != DELAYSTARTBLOCK) &&
> > > > > +	       (map->br_startblock != HOLESTARTBLOCK));
> > > > > +
> > > > > +	/* roll attribute extent map forwards */
> > > > > +	dac->lblkno += map->br_blockcount;
> > > > > +	dac->blkcnt -= map->br_blockcount;
> > > > > +
> > > > > +	return 0;
> > > > > +}
> > > > > +
> > > > > +/*
> > > > >     * Remove the value associated with an attribute by deleting the
> > > > >     * out-of-line buffer that it is stored on.
> > > > >     */
> > > > > @@ -671,48 +736,6 @@ xfs_attr_rmtval_invalidate(
> > > > >    }
> > > > >    /*
> > > > > - * Remove the value associated with an attribute by deleting the
> > > > > - * out-of-line buffer that it is stored on.
> > > > > - */
> > > > > -int
> > > > > -xfs_attr_rmtval_remove(
> > > > > -	struct xfs_da_args      *args)
> > > > > -{
> > > > > -	xfs_dablk_t		lblkno;
> > > > > -	int			blkcnt;
> > > > > -	int			error = 0;
> > > > > -	int			done = 0;
> > > > > -
> > > > > -	trace_xfs_attr_rmtval_remove(args);
> > > > > -
> > > > > -	error = xfs_attr_rmtval_invalidate(args);
> > > > > -	if (error)
> > > > > -		return error;
> > > > > -	/*
> > > > > -	 * Keep de-allocating extents until the remote-value region is gone.
> > > > > -	 */
> > > > > -	lblkno = args->rmtblkno;
> > > > > -	blkcnt = args->rmtblkcnt;
> > > > > -	while (!done) {
> > > > > -		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
> > > > > -				    XFS_BMAPI_ATTRFORK, 1, &done);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > -		error = xfs_defer_finish(&args->trans);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > -
> > > > > -		/*
> > > > > -		 * Close out trans and start the next one in the chain.
> > > > > -		 */
> > > > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > -	}
> > > > > -	return 0;
> > > > > -}
> > > > > -
> > > > > -/*
> > > > >     * Remove the value associated with an attribute by deleting the out-of-line
> > > > >     * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
> > > > >     * transaction and recall the function
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> > > > > index ee3337b..482dff9 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr_remote.h
> > > > > +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> > > > > @@ -15,4 +15,8 @@ int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
> > > > >    		xfs_buf_flags_t incore_flags);
> > > > >    int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
> > > > >    int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
> > > > > +int xfs_attr_rmt_find_hole(struct xfs_da_args *args);
> > > > > +int xfs_attr_rmtval_set_value(struct xfs_da_args *args);
> > > > > +int xfs_attr_rmtval_set_blk(struct xfs_delattr_context *dac);
> > > > > +int xfs_attr_rmtval_set_init(struct xfs_delattr_context *dac);
> > > > >    #endif /* __XFS_ATTR_REMOTE_H__ */
> > > > > diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
> > > > > index c42f90e..3e8cec5 100644
> > > > > --- a/fs/xfs/xfs_attr_inactive.c
> > > > > +++ b/fs/xfs/xfs_attr_inactive.c
> > > > > @@ -15,6 +15,7 @@
> > > > >    #include "xfs_da_format.h"
> > > > >    #include "xfs_da_btree.h"
> > > > >    #include "xfs_inode.h"
> > > > > +#include "xfs_attr.h"
> > > > >    #include "xfs_attr_remote.h"
> > > > >    #include "xfs_trans.h"
> > > > >    #include "xfs_bmap.h"
> > > > > diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> > > > > index a4323a6..26dc8bf 100644
> > > > > --- a/fs/xfs/xfs_trace.h
> > > > > +++ b/fs/xfs/xfs_trace.h
> > > > > @@ -1784,7 +1784,6 @@ DEFINE_ATTR_EVENT(xfs_attr_refillstate);
> > > > >    DEFINE_ATTR_EVENT(xfs_attr_rmtval_get);
> > > > >    DEFINE_ATTR_EVENT(xfs_attr_rmtval_set);
> > > > > -DEFINE_ATTR_EVENT(xfs_attr_rmtval_remove);
> > > > >    #define DEFINE_DA_EVENT(name) \
> > > > >    DEFINE_EVENT(xfs_da_class, name, \
> > > > > -- 
> > > > > 2.7.4
> > > > > 
> > > > 
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-03 22:12 ` [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform Allison Collins
  2020-04-07 15:23   ` Brian Foster
@ 2020-04-20  5:30   ` Chandan Rajendra
  2020-04-20 22:45     ` Allison Collins
  1 sibling, 1 reply; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-20  5:30 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> In this patch, we hoist code from xfs_attr_set_args into two new helpers
> xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
> to simplify xfs_attr_set_args when we get into delayed attrs later.
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 107 +++++++++++++++++++++++++++++++----------------
>  1 file changed, 72 insertions(+), 35 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4225a94..ba26ffe 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
>  }
>  
>  /*
> + * Check to see if the attr should be upgraded from non-existent or shortform to
> + * single-leaf-block attribute list.
> + */
> +static inline bool
> +xfs_attr_is_shortform(
> +	struct xfs_inode    *ip)
> +{
> +	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> +	      (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> +	      ip->i_d.di_anextents == 0);
> +}
> +
> +/*
> + * Attempts to set an attr in shortform, or converts the tree to leaf form if

I think you meant to say "converts short form to leaf form".

The functional changes look logically correct,

Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

> + * there is not enough room.  If the attr is set, the transaction is committed
> + * and set to NULL.
> + */
> +STATIC int
> +xfs_attr_set_shortform(
> +	struct xfs_da_args	*args,
> +	struct xfs_buf		**leaf_bp)
> +{
> +	struct xfs_inode	*dp = args->dp;
> +	int			error, error2 = 0;
> +
> +	/*
> +	 * Try to add the attr to the attribute list in the inode.
> +	 */
> +	error = xfs_attr_try_sf_addname(dp, args);
> +	if (error != -ENOSPC) {
> +		error2 = xfs_trans_commit(args->trans);
> +		args->trans = NULL;
> +		return error ? error : error2;
> +	}
> +	/*
> +	 * It won't fit in the shortform, transform to a leaf block.  GROT:
> +	 * another possible req'mt for a double-split btree op.
> +	 */
> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
> +	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. Once we're done rolling the transaction we
> +	 * can release the hold and add the attr to the leaf.
> +	 */
> +	xfs_trans_bhold(args->trans, *leaf_bp);
> +	error = xfs_defer_finish(&args->trans);
> +	xfs_trans_bhold_release(args->trans, *leaf_bp);
> +	if (error) {
> +		xfs_trans_brelse(args->trans, *leaf_bp);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
>   * Set the attribute specified in @args.
>   */
>  int
> @@ -212,48 +272,25 @@ xfs_attr_set_args(
>  {
>  	struct xfs_inode	*dp = args->dp;
>  	struct xfs_buf          *leaf_bp = NULL;
> -	int			error, error2 = 0;
> +	int			error = 0;
>  
>  	/*
> -	 * If the attribute list is non-existent or a shortform list,
> -	 * upgrade it to a single-leaf-block attribute list.
> +	 * 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.
>  	 */
> -	if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
> -	    (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
> -	     dp->i_d.di_anextents == 0)) {
> -
> -		/*
> -		 * Try to add the attr to the attribute list in the inode.
> -		 */
> -		error = xfs_attr_try_sf_addname(dp, args);
> -		if (error != -ENOSPC) {
> -			error2 = xfs_trans_commit(args->trans);
> -			args->trans = NULL;
> -			return error ? error : error2;
> -		}
> -
> -		/*
> -		 * It won't fit in the shortform, transform to a leaf block.
> -		 * GROT: another possible req'mt for a double-split btree op.
> -		 */
> -		error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
> -		if (error)
> -			return error;
> +	if (xfs_attr_is_shortform(dp)) {
>  
>  		/*
> -		 * 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.
> -		 * Once we're done rolling the transaction we can release
> -		 * the hold and add the attr to the leaf.
> +		 * If the attr was successfully set in shortform, the
> +		 * transaction is committed and set to NULL.  Otherwise, is it
> +		 * converted from shortform to leaf, and the transaction is
> +		 * retained.
>  		 */
> -		xfs_trans_bhold(args->trans, leaf_bp);
> -		error = xfs_defer_finish(&args->trans);
> -		xfs_trans_bhold_release(args->trans, leaf_bp);
> -		if (error) {
> -			xfs_trans_brelse(args->trans, leaf_bp);
> +		error = xfs_attr_set_shortform(args, &leaf_bp);
> +		if (error || !args->trans)
>  			return error;
> -		}
>  	}
>  
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> 


-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup
  2020-04-03 22:12 ` [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup Allison Collins
  2020-04-08 12:09   ` Brian Foster
@ 2020-04-20  6:25   ` Chandan Rajendra
  2020-04-20 22:46     ` Allison Collins
  1 sibling, 1 reply; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-20  6:25 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> This patch adds a new helper function xfs_attr_node_removename_setup.
> This will help modularize xfs_attr_node_removename when we add delay
> ready attributes later.
>
The changes look good to me,

Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 40 +++++++++++++++++++++++++++++++---------
>  1 file changed, 31 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index f70b4f2..3c33dc5 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1193,6 +1193,35 @@ xfs_attr_leaf_mark_incomplete(
>  }
>  
>  /*
> + * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
> + * the blocks are valid.  Any remote blocks will be marked incomplete.
> + */
> +STATIC
> +int xfs_attr_node_removename_setup(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	**state)
> +{
> +	int			error;
> +	struct xfs_da_state_blk	*blk;
> +
> +	error = xfs_attr_node_hasname(args, state);
> +	if (error != -EEXIST)
> +		return error;
> +
> +	blk = &(*state)->path.blk[(*state)->path.active - 1];
> +	ASSERT(blk->bp != NULL);
> +	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> +
> +	if (args->rmtblkno > 0) {
> +		error = xfs_attr_leaf_mark_incomplete(args, *state);
> +		if (error)
> +			return error;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
>   * Remove a name from a B-tree attribute list.
>   *
>   * This will involve walking down the Btree, and may involve joining
> @@ -1210,8 +1239,8 @@ xfs_attr_node_removename(
>  
>  	trace_xfs_attr_node_removename(args);
>  
> -	error = xfs_attr_node_hasname(args, &state);
> -	if (error != -EEXIST)
> +	error = xfs_attr_node_removename_setup(args, &state);
> +	if (error)
>  		goto out;
>  
>  	/*
> @@ -1219,14 +1248,7 @@ xfs_attr_node_removename(
>  	 * This is done before we remove the attribute so that we don't
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
> -	blk = &state->path.blk[ state->path.active-1 ];
> -	ASSERT(blk->bp != NULL);
> -	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_leaf_mark_incomplete(args, state);
> -		if (error)
> -			goto out;
> -
>  		error = xfs_attr_rmtval_remove(args);
>  		if (error)
>  			goto out;
> 


-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt
  2020-04-03 22:12 ` [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt Allison Collins
  2020-04-08 12:10   ` Brian Foster
@ 2020-04-20  6:38   ` Chandan Rajendra
  2020-04-20 22:46     ` Allison Collins
  1 sibling, 1 reply; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-20  6:38 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> This patch adds another new helper function
> xfs_attr_node_removename_rmt. This will also help modularize
> xfs_attr_node_removename when we add delay ready attributes later.
>

The changes look logically correct.

Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 32 +++++++++++++++++++++++---------
>  1 file changed, 23 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 3c33dc5..d735570 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1221,6 +1221,28 @@ int xfs_attr_node_removename_setup(
>  	return 0;
>  }
>  
> +STATIC int
> +xfs_attr_node_removename_rmt (
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	*state)
> +{
> +	int			error = 0;
> +
> +	error = xfs_attr_rmtval_remove(args);
> +	if (error)
> +		return error;
> +
> +	/*
> +	 * Refill the state structure with buffers, the prior calls
> +	 * released our buffers.
> +	 */
> +	error = xfs_attr_refillstate(state);
> +	if (error)
> +		return error;
> +
> +	return 0;
> +}
> +
>  /*
>   * Remove a name from a B-tree attribute list.
>   *
> @@ -1249,15 +1271,7 @@ xfs_attr_node_removename(
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_rmtval_remove(args);
> -		if (error)
> -			goto out;
> -
> -		/*
> -		 * Refill the state structure with buffers, the prior calls
> -		 * released our buffers.
> -		 */
> -		error = xfs_attr_refillstate(state);
> +		error = xfs_attr_node_removename_rmt(args, state);
>  		if (error)
>  			goto out;
>  	}
> 


-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-03 22:12 ` [PATCH v8 18/20] xfs: Add delay ready attr remove routines Allison Collins
  2020-04-13 12:30   ` Brian Foster
@ 2020-04-20  9:06   ` Chandan Rajendra
  2020-04-20 23:26     ` Allison Collins
  1 sibling, 1 reply; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-20  9:06 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> This patch modifies the attr remove routines to be delay ready. This
> means they no longer roll or commit transactions, but instead return
> -EAGAIN to have the calling routine roll and refresh the transaction. In
> this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
> uses a sort of state machine like switch to keep track of where it was
> when EAGAIN was returned. xfs_attr_node_removename has also been
> modified to use the switch, and a new version of xfs_attr_remove_args
> consists of a simple loop to refresh the transaction until the operation
> is completed.
> 
> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
> helper) and then __xfs_attr_rmtval_remove. We will rename
> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> done.
> 
> This patch also adds a new struct xfs_delattr_context, which we will use
> to keep track of the current state of an attribute operation. The new
> xfs_delattr_state enum is used to track various operations that are in
> progress so that we know not to repeat them, and resume where we left
> off before EAGAIN was returned to cycle out the transaction. Other
> members take the place of local variables that need to retain their
> values across multiple function recalls.
> 
> Below is a state machine diagram for attr remove operations. The
> XFS_DAS_* states indicate places where the function would return
> -EAGAIN, and then immediately resume from after being recalled by the
> calling function.  States marked as a "subroutine state" indicate that
> they belong to a subroutine, and so the calling function needs to pass
> them back to that subroutine to allow it to finish where it left off.
> But they otherwise do not have a role in the calling function other than
> just passing through.
> 
>  xfs_attr_remove_iter()
>          XFS_DAS_RM_SHRINK     ─┐
>          (subroutine state)     │
>                                 │
>          XFS_DAS_RMTVAL_REMOVE ─┤
>          (subroutine state)     │
>                                 └─>xfs_attr_node_removename()
>                                                  │
>                                                  v
>                                          need to remove
>                                    ┌─n──  rmt blocks?
>                                    │             │
>                                    │             y
>                                    │             │
>                                    │             v
>                                    │  ┌─>XFS_DAS_RMTVAL_REMOVE
>                                    │  │          │
>                                    │  │          v
>                                    │  └──y── more blks
>                                    │         to remove?
>                                    │             │
>                                    │             n
>                                    │             │
>                                    │             v
>                                    │         need to
>                                    └─────> shrink tree? ─n─┐
>                                                  │         │
>                                                  y         │
>                                                  │         │
>                                                  v         │
>                                          XFS_DAS_RM_SHRINK │
>                                                  │         │
>                                                  v         │
>                                                 done <─────┘
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
>  fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
>  2 files changed, 168 insertions(+), 38 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index d735570..f700976 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>   */
>  STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>  STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
> +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>  
>  /*
> @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>   */
>  STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>  STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
> +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>  				 struct xfs_da_state **state);
>  STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
>  STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
>  
> +STATIC void
> +xfs_delattr_context_init(
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_args		*args)
> +{
> +	memset(dac, 0, sizeof(struct xfs_delattr_context));
> +	dac->da_args = args;
> +}
> +
>  int
>  xfs_inode_hasattr(
>  	struct xfs_inode	*ip)
> @@ -356,20 +365,66 @@ xfs_has_attr(
>   */
>  int
>  xfs_attr_remove_args(
> -	struct xfs_da_args      *args)
> +	struct xfs_da_args	*args)
>  {
> +	int			error = 0;
> +	struct			xfs_delattr_context dac;
> +
> +	xfs_delattr_context_init(&dac, args);
> +
> +	do {
> +		error = xfs_attr_remove_iter(&dac);
> +		if (error != -EAGAIN)
> +			break;
> +
> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> +			error = xfs_defer_finish(&args->trans);
> +			if (error)
> +				break;
> +		}
> +
> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +		if (error)
> +			break;
> +	} while (true);
> +
> +	return error;
> +}
> +
> +/*
> + * Remove the attribute specified in @args.
> + *
> + * This function may return -EAGAIN to signal that the transaction needs to be
> + * rolled.  Callers should continue calling this function until they receive a
> + * return value other than -EAGAIN.
> + */
> +int
> +xfs_attr_remove_iter(
> +	struct xfs_delattr_context *dac)
> +{
> +	struct xfs_da_args	*args = dac->da_args;
>  	struct xfs_inode	*dp = args->dp;
>  	int			error;
>  
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_RM_SHRINK:
> +	case XFS_DAS_RMTVAL_REMOVE:
> +		return xfs_attr_node_removename(dac);
> +	default:
> +		break;
> +	}
> +
>  	if (!xfs_inode_hasattr(dp)) {
>  		error = -ENOATTR;
>  	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>  		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>  		error = xfs_attr_shortform_remove(args);
>  	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		error = xfs_attr_leaf_removename(args);
> +		error = xfs_attr_leaf_removename(dac);
>  	} else {
> -		error = xfs_attr_node_removename(args);
> +		error = xfs_attr_node_removename(dac);
>  	}
>  
>  	return error;
> @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
>   */
>  STATIC int
>  xfs_attr_leaf_removename(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> -	struct xfs_inode	*dp;
> -	struct xfs_buf		*bp;
> -	int			error, forkoff;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp;
> +	struct xfs_buf			*bp;
> +	int				error, forkoff;
>  
>  	trace_xfs_attr_leaf_removename(args);
>  
> @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
>  		/* bp is gone due to xfs_da_shrink_inode */
>  		if (error)
>  			return error;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> +
> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>  	}
>  	return 0;
>  }
> @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
>   */
>  STATIC int
>  xfs_attr_node_shrink(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state     *state)
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_state		*state)
>  {
> -	struct xfs_inode	*dp = args->dp;
> -	int			error, forkoff;
> -	struct xfs_buf		*bp;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	int				error, forkoff;
> +	struct xfs_buf			*bp;
>  
>  	/*
>  	 * Have to get rid of the copy of this dabuf in the state.
> @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
>  		if (error)
>  			return error;
>  
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>  	} else
>  		xfs_trans_brelse(args->trans, bp);
>  
> @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
>  
>  /*
>   * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
> - * the blocks are valid.  Any remote blocks will be marked incomplete.
> + * the blocks are valid.  Any remote blocks will be marked incomplete and
> + * invalidated.
>   */
>  STATIC
>  int xfs_attr_node_removename_setup(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state	**state)
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_state		**state)
>  {
> +	struct xfs_da_args	*args = dac->da_args;
>  	int			error;
>  	struct xfs_da_state_blk	*blk;
>  
> @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
>  	ASSERT(blk->bp != NULL);
>  	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  
> +	/*
> +	 * Store blk and state in the context incase we need to cycle out the
> +	 * transaction
> +	 */
> +	dac->blk = blk;
> +	dac->da_state = *state;
> +
>  	if (args->rmtblkno > 0) {
>  		error = xfs_attr_leaf_mark_incomplete(args, *state);
>  		if (error)
>  			return error;
> +
> +		error = xfs_attr_rmtval_invalidate(args);
> +		if (error)
> +			return error;
>  	}
>  
>  	return 0;
> @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
>  {
>  	int			error = 0;
>  
> -	error = xfs_attr_rmtval_remove(args);
> +	/*
> +	 * May return -EAGAIN to request that the caller recall this function
> +	 */
> +	error = __xfs_attr_rmtval_remove(args);
>  	if (error)
>  		return error;
>  
> @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
>   * This will involve walking down the Btree, and may involve joining
>   * leaf nodes and even joining intermediate nodes up to and including
>   * the root node (a special case of an intermediate node).
> + *
> + * This routine is meant to function as either an inline or 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_node_removename(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> +	struct xfs_da_args	*args = dac->da_args;
>  	struct xfs_da_state	*state;
>  	struct xfs_da_state_blk	*blk;
>  	int			retval, error;
>  	struct xfs_inode	*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
> +	state = dac->da_state;
> +	blk = dac->blk;
> +
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_RMTVAL_REMOVE:
> +		goto das_rmtval_remove;
> +	case XFS_DAS_RM_SHRINK:
> +		goto das_rm_shrink;
> +	default:
> +		break;
> +	}
>  
> -	error = xfs_attr_node_removename_setup(args, &state);
> +	error = xfs_attr_node_removename_setup(dac, &state);
>  	if (error)
>  		goto out;
>  
> @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
>  	 * This is done before we remove the attribute so that we don't
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
> +
> +das_rmtval_remove:
> +
>  	if (args->rmtblkno > 0) {
>  		error = xfs_attr_node_removename_rmt(args, state);
> -		if (error)
> -			goto out;
> +		if (error) {
> +			if (error == -EAGAIN)
> +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;

Shouldn't XFS_DAC_DEFER_FINISH be set in dac->flags?
xfs_attr_node_removename_rmt() indirectly calls __xfs_bunmapi() which would
have added items to the deferred list.

-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-03 22:12 ` [PATCH v8 19/20] xfs: Add delay ready attr set routines Allison Collins
  2020-04-13 13:40   ` Brian Foster
@ 2020-04-20 11:45   ` Chandan Rajendra
  2020-04-20 16:20     ` Brian Foster
  1 sibling, 1 reply; 70+ messages in thread
From: Chandan Rajendra @ 2020-04-20 11:45 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
> This patch modifies the attr set routines to be delay ready. This means
> they no longer roll or commit transactions, but instead return -EAGAIN
> to have the calling routine roll and refresh the transaction.  In this
> series, xfs_attr_set_args has become xfs_attr_set_iter, which uses a
> state machine like switch to keep track of where it was when EAGAIN was
> returned.
> 
> Two new helper functions have been added: xfs_attr_rmtval_set_init and
> xfs_attr_rmtval_set_blk.  They provide a subset of logic similar to
> xfs_attr_rmtval_set, but they store the current block in the delay attr
> context to allow the caller to roll the transaction between allocations.
> This helps to simplify and consolidate code used by
> xfs_attr_leaf_addname and xfs_attr_node_addname. xfs_attr_set_args has
> now become a simple loop to refresh the transaction until the operation
> is completed.  Lastly, xfs_attr_rmtval_remove is no longer used, and is
> removed.
> 
> Below is a state machine diagram for attr set operations. The XFS_DAS_*
> states indicate places where the function would return -EAGAIN, and then
> immediately resume from after being recalled by the calling function.
> States marked as a "subroutine state" indicate that they belong to a
> subroutine, and so the calling function needs to pass them back to that
> subroutine to allow it to finish where it left off.  But they otherwise
> do not have a role in the calling function other than just passing
> through.
> 
>  xfs_attr_set_iter()
>                  │
>                  v
>            need to upgrade
>           from sf to leaf? ──n─┐
>                  │             │
>                  y             │
>                  │             │
>                  V             │
>           XFS_DAS_ADD_LEAF     │
>                  │             │
>                  v             │
>   ┌──────n── fork has   <──────┘
>   │         only 1 blk?
>   │              │
>   │              y
>   │              │
>   │              v
>   │     xfs_attr_leaf_try_add()
>   │              │
>   │              v
>   │          had enough
>   ├──────n──   space?
>   │              │
>   │              y
>   │              │
>   │              v
>   │      XFS_DAS_FOUND_LBLK  ──┐
>   │                            │
>   │      XFS_DAS_FLIP_LFLAG  ──┤
>   │      (subroutine state)    │
>   │                            │
>   │      XFS_DAS_ALLOC_LEAF  ──┤
>   │      (subroutine state)    │
>   │                            └─>xfs_attr_leaf_addname()
>   │                                              │
>   │                                              v
>   │                                ┌─────n──  need to
>   │                                │        alloc blks?
>   │                                │             │
>   │                                │             y
>   │                                │             │
>   │                                │             v
>   │                                │  ┌─>XFS_DAS_ALLOC_LEAF
>   │                                │  │          │
>   │                                │  │          v
>   │                                │  └──y── need to alloc
>   │                                │         more blocks?
>   │                                │             │
>   │                                │             n
>   │                                │             │
>   │                                │             v
>   │                                │          was this
>   │                                └────────> a rename? ──n─┐
>   │                                              │          │
>   │                                              y          │
>   │                                              │          │
>   │                                              v          │
>   │                                        flip incomplete  │
>   │                                            flag         │
>   │                                              │          │
>   │                                              v          │
>   │                                      XFS_DAS_FLIP_LFLAG │
>   │                                              │          │
>   │                                              v          │
>   │                                            remove       │
>   │                        XFS_DAS_RM_LBLK ─> old name      │
>   │                                 ^            │          │
>   │                                 │            v          │
>   │                                 └──────y── more to      │
>   │                                            remove       │
>   │                                              │          │
>   │                                              n          │
>   │                                              │          │
>   │                                              v          │
>   │                                             done <──────┘
>   └────> XFS_DAS_LEAF_TO_NODE ─┐
>                                │
>          XFS_DAS_FOUND_NBLK  ──┤
>          (subroutine state)    │
>                                │
>          XFS_DAS_ALLOC_NODE  ──┤
>          (subroutine state)    │
>                                │
>          XFS_DAS_FLIP_NFLAG  ──┤
>          (subroutine state)    │
>                                │
>                                └─>xfs_attr_node_addname()
>                                                  │
>                                                  v
>                                          find space to store
>                                         attr. Split if needed
>                                                  │
>                                                  v
>                                          XFS_DAS_FOUND_NBLK
>                                                  │
>                                                  v
>                                    ┌─────n──  need to
>                                    │        alloc blks?
>                                    │             │
>                                    │             y
>                                    │             │
>                                    │             v
>                                    │  ┌─>XFS_DAS_ALLOC_NODE
>                                    │  │          │
>                                    │  │          v
>                                    │  └──y── need to alloc
>                                    │         more blocks?
>                                    │             │
>                                    │             n
>                                    │             │
>                                    │             v
>                                    │          was this
>                                    └────────> a rename? ──n─┐
>                                                  │          │
>                                                  y          │
>                                                  │          │
>                                                  v          │
>                                            flip incomplete  │
>                                                flag         │
>                                                  │          │
>                                                  v          │
>                                          XFS_DAS_FLIP_NFLAG │
>                                                  │          │
>                                                  v          │
>                                                remove       │
>                            XFS_DAS_RM_NBLK ─> old name      │
>                                     ^            │          │
>                                     │            v          │
>                                     └──────y── more to      │
>                                                remove       │
>                                                  │          │
>                                                  n          │
>                                                  │          │
>                                                  v          │
>                                                 done <──────┘
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
>  fs/xfs/libxfs/xfs_attr.h        |  16 ++
>  fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
>  fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
>  fs/xfs/libxfs/xfs_attr_remote.h |   4 +
>  fs/xfs/xfs_attr_inactive.c      |   1 +
>  fs/xfs/xfs_trace.h              |   1 -
>  7 files changed, 351 insertions(+), 167 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index f700976..c160b7a 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -44,7 +44,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>   * Internal routines when attribute list is one block.
>   */
>  STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
> -STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
> +STATIC int xfs_attr_leaf_addname(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>  
> @@ -52,12 +52,13 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>   * Internal routines when attribute list is more than one block.
>   */
>  STATIC int xfs_attr_node_get(xfs_da_args_t *args);
> -STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
> +STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>  				 struct xfs_da_state **state);
>  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 void
>  xfs_delattr_context_init(
> @@ -227,8 +228,11 @@ xfs_attr_is_shortform(
>  
>  /*
>   * Attempts to set an attr in shortform, or converts the tree to leaf form if
> - * there is not enough room.  If the attr is set, the transaction is committed
> - * and set to NULL.
> + * there is not enough room.  This function is meant to operate as a helper
> + * routine to the delayed attribute functions.  It returns -EAGAIN to indicate
> + * that the calling function should roll the transaction, and then proceed to
> + * add the attr in leaf form.  This subroutine does not expect to be recalled
> + * again like the other delayed attr routines do.
>   */
>  STATIC int
>  xfs_attr_set_shortform(
> @@ -236,16 +240,16 @@ xfs_attr_set_shortform(
>  	struct xfs_buf		**leaf_bp)
>  {
>  	struct xfs_inode	*dp = args->dp;
> -	int			error, error2 = 0;
> +	int			error = 0;
>  
>  	/*
>  	 * Try to add the attr to the attribute list in the inode.
>  	 */
>  	error = xfs_attr_try_sf_addname(dp, args);
> +
> +	/* Should only be 0, -EEXIST or ENOSPC */
>  	if (error != -ENOSPC) {
> -		error2 = xfs_trans_commit(args->trans);
> -		args->trans = NULL;
> -		return error ? error : error2;
> +		return error;
>  	}
>  	/*
>  	 * It won't fit in the shortform, transform to a leaf block.  GROT:
> @@ -258,18 +262,10 @@ xfs_attr_set_shortform(
>  	/*
>  	 * 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. Once we're done rolling the transaction we
> -	 * can release the hold and add the attr to the leaf.
> +	 * with the write verifier.
>  	 */
>  	xfs_trans_bhold(args->trans, *leaf_bp);
> -	error = xfs_defer_finish(&args->trans);
> -	xfs_trans_bhold_release(args->trans, *leaf_bp);
> -	if (error) {
> -		xfs_trans_brelse(args->trans, *leaf_bp);
> -		return error;
> -	}
> -
> -	return 0;
> +	return -EAGAIN;
>  }
>  
>  /*
> @@ -279,9 +275,83 @@ int
>  xfs_attr_set_args(
>  	struct xfs_da_args	*args)
>  {
> -	struct xfs_inode	*dp = args->dp;
> -	struct xfs_buf          *leaf_bp = NULL;
> -	int			error = 0;
> +	struct xfs_buf			*leaf_bp = NULL;
> +	int				error = 0;
> +	struct xfs_delattr_context	dac;
> +
> +	xfs_delattr_context_init(&dac, args);
> +
> +	do {
> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
> +		if (error != -EAGAIN)
> +			break;
> +
> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
> +			error = xfs_defer_finish(&args->trans);
> +			if (error)
> +				break;
> +		}
> +
> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +		if (error)
> +			break;
> +
> +		if (leaf_bp) {
> +			xfs_trans_bjoin(args->trans, leaf_bp);
> +			xfs_trans_bhold(args->trans, leaf_bp);
> +		}
> +
> +	} while (true);
> +
> +	return error;
> +}
> +
> +/*
> + * 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.
> + */
> +int
> +xfs_attr_set_iter(
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_buf			**leaf_bp)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	int				error = 0;
> +	int				sf_size;
> +
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_ADD_LEAF:
> +		goto das_add_leaf;
> +	case XFS_DAS_ALLOC_LEAF:
> +	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:
> +	case XFS_DAS_LEAF_TO_NODE:
> +		goto das_node;
> +	default:
> +		break;
> +	}
> +
> +	/*
> +	 * New inodes may not have an attribute fork yet. So set the attribute
> +	 * fork appropriately
> +	 */
> +	if (XFS_IFORK_Q((args->dp)) == 0) {
> +		sf_size = sizeof(struct xfs_attr_sf_hdr) +
> +		     XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
> +		xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
> +		args->dp->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0);
> +		args->dp->i_afp->if_flags = XFS_IFEXTENTS;
> +	}
>  
>  	/*
>  	 * If the attribute list is already in leaf format, jump straight to
> @@ -292,40 +362,53 @@ xfs_attr_set_args(
>  	if (xfs_attr_is_shortform(dp)) {
>  
>  		/*
> -		 * If the attr was successfully set in shortform, the
> -		 * transaction is committed and set to NULL.  Otherwise, is it
> -		 * converted from shortform to leaf, and the transaction is
> -		 * retained.
> +		 * 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 || !args->trans)
> -			return error;
> +		error = xfs_attr_set_shortform(args, leaf_bp);
> +		if (error == -EAGAIN) {
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_ADD_LEAF;
> +		}
> +		return error;
>  	}
>  
> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		error = xfs_attr_leaf_addname(args);
> -		if (error != -ENOSPC)
> -			return error;
> +das_add_leaf:
>  
> -		/*
> -		 * Commit that transaction so that the node_addname()
> -		 * call can manage its own transactions.
> -		 */
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> +	/*
> +	 * After a shortform to leaf conversion, we need to hold the leaf and
> +	 * cylce out the transaction.  When we get back, we need to release
> +	 * the leaf.
> +	 */
> +	if (*leaf_bp != NULL) {
> +		xfs_trans_brelse(args->trans, *leaf_bp);
> +		*leaf_bp = NULL;
> +	}
>  
> -		/*
> -		 * Commit the current trans (including the inode) and
> -		 * start a new one.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> +		error = xfs_attr_leaf_try_add(args, *leaf_bp);
> +		switch (error) {
> +		case -ENOSPC:
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> +			return -EAGAIN;
> +		case 0:
> +			dac->dela_state = XFS_DAS_FOUND_LBLK;
> +			return -EAGAIN;
> +		default:
>  			return error;
> -
> +		}
> +das_leaf:
> +		error = xfs_attr_leaf_addname(dac);
> +		if (error == -ENOSPC) {
> +			dac->dela_state = XFS_DAS_LEAF_TO_NODE;
> +			return -EAGAIN;
> +		}
> +		return error;
>  	}
> -
> -	error = xfs_attr_node_addname(args);
> +das_node:
> +	error = xfs_attr_node_addname(dac);
>  	return error;
>  }
>  
> @@ -716,28 +799,32 @@ xfs_attr_leaf_try_add(
>   *
>   * This leaf block cannot have a "remote" value, we only call this routine
>   * if bmap_one_block() says there is only one block (ie: no remote blks).
> + *
> + * 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_leaf_addname(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> -	int			error, forkoff;
> -	struct xfs_buf		*bp = NULL;
> -	struct xfs_inode	*dp = args->dp;
> -
> -	trace_xfs_attr_leaf_addname(args);
> -
> -	error = xfs_attr_leaf_try_add(args, bp);
> -	if (error)
> -		return error;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_buf			*bp = NULL;
> +	int				error, forkoff;
> +	struct xfs_inode		*dp = args->dp;
>  
> -	/*
> -	 * Commit the transaction that added the attr name so that
> -	 * later routines can manage their own transactions.
> -	 */
> -	error = xfs_trans_roll_inode(&args->trans, dp);
> -	if (error)
> -		return error;
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_FLIP_LFLAG:
> +		goto das_flip_flag;
> +	case XFS_DAS_ALLOC_LEAF:
> +		goto das_alloc_leaf;
> +	case XFS_DAS_RM_LBLK:
> +		goto das_rm_lblk;
> +	default:
> +		break;
> +	}
>  
>  	/*
>  	 * If there was an out-of-line value, allocate the blocks we
> @@ -746,7 +833,28 @@ xfs_attr_leaf_addname(
>  	 * maximum size of a transaction and/or hit a deadlock.
>  	 */
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_rmtval_set(args);
> +
> +		/* Open coded xfs_attr_rmtval_set without trans handling */
> +		error = xfs_attr_rmtval_set_init(dac);
> +		if (error)
> +			return error;
> +
> +		/*
> +		 * Roll through the "value", allocating blocks on disk as
> +		 * required.
> +		 */
> +das_alloc_leaf:
> +		while (dac->blkcnt > 0) {
> +			error = xfs_attr_rmtval_set_blk(dac);
> +			if (error)
> +				return error;
> +
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_ALLOC_LEAF;
> +			return -EAGAIN;
> +		}
> +
> +		error = xfs_attr_rmtval_set_value(args);
>  		if (error)
>  			return error;
>  	}
> @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
>  		error = xfs_attr3_leaf_flipflags(args);
>  		if (error)
>  			return error;
> -		/*
> -		 * Commit the flag value change and start the next trans in
> -		 * series.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -		if (error)
> -			return error;
> -
> +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> +		return -EAGAIN;
> +das_flip_flag:
>  		/*
>  		 * Dismantle the "old" attribute/value pair by removing
>  		 * a "remote" value (if it exists).
>  		 */
>  		xfs_attr_restore_rmt_blk(args);
>  
> +		xfs_attr_rmtval_invalidate(args);
> +das_rm_lblk:
>  		if (args->rmtblkno) {
> -			error = xfs_attr_rmtval_remove(args);
> +			error = __xfs_attr_rmtval_remove(args);
> +
> +			if (error == -EAGAIN) {
> +				dac->dela_state = XFS_DAS_RM_LBLK;

Similar to what I had observed in the patch "Add delay ready attr remove
routines",

Shouldn't XFS_DAC_DEFER_FINISH be set in dac->flags?
__xfs_attr_rmtval_remove() calls __xfs_bunmapi() which would
have added items to the deferred list.

-- 
chandan




^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-20 11:45   ` Chandan Rajendra
@ 2020-04-20 16:20     ` Brian Foster
  2020-04-20 23:26       ` Allison Collins
  0 siblings, 1 reply; 70+ messages in thread
From: Brian Foster @ 2020-04-20 16:20 UTC (permalink / raw)
  To: Chandan Rajendra; +Cc: Allison Collins, linux-xfs

On Mon, Apr 20, 2020 at 05:15:08PM +0530, Chandan Rajendra wrote:
> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote: 
...
> > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
> >  fs/xfs/libxfs/xfs_attr.h        |  16 ++
> >  fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
> >  fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
> >  fs/xfs/libxfs/xfs_attr_remote.h |   4 +
> >  fs/xfs/xfs_attr_inactive.c      |   1 +
> >  fs/xfs/xfs_trace.h              |   1 -
> >  7 files changed, 351 insertions(+), 167 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > index f700976..c160b7a 100644
> > --- a/fs/xfs/libxfs/xfs_attr.c
> > +++ b/fs/xfs/libxfs/xfs_attr.c
...
> > @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
> >  		error = xfs_attr3_leaf_flipflags(args);
> >  		if (error)
> >  			return error;
> > -		/*
> > -		 * Commit the flag value change and start the next trans in
> > -		 * series.
> > -		 */
> > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > -		if (error)
> > -			return error;
> > -
> > +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> > +		return -EAGAIN;
> > +das_flip_flag:
> >  		/*
> >  		 * Dismantle the "old" attribute/value pair by removing
> >  		 * a "remote" value (if it exists).
> >  		 */
> >  		xfs_attr_restore_rmt_blk(args);
> >  
> > +		xfs_attr_rmtval_invalidate(args);
> > +das_rm_lblk:
> >  		if (args->rmtblkno) {
> > -			error = xfs_attr_rmtval_remove(args);
> > +			error = __xfs_attr_rmtval_remove(args);
> > +
> > +			if (error == -EAGAIN) {
> > +				dac->dela_state = XFS_DAS_RM_LBLK;
> 
> Similar to what I had observed in the patch "Add delay ready attr remove
> routines",
> 
> Shouldn't XFS_DAC_DEFER_FINISH be set in dac->flags?
> __xfs_attr_rmtval_remove() calls __xfs_bunmapi() which would
> have added items to the deferred list.
> 

Just note that transaction rolls don't currently finish deferred ops. So
from the perspective of preserving current behavior it might make sense
to set the flag here if there was an explicit xfs_defer_finish() that's
been factored out, but not so if it was just a transaction roll.

Brian

> -- 
> chandan
> 
> 
> 


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform
  2020-04-20  5:30   ` Chandan Rajendra
@ 2020-04-20 22:45     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-20 22:45 UTC (permalink / raw)
  To: Chandan Rajendra; +Cc: linux-xfs



On 4/19/20 10:30 PM, Chandan Rajendra wrote:
> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote:
>> In this patch, we hoist code from xfs_attr_set_args into two new helpers
>> xfs_attr_is_shortform and xfs_attr_set_shortform.  These two will help
>> to simplify xfs_attr_set_args when we get into delayed attrs later.
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 107 +++++++++++++++++++++++++++++++----------------
>>   1 file changed, 72 insertions(+), 35 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4225a94..ba26ffe 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -204,6 +204,66 @@ xfs_attr_try_sf_addname(
>>   }
>>   
>>   /*
>> + * Check to see if the attr should be upgraded from non-existent or shortform to
>> + * single-leaf-block attribute list.
>> + */
>> +static inline bool
>> +xfs_attr_is_shortform(
>> +	struct xfs_inode    *ip)
>> +{
>> +	return ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>> +	      (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>> +	      ip->i_d.di_anextents == 0);
>> +}
>> +
>> +/*
>> + * Attempts to set an attr in shortform, or converts the tree to leaf form if
> 
> I think you meant to say "converts short form to leaf form".
> 
> The functional changes look logically correct,
Ok, will fix the comment.  Thanks for the review!

Allison

> 
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
> 
>> + * there is not enough room.  If the attr is set, the transaction is committed
>> + * and set to NULL.
>> + */
>> +STATIC int
>> +xfs_attr_set_shortform(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_buf		**leaf_bp)
>> +{
>> +	struct xfs_inode	*dp = args->dp;
>> +	int			error, error2 = 0;
>> +
>> +	/*
>> +	 * Try to add the attr to the attribute list in the inode.
>> +	 */
>> +	error = xfs_attr_try_sf_addname(dp, args);
>> +	if (error != -ENOSPC) {
>> +		error2 = xfs_trans_commit(args->trans);
>> +		args->trans = NULL;
>> +		return error ? error : error2;
>> +	}
>> +	/*
>> +	 * It won't fit in the shortform, transform to a leaf block.  GROT:
>> +	 * another possible req'mt for a double-split btree op.
>> +	 */
>> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
>> +	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. Once we're done rolling the transaction we
>> +	 * can release the hold and add the attr to the leaf.
>> +	 */
>> +	xfs_trans_bhold(args->trans, *leaf_bp);
>> +	error = xfs_defer_finish(&args->trans);
>> +	xfs_trans_bhold_release(args->trans, *leaf_bp);
>> +	if (error) {
>> +		xfs_trans_brelse(args->trans, *leaf_bp);
>> +		return error;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>>    * Set the attribute specified in @args.
>>    */
>>   int
>> @@ -212,48 +272,25 @@ xfs_attr_set_args(
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>>   	struct xfs_buf          *leaf_bp = NULL;
>> -	int			error, error2 = 0;
>> +	int			error = 0;
>>   
>>   	/*
>> -	 * If the attribute list is non-existent or a shortform list,
>> -	 * upgrade it to a single-leaf-block attribute list.
>> +	 * 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.
>>   	 */
>> -	if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL ||
>> -	    (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
>> -	     dp->i_d.di_anextents == 0)) {
>> -
>> -		/*
>> -		 * Try to add the attr to the attribute list in the inode.
>> -		 */
>> -		error = xfs_attr_try_sf_addname(dp, args);
>> -		if (error != -ENOSPC) {
>> -			error2 = xfs_trans_commit(args->trans);
>> -			args->trans = NULL;
>> -			return error ? error : error2;
>> -		}
>> -
>> -		/*
>> -		 * It won't fit in the shortform, transform to a leaf block.
>> -		 * GROT: another possible req'mt for a double-split btree op.
>> -		 */
>> -		error = xfs_attr_shortform_to_leaf(args, &leaf_bp);
>> -		if (error)
>> -			return error;
>> +	if (xfs_attr_is_shortform(dp)) {
>>   
>>   		/*
>> -		 * 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.
>> -		 * Once we're done rolling the transaction we can release
>> -		 * the hold and add the attr to the leaf.
>> +		 * If the attr was successfully set in shortform, the
>> +		 * transaction is committed and set to NULL.  Otherwise, is it
>> +		 * converted from shortform to leaf, and the transaction is
>> +		 * retained.
>>   		 */
>> -		xfs_trans_bhold(args->trans, leaf_bp);
>> -		error = xfs_defer_finish(&args->trans);
>> -		xfs_trans_bhold_release(args->trans, leaf_bp);
>> -		if (error) {
>> -			xfs_trans_brelse(args->trans, leaf_bp);
>> +		error = xfs_attr_set_shortform(args, &leaf_bp);
>> +		if (error || !args->trans)
>>   			return error;
>> -		}
>>   	}
>>   
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>
> 
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup
  2020-04-20  6:25   ` Chandan Rajendra
@ 2020-04-20 22:46     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-20 22:46 UTC (permalink / raw)
  To: Chandan Rajendra; +Cc: linux-xfs



On 4/19/20 11:25 PM, Chandan Rajendra wrote:
> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote:
>> This patch adds a new helper function xfs_attr_node_removename_setup.
>> This will help modularize xfs_attr_node_removename when we add delay
>> ready attributes later.
>>
> The changes look good to me,
> 
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
Alrighty, thank you!

Allison

> 
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 40 +++++++++++++++++++++++++++++++---------
>>   1 file changed, 31 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index f70b4f2..3c33dc5 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1193,6 +1193,35 @@ xfs_attr_leaf_mark_incomplete(
>>   }
>>   
>>   /*
>> + * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
>> + * the blocks are valid.  Any remote blocks will be marked incomplete.
>> + */
>> +STATIC
>> +int xfs_attr_node_removename_setup(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	**state)
>> +{
>> +	int			error;
>> +	struct xfs_da_state_blk	*blk;
>> +
>> +	error = xfs_attr_node_hasname(args, state);
>> +	if (error != -EEXIST)
>> +		return error;
>> +
>> +	blk = &(*state)->path.blk[(*state)->path.active - 1];
>> +	ASSERT(blk->bp != NULL);
>> +	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>> +
>> +	if (args->rmtblkno > 0) {
>> +		error = xfs_attr_leaf_mark_incomplete(args, *state);
>> +		if (error)
>> +			return error;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>>    * Remove a name from a B-tree attribute list.
>>    *
>>    * This will involve walking down the Btree, and may involve joining
>> @@ -1210,8 +1239,8 @@ xfs_attr_node_removename(
>>   
>>   	trace_xfs_attr_node_removename(args);
>>   
>> -	error = xfs_attr_node_hasname(args, &state);
>> -	if (error != -EEXIST)
>> +	error = xfs_attr_node_removename_setup(args, &state);
>> +	if (error)
>>   		goto out;
>>   
>>   	/*
>> @@ -1219,14 +1248,7 @@ xfs_attr_node_removename(
>>   	 * This is done before we remove the attribute so that we don't
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>> -	blk = &state->path.blk[ state->path.active-1 ];
>> -	ASSERT(blk->bp != NULL);
>> -	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_leaf_mark_incomplete(args, state);
>> -		if (error)
>> -			goto out;
>> -
>>   		error = xfs_attr_rmtval_remove(args);
>>   		if (error)
>>   			goto out;
>>
> 
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt
  2020-04-20  6:38   ` Chandan Rajendra
@ 2020-04-20 22:46     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-20 22:46 UTC (permalink / raw)
  To: Chandan Rajendra; +Cc: linux-xfs



On 4/19/20 11:38 PM, Chandan Rajendra wrote:
> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote:
>> This patch adds another new helper function
>> xfs_attr_node_removename_rmt. This will also help modularize
>> xfs_attr_node_removename when we add delay ready attributes later.
>>
> 
> The changes look logically correct.
> 
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
Thank you!
Allison

> 
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 32 +++++++++++++++++++++++---------
>>   1 file changed, 23 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 3c33dc5..d735570 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1221,6 +1221,28 @@ int xfs_attr_node_removename_setup(
>>   	return 0;
>>   }
>>   
>> +STATIC int
>> +xfs_attr_node_removename_rmt (
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	*state)
>> +{
>> +	int			error = 0;
>> +
>> +	error = xfs_attr_rmtval_remove(args);
>> +	if (error)
>> +		return error;
>> +
>> +	/*
>> +	 * Refill the state structure with buffers, the prior calls
>> +	 * released our buffers.
>> +	 */
>> +	error = xfs_attr_refillstate(state);
>> +	if (error)
>> +		return error;
>> +
>> +	return 0;
>> +}
>> +
>>   /*
>>    * Remove a name from a B-tree attribute list.
>>    *
>> @@ -1249,15 +1271,7 @@ xfs_attr_node_removename(
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_rmtval_remove(args);
>> -		if (error)
>> -			goto out;
>> -
>> -		/*
>> -		 * Refill the state structure with buffers, the prior calls
>> -		 * released our buffers.
>> -		 */
>> -		error = xfs_attr_refillstate(state);
>> +		error = xfs_attr_node_removename_rmt(args, state);
>>   		if (error)
>>   			goto out;
>>   	}
>>
> 
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 18/20] xfs: Add delay ready attr remove routines
  2020-04-20  9:06   ` Chandan Rajendra
@ 2020-04-20 23:26     ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-20 23:26 UTC (permalink / raw)
  To: Chandan Rajendra; +Cc: linux-xfs



On 4/20/20 2:06 AM, Chandan Rajendra wrote:
> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote:
>> This patch modifies the attr remove routines to be delay ready. This
>> means they no longer roll or commit transactions, but instead return
>> -EAGAIN to have the calling routine roll and refresh the transaction. In
>> this series, xfs_attr_remove_args has become xfs_attr_remove_iter, which
>> uses a sort of state machine like switch to keep track of where it was
>> when EAGAIN was returned. xfs_attr_node_removename has also been
>> modified to use the switch, and a new version of xfs_attr_remove_args
>> consists of a simple loop to refresh the transaction until the operation
>> is completed.
>>
>> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
>> counter parts: xfs_attr_rmtval_invalidate (appearing in the setup
>> helper) and then __xfs_attr_rmtval_remove. We will rename
>> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
>> done.
>>
>> This patch also adds a new struct xfs_delattr_context, which we will use
>> to keep track of the current state of an attribute operation. The new
>> xfs_delattr_state enum is used to track various operations that are in
>> progress so that we know not to repeat them, and resume where we left
>> off before EAGAIN was returned to cycle out the transaction. Other
>> members take the place of local variables that need to retain their
>> values across multiple function recalls.
>>
>> Below is a state machine diagram for attr remove operations. The
>> XFS_DAS_* states indicate places where the function would return
>> -EAGAIN, and then immediately resume from after being recalled by the
>> calling function.  States marked as a "subroutine state" indicate that
>> they belong to a subroutine, and so the calling function needs to pass
>> them back to that subroutine to allow it to finish where it left off.
>> But they otherwise do not have a role in the calling function other than
>> just passing through.
>>
>>   xfs_attr_remove_iter()
>>           XFS_DAS_RM_SHRINK     ─┐
>>           (subroutine state)     │
>>                                  │
>>           XFS_DAS_RMTVAL_REMOVE ─┤
>>           (subroutine state)     │
>>                                  └─>xfs_attr_node_removename()
>>                                                   │
>>                                                   v
>>                                           need to remove
>>                                     ┌─n──  rmt blocks?
>>                                     │             │
>>                                     │             y
>>                                     │             │
>>                                     │             v
>>                                     │  ┌─>XFS_DAS_RMTVAL_REMOVE
>>                                     │  │          │
>>                                     │  │          v
>>                                     │  └──y── more blks
>>                                     │         to remove?
>>                                     │             │
>>                                     │             n
>>                                     │             │
>>                                     │             v
>>                                     │         need to
>>                                     └─────> shrink tree? ─n─┐
>>                                                   │         │
>>                                                   y         │
>>                                                   │         │
>>                                                   v         │
>>                                           XFS_DAS_RM_SHRINK │
>>                                                   │         │
>>                                                   v         │
>>                                                  done <─────┘
>>
>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 168 ++++++++++++++++++++++++++++++++++++-----------
>>   fs/xfs/libxfs/xfs_attr.h |  38 +++++++++++
>>   2 files changed, 168 insertions(+), 38 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index d735570..f700976 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -45,7 +45,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
>>    */
>>   STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
>>   STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
>> -STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
>> +STATIC int xfs_attr_leaf_removename(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>   
>>   /*
>> @@ -53,12 +53,21 @@ STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
>>    */
>>   STATIC int xfs_attr_node_get(xfs_da_args_t *args);
>>   STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
>> -STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
>> +STATIC int xfs_attr_node_removename(struct xfs_delattr_context *dac);
>>   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>>   				 struct xfs_da_state **state);
>>   STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
>>   STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
>>   
>> +STATIC void
>> +xfs_delattr_context_init(
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_args		*args)
>> +{
>> +	memset(dac, 0, sizeof(struct xfs_delattr_context));
>> +	dac->da_args = args;
>> +}
>> +
>>   int
>>   xfs_inode_hasattr(
>>   	struct xfs_inode	*ip)
>> @@ -356,20 +365,66 @@ xfs_has_attr(
>>    */
>>   int
>>   xfs_attr_remove_args(
>> -	struct xfs_da_args      *args)
>> +	struct xfs_da_args	*args)
>>   {
>> +	int			error = 0;
>> +	struct			xfs_delattr_context dac;
>> +
>> +	xfs_delattr_context_init(&dac, args);
>> +
>> +	do {
>> +		error = xfs_attr_remove_iter(&dac);
>> +		if (error != -EAGAIN)
>> +			break;
>> +
>> +		if (dac.flags & XFS_DAC_DEFER_FINISH) {
>> +			dac.flags &= ~XFS_DAC_DEFER_FINISH;
>> +			error = xfs_defer_finish(&args->trans);
>> +			if (error)
>> +				break;
>> +		}
>> +
>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> +		if (error)
>> +			break;
>> +	} while (true);
>> +
>> +	return error;
>> +}
>> +
>> +/*
>> + * Remove the attribute specified in @args.
>> + *
>> + * This function may return -EAGAIN to signal that the transaction needs to be
>> + * rolled.  Callers should continue calling this function until they receive a
>> + * return value other than -EAGAIN.
>> + */
>> +int
>> +xfs_attr_remove_iter(
>> +	struct xfs_delattr_context *dac)
>> +{
>> +	struct xfs_da_args	*args = dac->da_args;
>>   	struct xfs_inode	*dp = args->dp;
>>   	int			error;
>>   
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_RM_SHRINK:
>> +	case XFS_DAS_RMTVAL_REMOVE:
>> +		return xfs_attr_node_removename(dac);
>> +	default:
>> +		break;
>> +	}
>> +
>>   	if (!xfs_inode_hasattr(dp)) {
>>   		error = -ENOATTR;
>>   	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
>>   		ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>>   		error = xfs_attr_shortform_remove(args);
>>   	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -		error = xfs_attr_leaf_removename(args);
>> +		error = xfs_attr_leaf_removename(dac);
>>   	} else {
>> -		error = xfs_attr_node_removename(args);
>> +		error = xfs_attr_node_removename(dac);
>>   	}
>>   
>>   	return error;
>> @@ -794,11 +849,12 @@ xfs_attr_leaf_hasname(
>>    */
>>   STATIC int
>>   xfs_attr_leaf_removename(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	struct xfs_inode	*dp;
>> -	struct xfs_buf		*bp;
>> -	int			error, forkoff;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp;
>> +	struct xfs_buf			*bp;
>> +	int				error, forkoff;
>>   
>>   	trace_xfs_attr_leaf_removename(args);
>>   
>> @@ -825,9 +881,8 @@ xfs_attr_leaf_removename(
>>   		/* bp is gone due to xfs_da_shrink_inode */
>>   		if (error)
>>   			return error;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> +
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	}
>>   	return 0;
>>   }
>> @@ -1128,12 +1183,13 @@ xfs_attr_node_addname(
>>    */
>>   STATIC int
>>   xfs_attr_node_shrink(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state     *state)
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_state		*state)
>>   {
>> -	struct xfs_inode	*dp = args->dp;
>> -	int			error, forkoff;
>> -	struct xfs_buf		*bp;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>> +	int				error, forkoff;
>> +	struct xfs_buf			*bp;
>>   
>>   	/*
>>   	 * Have to get rid of the copy of this dabuf in the state.
>> @@ -1153,9 +1209,7 @@ xfs_attr_node_shrink(
>>   		if (error)
>>   			return error;
>>   
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	} else
>>   		xfs_trans_brelse(args->trans, bp);
>>   
>> @@ -1194,13 +1248,15 @@ xfs_attr_leaf_mark_incomplete(
>>   
>>   /*
>>    * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
>> - * the blocks are valid.  Any remote blocks will be marked incomplete.
>> + * the blocks are valid.  Any remote blocks will be marked incomplete and
>> + * invalidated.
>>    */
>>   STATIC
>>   int xfs_attr_node_removename_setup(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state	**state)
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_state		**state)
>>   {
>> +	struct xfs_da_args	*args = dac->da_args;
>>   	int			error;
>>   	struct xfs_da_state_blk	*blk;
>>   
>> @@ -1212,10 +1268,21 @@ int xfs_attr_node_removename_setup(
>>   	ASSERT(blk->bp != NULL);
>>   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   
>> +	/*
>> +	 * Store blk and state in the context incase we need to cycle out the
>> +	 * transaction
>> +	 */
>> +	dac->blk = blk;
>> +	dac->da_state = *state;
>> +
>>   	if (args->rmtblkno > 0) {
>>   		error = xfs_attr_leaf_mark_incomplete(args, *state);
>>   		if (error)
>>   			return error;
>> +
>> +		error = xfs_attr_rmtval_invalidate(args);
>> +		if (error)
>> +			return error;
>>   	}
>>   
>>   	return 0;
>> @@ -1228,7 +1295,10 @@ xfs_attr_node_removename_rmt (
>>   {
>>   	int			error = 0;
>>   
>> -	error = xfs_attr_rmtval_remove(args);
>> +	/*
>> +	 * May return -EAGAIN to request that the caller recall this function
>> +	 */
>> +	error = __xfs_attr_rmtval_remove(args);
>>   	if (error)
>>   		return error;
>>   
>> @@ -1249,19 +1319,37 @@ xfs_attr_node_removename_rmt (
>>    * This will involve walking down the Btree, and may involve joining
>>    * leaf nodes and even joining intermediate nodes up to and including
>>    * the root node (a special case of an intermediate node).
>> + *
>> + * This routine is meant to function as either an inline or 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_node_removename(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> +	struct xfs_da_args	*args = dac->da_args;
>>   	struct xfs_da_state	*state;
>>   	struct xfs_da_state_blk	*blk;
>>   	int			retval, error;
>>   	struct xfs_inode	*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>> +	state = dac->da_state;
>> +	blk = dac->blk;
>> +
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_RMTVAL_REMOVE:
>> +		goto das_rmtval_remove;
>> +	case XFS_DAS_RM_SHRINK:
>> +		goto das_rm_shrink;
>> +	default:
>> +		break;
>> +	}
>>   
>> -	error = xfs_attr_node_removename_setup(args, &state);
>> +	error = xfs_attr_node_removename_setup(dac, &state);
>>   	if (error)
>>   		goto out;
>>   
>> @@ -1270,10 +1358,16 @@ xfs_attr_node_removename(
>>   	 * This is done before we remove the attribute so that we don't
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>> +
>> +das_rmtval_remove:
>> +
>>   	if (args->rmtblkno > 0) {
>>   		error = xfs_attr_node_removename_rmt(args, state);
>> -		if (error)
>> -			goto out;
>> +		if (error) {
>> +			if (error == -EAGAIN)
>> +				dac->dela_state = XFS_DAS_RMTVAL_REMOVE;
> 
> Shouldn't XFS_DAC_DEFER_FINISH be set in dac->flags?
> xfs_attr_node_removename_rmt() indirectly calls __xfs_bunmapi() which would
> have added items to the deferred list.
> 
Ok I see it.  I think what I'll do based on the over all v8 feed back 
is: make __xfs_attr_rmtval_remove a helper function of 
xfs_attr_rmtval_remove in patch 10.  I'll keep the xfs_defer_finish as 
part of that helper.  Then when we come to this patch, the 
xfs_defer_finish will turn into the flag set.  That should fix this I 
believe.  Thank for the review!

Allison

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 19/20] xfs: Add delay ready attr set routines
  2020-04-20 16:20     ` Brian Foster
@ 2020-04-20 23:26       ` Allison Collins
  0 siblings, 0 replies; 70+ messages in thread
From: Allison Collins @ 2020-04-20 23:26 UTC (permalink / raw)
  To: Brian Foster, Chandan Rajendra; +Cc: linux-xfs



On 4/20/20 9:20 AM, Brian Foster wrote:
> On Mon, Apr 20, 2020 at 05:15:08PM +0530, Chandan Rajendra wrote:
>> On Saturday, April 4, 2020 3:42 AM Allison Collins wrote:
> ...
>>> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
>>> ---
>>>   fs/xfs/libxfs/xfs_attr.c        | 384 +++++++++++++++++++++++++++-------------
>>>   fs/xfs/libxfs/xfs_attr.h        |  16 ++
>>>   fs/xfs/libxfs/xfs_attr_leaf.c   |   1 +
>>>   fs/xfs/libxfs/xfs_attr_remote.c | 111 +++++++-----
>>>   fs/xfs/libxfs/xfs_attr_remote.h |   4 +
>>>   fs/xfs/xfs_attr_inactive.c      |   1 +
>>>   fs/xfs/xfs_trace.h              |   1 -
>>>   7 files changed, 351 insertions(+), 167 deletions(-)
>>>
>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>> index f700976..c160b7a 100644
>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>>> @@ -765,22 +873,25 @@ xfs_attr_leaf_addname(
>>>   		error = xfs_attr3_leaf_flipflags(args);
>>>   		if (error)
>>>   			return error;
>>> -		/*
>>> -		 * Commit the flag value change and start the next trans in
>>> -		 * series.
>>> -		 */
>>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>>> -		if (error)
>>> -			return error;
>>> -
>>> +		dac->dela_state = XFS_DAS_FLIP_LFLAG;
>>> +		return -EAGAIN;
>>> +das_flip_flag:
>>>   		/*
>>>   		 * Dismantle the "old" attribute/value pair by removing
>>>   		 * a "remote" value (if it exists).
>>>   		 */
>>>   		xfs_attr_restore_rmt_blk(args);
>>>   
>>> +		xfs_attr_rmtval_invalidate(args);
>>> +das_rm_lblk:
>>>   		if (args->rmtblkno) {
>>> -			error = xfs_attr_rmtval_remove(args);
>>> +			error = __xfs_attr_rmtval_remove(args);
>>> +
>>> +			if (error == -EAGAIN) {
>>> +				dac->dela_state = XFS_DAS_RM_LBLK;
>>
>> Similar to what I had observed in the patch "Add delay ready attr remove
>> routines",
>>
>> Shouldn't XFS_DAC_DEFER_FINISH be set in dac->flags?
>> __xfs_attr_rmtval_remove() calls __xfs_bunmapi() which would
>> have added items to the deferred list.
>>
> 
> Just note that transaction rolls don't currently finish deferred ops. So
> from the perspective of preserving current behavior it might make sense
> to set the flag here if there was an explicit xfs_defer_finish() that's
> been factored out, but not so if it was just a transaction roll.
> 
> Brian
Yep, I think Chandan is right, xfs_attr_rmtval_remove used to have a 
xfs_defer_finish and __xfs_attr_rmtval_remove does not.  I think 
probably I'll keep the xfs_defer_finish in the __xfs_attr_rmtval_remove 
helper, and then when we get to this patch, turn it into the flag set. 
I believe that should be correct.

Allison
> 
>> -- 
>> chandan
>>
>>
>>
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove
  2020-04-03 22:12 ` [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove Allison Collins
  2020-04-07 14:16   ` Brian Foster
@ 2020-04-29 23:05   ` Darrick J. Wong
  2020-04-29 23:09     ` Darrick J. Wong
  1 sibling, 1 reply; 70+ messages in thread
From: Darrick J. Wong @ 2020-04-29 23:05 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Fri, Apr 03, 2020 at 03:12:19PM -0700, Allison Collins wrote:
> This function is similar to xfs_attr_rmtval_remove, but adapted to
> return EAGAIN for new transactions. We will use this later when we
> introduce delayed attributes.  This function will eventually replace
> xfs_attr_rmtval_remove
> 
> Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>

Seems reasonable, but oh my the transaction roll hoisting is complex...

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/libxfs/xfs_attr_remote.c | 25 +++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_attr_remote.h |  1 +
>  2 files changed, 26 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index 4d51969..fd4be9d 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -711,3 +711,28 @@ xfs_attr_rmtval_remove(
>  	}
>  	return 0;
>  }
> +
> +/*
> + * Remove the value associated with an attribute by deleting the out-of-line
> + * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
> + * transaction and recall the function
> + */
> +int
> +__xfs_attr_rmtval_remove(
> +	struct xfs_da_args	*args)
> +{
> +	int	error, done;
> +
> +	/*
> +	 * Unmap value blocks for this attr.
> +	 */
> +	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
> +			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
> +	if (error)
> +		return error;
> +
> +	if (!done)
> +		return -EAGAIN;
> +
> +	return 0;
> +}
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> index eff5f95..ee3337b 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.h
> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> @@ -14,4 +14,5 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args);
>  int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
>  		xfs_buf_flags_t incore_flags);
>  int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
> +int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
>  #endif /* __XFS_ATTR_REMOTE_H__ */
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove
  2020-04-29 23:05   ` Darrick J. Wong
@ 2020-04-29 23:09     ` Darrick J. Wong
  0 siblings, 0 replies; 70+ messages in thread
From: Darrick J. Wong @ 2020-04-29 23:09 UTC (permalink / raw)
  To: Allison Collins; +Cc: linux-xfs

On Wed, Apr 29, 2020 at 04:05:40PM -0700, Darrick J. Wong wrote:
> On Fri, Apr 03, 2020 at 03:12:19PM -0700, Allison Collins wrote:
> > This function is similar to xfs_attr_rmtval_remove, but adapted to
> > return EAGAIN for new transactions. We will use this later when we
> > introduce delayed attributes.  This function will eventually replace
> > xfs_attr_rmtval_remove
> > 
> > Signed-off-by: Allison Collins <allison.henderson@oracle.com>
> > Reviewed-by: Chandan Rajendra <chandanrlinux@gmail.com>
> 
> Seems reasonable, but oh my the transaction roll hoisting is complex...
> 
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

Err... whooops.  I meant to send this for patch 13.  Maybe I should just
wait for v9 to appear on the list...

--D

> --D
> 
> > ---
> >  fs/xfs/libxfs/xfs_attr_remote.c | 25 +++++++++++++++++++++++++
> >  fs/xfs/libxfs/xfs_attr_remote.h |  1 +
> >  2 files changed, 26 insertions(+)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > index 4d51969..fd4be9d 100644
> > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > @@ -711,3 +711,28 @@ xfs_attr_rmtval_remove(
> >  	}
> >  	return 0;
> >  }
> > +
> > +/*
> > + * Remove the value associated with an attribute by deleting the out-of-line
> > + * buffer that it is stored on. Returns EAGAIN for the caller to refresh the
> > + * transaction and recall the function
> > + */
> > +int
> > +__xfs_attr_rmtval_remove(
> > +	struct xfs_da_args	*args)
> > +{
> > +	int	error, done;
> > +
> > +	/*
> > +	 * Unmap value blocks for this attr.
> > +	 */
> > +	error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno,
> > +			    args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done);
> > +	if (error)
> > +		return error;
> > +
> > +	if (!done)
> > +		return -EAGAIN;
> > +
> > +	return 0;
> > +}
> > diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> > index eff5f95..ee3337b 100644
> > --- a/fs/xfs/libxfs/xfs_attr_remote.h
> > +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> > @@ -14,4 +14,5 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args);
> >  int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map,
> >  		xfs_buf_flags_t incore_flags);
> >  int xfs_attr_rmtval_invalidate(struct xfs_da_args *args);
> > +int __xfs_attr_rmtval_remove(struct xfs_da_args *args);
> >  #endif /* __XFS_ATTR_REMOTE_H__ */
> > -- 
> > 2.7.4
> > 

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2020-04-29 23:09 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-03 22:12 [PATCH v8 00/20] xfs: Delay Ready Attributes Allison Collins
2020-04-03 22:12 ` [PATCH v8 01/20] xfs: Add xfs_has_attr and subroutines Allison Collins
2020-04-06 14:31   ` Brian Foster
2020-04-06 22:09     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 02/20] xfs: Check for -ENOATTR or -EEXIST Allison Collins
2020-04-06 14:31   ` Brian Foster
2020-04-06 23:14     ` Allison Collins
2020-04-15  6:43   ` Chandan Rajendra
2020-04-03 22:12 ` [PATCH v8 03/20] xfs: Factor out new helper functions xfs_attr_rmtval_set Allison Collins
2020-04-03 22:12 ` [PATCH v8 04/20] xfs: Pull up trans handling in xfs_attr3_leaf_flipflags Allison Collins
2020-04-03 22:12 ` [PATCH v8 05/20] xfs: Split apart xfs_attr_leaf_addname Allison Collins
2020-04-03 22:12 ` [PATCH v8 06/20] xfs: Refactor xfs_attr_try_sf_addname Allison Collins
2020-04-03 22:12 ` [PATCH v8 07/20] xfs: Pull up trans roll from xfs_attr3_leaf_setflag Allison Collins
2020-04-03 22:12 ` [PATCH v8 08/20] xfs: Factor out xfs_attr_rmtval_invalidate Allison Collins
2020-04-03 22:12 ` [PATCH v8 09/20] xfs: Pull up trans roll in xfs_attr3_leaf_clearflag Allison Collins
2020-04-03 22:12 ` [PATCH v8 10/20] xfs: Add helper function __xfs_attr_rmtval_remove Allison Collins
2020-04-07 14:16   ` Brian Foster
2020-04-07 22:19     ` Allison Collins
2020-04-29 23:05   ` Darrick J. Wong
2020-04-29 23:09     ` Darrick J. Wong
2020-04-03 22:12 ` [PATCH v8 11/20] xfs: Add helper function xfs_attr_node_shrink Allison Collins
2020-04-07 14:17   ` Brian Foster
2020-04-07 21:52     ` Allison Collins
2020-04-15 10:16   ` Chandan Rajendra
2020-04-15 22:13     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 12/20] xfs: Removed unneeded xfs_trans_roll_inode calls Allison Collins
2020-04-07 14:17   ` Brian Foster
2020-04-07 21:53     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 13/20] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform Allison Collins
2020-04-07 15:23   ` Brian Foster
2020-04-07 21:53     ` Allison Collins
2020-04-10 16:55       ` Allison Collins
2020-04-11 12:57         ` Brian Foster
2020-04-20  5:30   ` Chandan Rajendra
2020-04-20 22:45     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 14/20] xfs: Add helper function xfs_attr_leaf_mark_incomplete Allison Collins
2020-04-07 15:23   ` Brian Foster
2020-04-07 21:53     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 15/20] xfs: Add remote block helper functions Allison Collins
2020-04-08 12:09   ` Brian Foster
2020-04-08 16:32     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 16/20] xfs: Add helper function xfs_attr_node_removename_setup Allison Collins
2020-04-08 12:09   ` Brian Foster
2020-04-08 16:32     ` Allison Collins
2020-04-20  6:25   ` Chandan Rajendra
2020-04-20 22:46     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 17/20] xfs: Add helper function xfs_attr_node_removename_rmt Allison Collins
2020-04-08 12:10   ` Brian Foster
2020-04-08 16:32     ` Allison Collins
2020-04-20  6:38   ` Chandan Rajendra
2020-04-20 22:46     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 18/20] xfs: Add delay ready attr remove routines Allison Collins
2020-04-13 12:30   ` Brian Foster
2020-04-14 21:35     ` Allison Collins
2020-04-15 11:46       ` Brian Foster
2020-04-16  3:17         ` Allison Collins
2020-04-16 10:58           ` Brian Foster
2020-04-16 22:41             ` Allison Collins
2020-04-20  9:06   ` Chandan Rajendra
2020-04-20 23:26     ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 19/20] xfs: Add delay ready attr set routines Allison Collins
2020-04-13 13:40   ` Brian Foster
2020-04-15 22:08     ` Allison Collins
2020-04-16 11:01       ` Brian Foster
2020-04-16 22:54         ` Allison Collins
2020-04-17 10:36           ` Brian Foster
2020-04-20 11:45   ` Chandan Rajendra
2020-04-20 16:20     ` Brian Foster
2020-04-20 23:26       ` Allison Collins
2020-04-03 22:12 ` [PATCH v8 20/20] xfs: Rename __xfs_attr_rmtval_remove Allison Collins

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.