linux-xfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v16 00/11] xfs: Delay Ready Attributes
@ 2021-03-26  0:32 Allison Henderson
  2021-03-26  0:32 ` [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d Allison Henderson
                   ` (11 more replies)
  0 siblings, 12 replies; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:32 UTC (permalink / raw)
  To: linux-xfs

Hi all,

This set is a subset of a larger series for Dealyed Attributes. Which is a
subset of a yet larger series for 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 a delayed
operation.  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 delayed attribute portion.
We will introduce parent pointers in a later set.

In this version I have reduced the set back to the "Delay Ready Attrs" sub series to
avoid reviewer burn out, but the extended series is available to view in the inlcuded
git hub links, which extend all the way through parent pointers.  Feel free to review
as much as feels reasonable.  The set as a whole is a bit much to digest at once, so
working through it in progressive subsets seems like a reasonable way to manage its
dev efforts.

Lastly, in the last revision folks asked for some stress testing on the set.  On my
system, I found that in an fsstress test with all patches applied, we spend at most
%0.17 of the time in the attr routines, compared to at most %0.12 with out the set applied.
Both can fluctuate quite a bit depending on the other operations going on that seem to
occupy most of the activity.  For the most part though, I do not find these results to be
particularly concerning.  Though folks are certainly welcome to try it out on their own 
system to see how the results might differ.

Updates since v15: Mostly just review feed back from the previous revision.  I've
tracked changes below to help reviews recall the changes discussed

xfs: Reverse apply 72b97ea40d
  NEW

xfs: Add helper xfs_attr_node_remove_step
  DROPPED

xfs: Add xfs_attr_node_remove_cleanup
  No change

xfs: Hoist transaction handling in xfs_attr_node_remove_step
  DROPPED

xfs: Hoist xfs_attr_set_shortform
  No change

xfs: Add helper xfs_attr_set_fmt
  Fixed helper to return error when defer_finish fails

xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  Renamed xfs_attr_node_addname_work to xfs_attr_node_addname_clear_incomplete

xfs: Add helper xfs_attr_node_addname_find_attr
  Renamed goto out, to goto error

xfs: Hoist xfs_attr_node_addname
  Removed unused retval variable
  Removed extra state free in xfs_attr_node_addname

xfs: Hoist xfs_attr_leaf_addname
  Fixed spelling typos

xfs: Hoist node transaction handling
  Added consistent braces to if/else statement

xfs: Add delay ready attr remove routines
  Typo fixes
  Merged xfs_attr_remove_iter with xfs_attr_node_removename_iter
  Added state XFS_DAS_RMTBLK
  Flow chart updated

xfs: Add delay ready attr set routines
  Rebase adjustments
  Typo fixes


Extended Series Changes
------------------------
xfs: Add state machine tracepoints
  Rebase adjustments
  xfs_attr_node_remove_rmt_return removed to match earlier refactoring changes
  trace_xfs_attr_node_removename_iter_return becomes
  trace_xfs_attr_remove_iter_return to match earlier refactoring changes

xfs: Rename __xfs_attr_rmtval_remove
  No change

xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
  Added kmem_alloc_large fall back
 
xfs: Set up infrastructure for deferred attribute operations
  Typo fixes
  Rename xfs_trans_attr to xfs_trans_attr_finish_update
  Added helper function xfs_attri_validate
  Split patch into infrastructure and implementation patches
  Added XFS_ERROR_REPORT in xlog_recover_attri_commit_pass2:

xfs: Implement for deferred attribute operations
  NEW

xfs: Skip flip flags for delayed attrs
  Did a performance analysis

xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
  Typo fixes

xfs: Remove unused xfs_attr_*_args
  Rebase adjustments

xfs: Add delayed attributes error tag
  Added errortag include

xfs: Merge xfs_delattr_context into xfs_attr_item
  Typo fixes


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

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

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

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_v16
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v16_extended

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

To run as delayed attributes run:
export MOUNT_OPTIONS="-o 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 Henderson (11):
  xfs: Reverse apply 72b97ea40d
  xfs: Add xfs_attr_node_remove_cleanup
  xfs: Hoist xfs_attr_set_shortform
  xfs: Add helper xfs_attr_set_fmt
  xfs: Separate xfs_attr_node_addname and
    xfs_attr_node_addname_clear_incomplete
  xfs: Add helper xfs_attr_node_addname_find_attr
  xfs: Hoist xfs_attr_node_addname
  xfs: Hoist xfs_attr_leaf_addname
  xfs: Hoist node transaction handling
  xfs: Add delay ready attr remove routines
  xfs: Add delay ready attr set routines

 fs/xfs/libxfs/xfs_attr.c        | 903 ++++++++++++++++++++++++----------------
 fs/xfs/libxfs/xfs_attr.h        | 364 ++++++++++++++++
 fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
 fs/xfs/libxfs/xfs_attr_remote.c | 126 ++++--
 fs/xfs/libxfs/xfs_attr_remote.h |   7 +-
 fs/xfs/xfs_attr_inactive.c      |   2 +-
 fs/xfs/xfs_trace.h              |   1 -
 7 files changed, 998 insertions(+), 407 deletions(-)

-- 
2.7.4


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

* [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
@ 2021-03-26  0:32 ` Allison Henderson
  2021-03-29  8:44   ` Chandan Babu R
  2021-04-01 15:43   ` Brian Foster
  2021-03-26  0:32 ` [PATCH v16 02/11] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:32 UTC (permalink / raw)
  To: linux-xfs

Originally we added this patch to help modularize the attr code in
preparation for delayed attributes and the state machine it requires.
However, later reviews found that this slightly alters the transaction
handling as the helper function is ambiguous as to whether the
transaction is diry or clean.  This may cause a dirty transaction to be
included in the next roll, where previously it had not.  To preserve the
existing code flow, we reverse apply this commit.

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

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 472b303..b42144e 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1202,24 +1202,6 @@ int xfs_attr_node_removename_setup(
 	return 0;
 }
 
-STATIC int
-xfs_attr_node_remove_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.
-	 */
-	return xfs_attr_refillstate(state);
-}
-
 /*
  * Remove a name from a B-tree attribute list.
  *
@@ -1248,7 +1230,15 @@ xfs_attr_node_removename(
 	 * overflow the maximum size of a transaction and/or hit a deadlock.
 	 */
 	if (args->rmtblkno > 0) {
-		error = xfs_attr_node_remove_rmt(args, state);
+		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);
 		if (error)
 			goto out;
 	}
-- 
2.7.4


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

* [PATCH v16 02/11] xfs: Add xfs_attr_node_remove_cleanup
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
  2021-03-26  0:32 ` [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d Allison Henderson
@ 2021-03-26  0:32 ` Allison Henderson
  2021-03-26  0:33 ` [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform Allison Henderson
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:32 UTC (permalink / raw)
  To: linux-xfs

This patch pulls a new helper function xfs_attr_node_remove_cleanup out
of xfs_attr_node_remove_step.  This helps to modularize
xfs_attr_node_remove_step which will help make the delayed attribute
code easier to follow

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Brian Foster <bfoster@redhat.com>
---
 fs/xfs/libxfs/xfs_attr.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index b42144e..32c7447 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1202,6 +1202,25 @@ int xfs_attr_node_removename_setup(
 	return 0;
 }
 
+STATIC int
+xfs_attr_node_remove_cleanup(
+	struct xfs_da_args	*args,
+	struct xfs_da_state	*state)
+{
+	struct xfs_da_state_blk	*blk;
+	int			retval;
+
+	/*
+	 * Remove the name and update the hashvals in the tree.
+	 */
+	blk = &state->path.blk[state->path.active-1];
+	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+	retval = xfs_attr3_leaf_remove(blk->bp, args);
+	xfs_da3_fixhashpath(state, &state->path);
+
+	return retval;
+}
+
 /*
  * Remove a name from a B-tree attribute list.
  *
@@ -1214,7 +1233,6 @@ xfs_attr_node_removename(
 	struct xfs_da_args	*args)
 {
 	struct xfs_da_state	*state;
-	struct xfs_da_state_blk	*blk;
 	int			retval, error;
 	struct xfs_inode	*dp = args->dp;
 
@@ -1242,14 +1260,7 @@ xfs_attr_node_removename(
 		if (error)
 			goto out;
 	}
-
-	/*
-	 * Remove the name and update the hashvals in the tree.
-	 */
-	blk = &state->path.blk[ state->path.active-1 ];
-	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
-	retval = xfs_attr3_leaf_remove(blk->bp, args);
-	xfs_da3_fixhashpath(state, &state->path);
+	retval = xfs_attr_node_remove_cleanup(args, state);
 
 	/*
 	 * Check to see if the tree needs to be collapsed.
-- 
2.7.4


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

* [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
  2021-03-26  0:32 ` [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d Allison Henderson
  2021-03-26  0:32 ` [PATCH v16 02/11] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-03-29  9:21   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt Allison Henderson
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch hoists xfs_attr_set_shortform into the calling function. This
will help keep all state management code in the same scope.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_attr.c | 81 ++++++++++++++++--------------------------------
 1 file changed, 27 insertions(+), 54 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 32c7447..5216f67 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -217,53 +217,6 @@ xfs_attr_is_shortform(
 }
 
 /*
- * Attempts to set an attr in shortform, or converts short form 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
@@ -272,7 +225,7 @@ xfs_attr_set_args(
 {
 	struct xfs_inode	*dp = args->dp;
 	struct xfs_buf          *leaf_bp = NULL;
-	int			error = 0;
+	int			error2, error = 0;
 
 	/*
 	 * If the attribute list is already in leaf format, jump straight to
@@ -281,16 +234,36 @@ xfs_attr_set_args(
 	 * again.
 	 */
 	if (xfs_attr_is_shortform(dp)) {
+		/*
+		 * 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 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.
+		 * 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.
 		 */
-		error = xfs_attr_set_shortform(args, &leaf_bp);
-		if (error || !args->trans)
+		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;
+		}
 	}
 
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-- 
2.7.4


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

* [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (2 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-03-29  9:37   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete Allison Henderson
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch adds a helper function xfs_attr_set_fmt.  This will help
isolate the code that will require state management from the portions
that do not.  xfs_attr_set_fmt returns 0 when the attr has been set and
no further action is needed.  It returns -EAGAIN when shortform has been
transformed to leaf, and the calling function should proceed the set the
attr in leaf form.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>

---
 fs/xfs/libxfs/xfs_attr.c | 79 ++++++++++++++++++++++++++++--------------------
 1 file changed, 46 insertions(+), 33 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 5216f67..d46324a 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -216,6 +216,48 @@ xfs_attr_is_shortform(
 		ip->i_afp->if_nextents == 0);
 }
 
+STATIC int
+xfs_attr_set_fmt(
+	struct xfs_da_args	*args)
+{
+	struct xfs_buf          *leaf_bp = NULL;
+	struct xfs_inode	*dp = args->dp;
+	int			error2, error = 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.
+	 */
+	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 -EAGAIN;
+}
+
 /*
  * Set the attribute specified in @args.
  */
@@ -224,8 +266,7 @@ xfs_attr_set_args(
 	struct xfs_da_args	*args)
 {
 	struct xfs_inode	*dp = args->dp;
-	struct xfs_buf          *leaf_bp = NULL;
-	int			error2, error = 0;
+	int			error;
 
 	/*
 	 * If the attribute list is already in leaf format, jump straight to
@@ -234,36 +275,9 @@ xfs_attr_set_args(
 	 * again.
 	 */
 	if (xfs_attr_is_shortform(dp)) {
-		/*
-		 * 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.
-		 */
-		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_fmt(args);
+		if (error != -EAGAIN)
 			return error;
-		}
 	}
 
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
@@ -297,8 +311,7 @@ xfs_attr_set_args(
 			return error;
 	}
 
-	error = xfs_attr_node_addname(args);
-	return error;
+	return xfs_attr_node_addname(args);
 }
 
 /*
-- 
2.7.4


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

* [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (3 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-03-29 14:42   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch separate xfs_attr_node_addname into two functions.  This will
help to make it easier to hoist parts of xfs_attr_node_addname that need
state management

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
---
 fs/xfs/libxfs/xfs_attr.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index d46324a..531ff56 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -54,6 +54,7 @@ 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_addname_clear_incomplete(struct xfs_da_args *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);
@@ -1061,6 +1062,25 @@ xfs_attr_node_addname(
 			return error;
 	}
 
+	error = xfs_attr_node_addname_clear_incomplete(args);
+out:
+	if (state)
+		xfs_da_state_free(state);
+	if (error)
+		return error;
+	return retval;
+}
+
+
+STATIC
+int xfs_attr_node_addname_clear_incomplete(
+	struct xfs_da_args		*args)
+{
+	struct xfs_da_state		*state = NULL;
+	struct xfs_da_state_blk		*blk;
+	int				retval = 0;
+	int				error = 0;
+
 	/*
 	 * Re-find the "old" attribute entry after any split ops. The INCOMPLETE
 	 * flag means that we will find the "old" attr, not the "new" one.
-- 
2.7.4


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

* [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (4 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-04-02  4:05   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname Allison Henderson
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch separates the first half of xfs_attr_node_addname into a
helper function xfs_attr_node_addname_find_attr.  It also replaces the
restart goto with with an EAGAIN return code driven by a loop in the
calling function.  This looks odd now, but will clean up nicly once we
introduce the state machine.  It will also enable hoisting the last
state out of xfs_attr_node_addname with out having to plumb in a "done"
parameter to know if we need to move to the next state or not.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_attr.c | 86 ++++++++++++++++++++++++++++++------------------
 1 file changed, 54 insertions(+), 32 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 531ff56..16159f6 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -52,7 +52,10 @@ 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_da_args *args,
+				 struct xfs_da_state *state);
+STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
+				 struct xfs_da_state **state);
 STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
 STATIC int xfs_attr_node_addname_clear_incomplete(struct xfs_da_args *args);
 STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
@@ -267,6 +270,7 @@ xfs_attr_set_args(
 	struct xfs_da_args	*args)
 {
 	struct xfs_inode	*dp = args->dp;
+	struct xfs_da_state     *state;
 	int			error;
 
 	/*
@@ -312,7 +316,14 @@ xfs_attr_set_args(
 			return error;
 	}
 
-	return xfs_attr_node_addname(args);
+	do {
+		error = xfs_attr_node_addname_find_attr(args, &state);
+		if (error)
+			return error;
+		error = xfs_attr_node_addname(args, state);
+	} while (error == -EAGAIN);
+
+	return error;
 }
 
 /*
@@ -885,47 +896,26 @@ xfs_attr_node_hasname(
  * External routines when attribute list size > geo->blksize
  *========================================================================*/
 
-/*
- * Add a name to a Btree-format attribute list.
- *
- * This will involve walking down the Btree, and may involve splitting
- * leaf nodes and even splitting intermediate nodes up to and including
- * the root node (a special case of an intermediate node).
- *
- * "Remote" attribute values confuse the issue and atomic rename operations
- * add a whole extra layer of confusion on top of that.
- */
 STATIC int
-xfs_attr_node_addname(
-	struct xfs_da_args	*args)
+xfs_attr_node_addname_find_attr(
+	struct xfs_da_args	*args,
+	struct xfs_da_state     **state)
 {
-	struct xfs_da_state	*state;
-	struct xfs_da_state_blk	*blk;
-	struct xfs_inode	*dp;
-	int			retval, error;
-
-	trace_xfs_attr_node_addname(args);
+	int			retval;
 
 	/*
-	 * Fill in bucket of arguments/results/context to carry around.
-	 */
-	dp = args->dp;
-restart:
-	/*
 	 * Search to see if name already exists, and get back a pointer
 	 * to where it should go.
 	 */
-	retval = xfs_attr_node_hasname(args, &state);
+	retval = xfs_attr_node_hasname(args, state);
 	if (retval != -ENOATTR && retval != -EEXIST)
-		goto out;
+		goto error;
 
-	blk = &state->path.blk[ state->path.active-1 ];
-	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
-		goto out;
+		goto error;
 	if (retval == -EEXIST) {
 		if (args->attr_flags & XATTR_CREATE)
-			goto out;
+			goto error;
 
 		trace_xfs_attr_node_replace(args);
 
@@ -943,6 +933,38 @@ xfs_attr_node_addname(
 		args->rmtvaluelen = 0;
 	}
 
+	return 0;
+error:
+	if (*state)
+		xfs_da_state_free(*state);
+	return retval;
+}
+
+/*
+ * Add a name to a Btree-format attribute list.
+ *
+ * This will involve walking down the Btree, and may involve splitting
+ * leaf nodes and even splitting intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ *
+ * "Remote" attribute values confuse the issue and atomic rename operations
+ * add a whole extra layer of confusion on top of that.
+ */
+STATIC int
+xfs_attr_node_addname(
+	struct xfs_da_args	*args,
+	struct xfs_da_state	*state)
+{
+	struct xfs_da_state_blk	*blk;
+	struct xfs_inode	*dp;
+	int			retval, error;
+
+	trace_xfs_attr_node_addname(args);
+
+	dp = args->dp;
+	blk = &state->path.blk[state->path.active-1];
+	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+
 	retval = xfs_attr3_leaf_add(blk->bp, state->args);
 	if (retval == -ENOSPC) {
 		if (state->path.active == 1) {
@@ -968,7 +990,7 @@ xfs_attr_node_addname(
 			if (error)
 				goto out;
 
-			goto restart;
+			return -EAGAIN;
 		}
 
 		/*
-- 
2.7.4


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

* [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (5 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-04-02  4:26   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch hoists the later half of xfs_attr_node_addname into
the calling function.  We do this because it is this area that
will need the most state management, and we want to keep such
code in the same scope as much as possible

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_attr.c | 161 +++++++++++++++++++++++------------------------
 1 file changed, 78 insertions(+), 83 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 16159f6..5b5410f 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -52,6 +52,7 @@ 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 void xfs_attr_restore_rmt_blk(struct xfs_da_args *args);
 STATIC int xfs_attr_node_addname(struct xfs_da_args *args,
 				 struct xfs_da_state *state);
 STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
@@ -270,8 +271,8 @@ xfs_attr_set_args(
 	struct xfs_da_args	*args)
 {
 	struct xfs_inode	*dp = args->dp;
-	struct xfs_da_state     *state;
-	int			error;
+	struct xfs_da_state     *state = NULL;
+	int			error = 0;
 
 	/*
 	 * If the attribute list is already in leaf format, jump straight to
@@ -322,8 +323,79 @@ xfs_attr_set_args(
 			return error;
 		error = xfs_attr_node_addname(args, state);
 	} while (error == -EAGAIN);
+	if (error)
+		return error;
+
+	/*
+	 * 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;
+
+	/*
+	 * If there was an out-of-line value, allocate the blocks we
+	 * identified for its storage and copy the value.  This is done
+	 * after we create 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_rmtval_set(args);
+		if (error)
+			return error;
+	}
+
+	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
+		/*
+		 * Added a "remote" value, just clear the incomplete flag.
+		 */
+		if (args->rmtblkno > 0)
+			error = xfs_attr3_leaf_clearflag(args);
+		goto out;
+	}
+
+	/*
+	 * If this is an atomic rename operation, we must "flip" the incomplete
+	 * flags on the "new" and "old" attribute/value pairs so that one
+	 * disappears and one appears atomically.  Then we must remove the "old"
+	 * attribute/value pair.
+	 *
+	 * In a separate transaction, set the incomplete flag on the "old" attr
+	 * and clear the incomplete flag on the "new" attr.
+	 */
+	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 a "remote" value
+	 * (if it exists).
+	 */
+	xfs_attr_restore_rmt_blk(args);
+
+	if (args->rmtblkno) {
+		error = xfs_attr_rmtval_invalidate(args);
+		if (error)
+			return error;
+
+		error = xfs_attr_rmtval_remove(args);
+		if (error)
+			return error;
+	}
+
+	error = xfs_attr_node_addname_clear_incomplete(args);
+out:
+	if (state)
+		xfs_da_state_free(state);
 	return error;
+
 }
 
 /*
@@ -957,7 +1029,7 @@ xfs_attr_node_addname(
 {
 	struct xfs_da_state_blk	*blk;
 	struct xfs_inode	*dp;
-	int			retval, error;
+	int			error;
 
 	trace_xfs_attr_node_addname(args);
 
@@ -965,8 +1037,8 @@ xfs_attr_node_addname(
 	blk = &state->path.blk[state->path.active-1];
 	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 
-	retval = xfs_attr3_leaf_add(blk->bp, state->args);
-	if (retval == -ENOSPC) {
+	error = xfs_attr3_leaf_add(blk->bp, state->args);
+	if (error == -ENOSPC) {
 		if (state->path.active == 1) {
 			/*
 			 * Its really a single leaf node, but it had
@@ -1012,85 +1084,8 @@ xfs_attr_node_addname(
 		xfs_da3_fixhashpath(state, &state->path);
 	}
 
-	/*
-	 * Kill the state structure, we're done with it and need to
-	 * allow the buffers to come back later.
-	 */
-	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;
-
-	/*
-	 * If there was an out-of-line value, allocate the blocks we
-	 * identified for its storage and copy the value.  This is done
-	 * after we create 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_rmtval_set(args);
-		if (error)
-			return error;
-	}
-
-	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
-		/*
-		 * Added a "remote" value, just clear the incomplete flag.
-		 */
-		if (args->rmtblkno > 0)
-			error = xfs_attr3_leaf_clearflag(args);
-		retval = error;
-		goto out;
-	}
-
-	/*
-	 * If this is an atomic rename operation, we must "flip" the incomplete
-	 * flags on the "new" and "old" attribute/value pairs so that one
-	 * disappears and one appears atomically.  Then we must remove the "old"
-	 * attribute/value pair.
-	 *
-	 * In a separate transaction, set the incomplete flag on the "old" attr
-	 * and clear the incomplete flag on the "new" attr.
-	 */
-	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 a "remote" value
-	 * (if it exists).
-	 */
-	xfs_attr_restore_rmt_blk(args);
-
-	if (args->rmtblkno) {
-		error = xfs_attr_rmtval_invalidate(args);
-		if (error)
-			return error;
-
-		error = xfs_attr_rmtval_remove(args);
-		if (error)
-			return error;
-	}
-
-	error = xfs_attr_node_addname_clear_incomplete(args);
 out:
-	if (state)
-		xfs_da_state_free(state);
-	if (error)
-		return error;
-	return retval;
+	return error;
 }
 
 
-- 
2.7.4


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

* [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (6 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-04-01 15:43   ` Brian Foster
  2021-04-02  4:40   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 09/11] xfs: Hoist node transaction handling Allison Henderson
                   ` (3 subsequent siblings)
  11 siblings, 2 replies; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch hoists xfs_attr_leaf_addname into the calling function.  The
goal being to get all the code that will require state management into
the same scope. This isn't particuarly aesthetic right away, but it is a
preliminary step to merging in the state machine code.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_attr.c | 209 ++++++++++++++++++++++-------------------------
 1 file changed, 96 insertions(+), 113 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 5b5410f..16f10ac 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -44,9 +44,9 @@ 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_removename(xfs_da_args_t *args);
 STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
+STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args, struct xfs_buf *bp);
 
 /*
  * Internal routines when attribute list is more than one block.
@@ -271,8 +271,9 @@ xfs_attr_set_args(
 	struct xfs_da_args	*args)
 {
 	struct xfs_inode	*dp = args->dp;
+	struct xfs_buf		*bp = NULL;
 	struct xfs_da_state     *state = NULL;
-	int			error = 0;
+	int			forkoff, error = 0;
 
 	/*
 	 * If the attribute list is already in leaf format, jump straight to
@@ -287,10 +288,101 @@ xfs_attr_set_args(
 	}
 
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-		error = xfs_attr_leaf_addname(args);
-		if (error != -ENOSPC)
+		error = xfs_attr_leaf_try_add(args, bp);
+		if (error == -ENOSPC)
+			goto node;
+		else if (error)
+			return error;
+
+		/*
+		 * 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;
+
+		/*
+		 * If there was an out-of-line value, allocate the blocks we
+		 * identified for its storage and copy the value.  This is done
+		 * after we create 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_rmtval_set(args);
+			if (error)
+				return error;
+		}
+
+		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
+			/*
+			 * Added a "remote" value, just clear the incomplete
+			 *flag.
+			 */
+			if (args->rmtblkno > 0)
+				error = xfs_attr3_leaf_clearflag(args);
+
+			return error;
+		}
+
+		/*
+		 * If this is an atomic rename operation, we must "flip" the
+		 * incomplete flags on the "new" and "old" attribute/value pairs
+		 * so that one disappears and one appears atomically.  Then we
+		 * must remove the "old" attribute/value pair.
+		 *
+		 * In a separate transaction, set the incomplete flag on the
+		 * "old" attr and clear the incomplete flag on the "new" attr.
+		 */
+
+		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 a
+		 * "remote" value (if it exists).
+		 */
+		xfs_attr_restore_rmt_blk(args);
+
+		if (args->rmtblkno) {
+			error = xfs_attr_rmtval_invalidate(args);
+			if (error)
+				return error;
+
+			error = xfs_attr_rmtval_remove(args);
+			if (error)
+				return error;
+		}
+
+		/*
+		 * Read in the block containing the "old" attr, then remove the
+		 * "old" attr from that block (neat, huh!)
+		 */
+		error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
+					   &bp);
+		if (error)
 			return error;
 
+		xfs_attr3_leaf_remove(bp, args);
+
+		/*
+		 * If the result is small enough, shrink it all into the inode.
+		 */
+		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 */
+
+		return error;
+node:
 		/*
 		 * Promote the attribute list to the Btree format.
 		 */
@@ -729,115 +821,6 @@ xfs_attr_leaf_try_add(
 	return retval;
 }
 
-
-/*
- * 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
-	 * later routines can manage their own transactions.
-	 */
-	error = xfs_trans_roll_inode(&args->trans, dp);
-	if (error)
-		return error;
-
-	/*
-	 * If there was an out-of-line value, allocate the blocks we
-	 * identified for its storage and copy the value.  This is done
-	 * after we create 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_rmtval_set(args);
-		if (error)
-			return error;
-	}
-
-	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
-		/*
-		 * Added a "remote" value, just clear the incomplete flag.
-		 */
-		if (args->rmtblkno > 0)
-			error = xfs_attr3_leaf_clearflag(args);
-
-		return error;
-	}
-
-	/*
-	 * If this is an atomic rename operation, we must "flip" the incomplete
-	 * flags on the "new" and "old" attribute/value pairs so that one
-	 * disappears and one appears atomically.  Then we must remove the "old"
-	 * attribute/value pair.
-	 *
-	 * In a separate transaction, set the incomplete flag on the "old" attr
-	 * and clear the incomplete flag on the "new" attr.
-	 */
-
-	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 a "remote" value
-	 * (if it exists).
-	 */
-	xfs_attr_restore_rmt_blk(args);
-
-	if (args->rmtblkno) {
-		error = xfs_attr_rmtval_invalidate(args);
-		if (error)
-			return error;
-
-		error = xfs_attr_rmtval_remove(args);
-		if (error)
-			return error;
-	}
-
-	/*
-	 * Read in the block containing the "old" attr, then remove the "old"
-	 * attr from that block (neat, huh!)
-	 */
-	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
-				   &bp);
-	if (error)
-		return error;
-
-	xfs_attr3_leaf_remove(bp, args);
-
-	/*
-	 * If the result is small enough, shrink it all into the inode.
-	 */
-	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 */
-
-	return error;
-}
-
 /*
  * Return EEXIST if attr is found, or ENOATTR if not
  */
-- 
2.7.4


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

* [PATCH v16 09/11] xfs: Hoist node transaction handling
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (7 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-04-02  5:04   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 10/11] xfs: Add delay ready attr remove routines Allison Henderson
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

This patch basically hoists the node transaction handling around the
leaf code we just hoisted.  This will helps setup this area for the
state machine since the goto is easily replaced with a state since it
ends with a transaction roll.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_attr.c | 56 ++++++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 26 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 16f10ac..41accd5 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -289,10 +289,37 @@ xfs_attr_set_args(
 
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
 		error = xfs_attr_leaf_try_add(args, bp);
-		if (error == -ENOSPC)
+		if (error == -ENOSPC) {
+			/*
+			 * Promote the attribute list to the Btree format.
+			 */
+			error = xfs_attr3_leaf_to_node(args);
+			if (error)
+				return error;
+
+			/*
+			 * Finish any deferred work items and roll the transaction once
+			 * more.  The goal here is to call node_addname with the inode
+			 * and transaction in the same state (inode locked and joined,
+			 * transaction clean) no matter how we got to this step.
+			 */
+			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;
+
 			goto node;
-		else if (error)
+		}
+		else if (error) {
 			return error;
+		}
 
 		/*
 		 * Commit the transaction that added the attr name so that
@@ -382,32 +409,9 @@ xfs_attr_set_args(
 			/* bp is gone due to xfs_da_shrink_inode */
 
 		return error;
+	}
 node:
-		/*
-		 * Promote the attribute list to the Btree format.
-		 */
-		error = xfs_attr3_leaf_to_node(args);
-		if (error)
-			return error;
-
-		/*
-		 * Finish any deferred work items and roll the transaction once
-		 * more.  The goal here is to call node_addname with the inode
-		 * and transaction in the same state (inode locked and joined,
-		 * transaction clean) no matter how we got to this step.
-		 */
-		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;
-	}
 
 	do {
 		error = xfs_attr_node_addname_find_attr(args, &state);
-- 
2.7.4


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

* [PATCH v16 10/11] xfs: Add delay ready attr remove routines
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (8 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 09/11] xfs: Hoist node transaction handling Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-04-01 16:55   ` Brian Foster
  2021-04-02  7:59   ` Chandan Babu R
  2021-03-26  0:33 ` [PATCH v16 11/11] xfs: Add delay ready attr set routines Allison Henderson
  2021-03-29 21:59 ` [PATCH v16 00/11] xfs: Delay Ready Attributes Darrick J. Wong
  11 siblings, 2 replies; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 18822 bytes --]

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 is merged with
xfs_attr_node_removename become a new function, xfs_attr_remove_iter.
This new version uses a sort of state machine like switch to keep track
of where it was when EAGAIN was returned. A new version of
xfs_attr_remove_args consists of a simple loop to refresh the
transaction until the operation is completed. A new XFS_DAC_DEFER_FINISH
flag is used to finish the transaction where ever the existing code used
to.

Calls to xfs_attr_rmtval_remove are replaced with the delay ready
version __xfs_attr_rmtval_remove. We will rename
__xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
done.

xfs_attr_rmtval_remove itself is still in use by the set routines (used
during a rename).  For reasons of preserving existing function, we
modify xfs_attr_rmtval_remove to call xfs_defer_finish when the flag is
set.  Similar to how xfs_attr_remove_args does here.  Once we transition
the set routines to be delay ready, xfs_attr_rmtval_remove is no longer
used and will be removed.

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.  See xfs_attr.h for a more
detailed diagram of the states.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c        | 206 +++++++++++++++++++++++++++-------------
 fs/xfs/libxfs/xfs_attr.h        | 125 ++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
 fs/xfs/libxfs/xfs_attr_remote.c |  48 ++++++----
 fs/xfs/libxfs/xfs_attr_remote.h |   2 +-
 fs/xfs/xfs_attr_inactive.c      |   2 +-
 6 files changed, 297 insertions(+), 88 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 41accd5..4a73691 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -57,7 +57,6 @@ STATIC int xfs_attr_node_addname(struct xfs_da_args *args,
 				 struct xfs_da_state *state);
 STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
 				 struct xfs_da_state **state);
-STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
 STATIC int xfs_attr_node_addname_clear_incomplete(struct xfs_da_args *args);
 STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
 				 struct xfs_da_state **state);
@@ -221,6 +220,32 @@ xfs_attr_is_shortform(
 		ip->i_afp->if_nextents == 0);
 }
 
+/*
+ * Checks to see if a delayed attribute transaction should be rolled.  If so,
+ * also checks for a defer finish.  Transaction is finished and rolled as
+ * needed, and returns true of false if the delayed operation should continue.
+ */
+int
+xfs_attr_trans_roll(
+	struct xfs_delattr_context	*dac)
+{
+	struct xfs_da_args		*args = dac->da_args;
+	int				error;
+
+	if (dac->flags & XFS_DAC_DEFER_FINISH) {
+		/*
+		 * The caller wants us to finish all the deferred ops so that we
+		 * avoid pinning the log tail with a large number of deferred
+		 * ops.
+		 */
+		dac->flags &= ~XFS_DAC_DEFER_FINISH;
+		error = xfs_defer_finish(&args->trans);
+	} else
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+
+	return error;
+}
+
 STATIC int
 xfs_attr_set_fmt(
 	struct xfs_da_args	*args)
@@ -530,21 +555,23 @@ xfs_has_attr(
  */
 int
 xfs_attr_remove_args(
-	struct xfs_da_args      *args)
+	struct xfs_da_args	*args)
 {
-	struct xfs_inode	*dp = args->dp;
-	int			error;
+	int				error;
+	struct xfs_delattr_context	dac = {
+		.da_args	= args,
+	};
 
-	if (!xfs_inode_hasattr(dp)) {
-		error = -ENOATTR;
-	} else if (dp->i_afp->if_format == 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);
-	} else {
-		error = xfs_attr_node_removename(args);
-	}
+	do {
+		error = xfs_attr_remove_iter(&dac);
+		if (error != -EAGAIN)
+			break;
+
+		error = xfs_attr_trans_roll(&dac);
+		if (error)
+			return error;
+
+	} while (true);
 
 	return error;
 }
@@ -1188,14 +1215,16 @@ xfs_attr_leaf_mark_incomplete(
  */
 STATIC
 int xfs_attr_node_removename_setup(
-	struct xfs_da_args	*args,
-	struct xfs_da_state	**state)
+	struct xfs_delattr_context	*dac)
 {
-	int			error;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_state		**state = &dac->da_state;
+	int				error;
 
 	error = xfs_attr_node_hasname(args, state);
 	if (error != -EEXIST)
 		return error;
+	error = 0;
 
 	ASSERT((*state)->path.blk[(*state)->path.active - 1].bp != NULL);
 	ASSERT((*state)->path.blk[(*state)->path.active - 1].magic ==
@@ -1204,10 +1233,13 @@ int xfs_attr_node_removename_setup(
 	if (args->rmtblkno > 0) {
 		error = xfs_attr_leaf_mark_incomplete(args, *state);
 		if (error)
-			return error;
+			goto out;
 
-		return xfs_attr_rmtval_invalidate(args);
+		error = xfs_attr_rmtval_invalidate(args);
 	}
+out:
+	if (error)
+		xfs_da_state_free(*state);
 
 	return 0;
 }
@@ -1232,70 +1264,114 @@ xfs_attr_node_remove_cleanup(
 }
 
 /*
- * Remove a name from a B-tree attribute list.
+ * Remove the attribute specified in @args.
  *
  * 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 in-line 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)
+int
+xfs_attr_remove_iter(
+	struct xfs_delattr_context	*dac)
 {
-	struct xfs_da_state	*state;
-	int			retval, error;
-	struct xfs_inode	*dp = args->dp;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_state		*state = dac->da_state;
+	int				retval, error;
+	struct xfs_inode		*dp = args->dp;
 
 	trace_xfs_attr_node_removename(args);
 
-	error = xfs_attr_node_removename_setup(args, &state);
-	if (error)
-		goto out;
+	switch (dac->dela_state) {
+	case XFS_DAS_UNINIT:
+		if (!xfs_inode_hasattr(dp))
+			return -ENOATTR;
 
-	/*
-	 * 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_rmtval_remove(args);
-		if (error)
-			goto out;
+		if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
+			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
+			return xfs_attr_shortform_remove(args);
+		}
+
+		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
+			return xfs_attr_leaf_removename(args);
+
+	/* fallthrough */
+	case XFS_DAS_RMTBLK:
+		dac->dela_state = XFS_DAS_RMTBLK;
+
+		if (!dac->da_state) {
+			error = xfs_attr_node_removename_setup(dac);
+			if (error)
+				goto out;
+		}
+		state = dac->da_state;
 
 		/*
-		 * Refill the state structure with buffers, the prior calls
-		 * released our buffers.
+		 * 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.
 		 */
-		error = xfs_attr_refillstate(state);
-		if (error)
-			goto out;
-	}
-	retval = xfs_attr_node_remove_cleanup(args, state);
+		if (args->rmtblkno > 0) {
+			/*
+			 * May return -EAGAIN. Remove blocks until
+			 * args->rmtblkno == 0
+			 */
+			error = __xfs_attr_rmtval_remove(dac);
+			if (error)
+				break;
+
+			/*
+			 * Refill the state structure with buffers, the prior
+			 * calls released our buffers.
+			 */
+			ASSERT(args->rmtblkno == 0);
+			error = xfs_attr_refillstate(state);
+			if (error)
+				goto out;
+
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			return -EAGAIN;
+		}
+
+		retval = xfs_attr_node_remove_cleanup(args, state);
 
-	/*
-	 * Check to see if the tree needs to be collapsed.
-	 */
-	if (retval && (state->path.active > 1)) {
-		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.
+		 * Check to see if the tree needs to be collapsed. Set the flag
+		 * to indicate that the calling function needs to move the
+		 * shrink operation
 		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
-			goto out;
-	}
+		if (retval && (state->path.active > 1)) {
+			error = xfs_da3_join(state);
+			if (error)
+				goto out;
 
-	/*
-	 * 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);
+			dac->flags |= XFS_DAC_DEFER_FINISH;
+			dac->dela_state = XFS_DAS_RM_SHRINK;
+			return -EAGAIN;
+		}
+
+		/* fallthrough */
+	case 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);
+
+		break;
+	default:
+		ASSERT(0);
+		error = -EINVAL;
+		goto out;
+	}
 
+	if (error == -EAGAIN)
+		return error;
 out:
 	if (state)
 		xfs_da_state_free(state);
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 3e97a93..92a6a50 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -74,6 +74,127 @@ struct xfs_attr_list_context {
 };
 
 
+/*
+ * ========================================================================
+ * Structure used to pass context around among the delayed routines.
+ * ========================================================================
+ */
+
+/*
+ * 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()
+ *              │
+ *              v
+ *        have attr to remove? ──n──> done
+ *              │
+ *              y
+ *              │
+ *              v
+ *        are we short form? ──y──> xfs_attr_shortform_remove ──> done
+ *              │
+ *              n
+ *              │
+ *              V
+ *        are we leaf form? ──y──> xfs_attr_leaf_removename ──> done
+ *              │
+ *              n
+ *              │
+ *              V
+ *   ┌── need to setup state?
+ *   │          │
+ *   n          y
+ *   │          │
+ *   │          v
+ *   │ find attr and get state
+ *   │    attr has blks? ───n────┐
+ *   │          │                v
+ *   │          │         find and invalidate
+ *   │          y         the blocks. mark
+ *   │          │         attr incomplete
+ *   │          ├────────────────┘
+ *   └──────────┤
+ *              │
+ *              v
+ *      Have blks to remove? ─────y────┐
+ *              │       ^      remove the blks
+ *              │       │              │
+ *              │       │              v
+ *              │       │        refill the state
+ *              n       │              │
+ *              │       │              v
+ *              │       │         XFS_DAS_RMTBLK
+ *              │       └─────  re-enter with one
+ *              │               less blk to remove
+ *              │
+ *              v
+ *       remove leaf and
+ *       update hash with
+ *   xfs_attr_node_remove_cleanup
+ *              │
+ *              v
+ *           need to
+ *        shrink tree? ─n─┐
+ *              │         │
+ *              y         │
+ *              │         │
+ *              v         │
+ *          join leaf     │
+ *              │         │
+ *              v         │
+ *      XFS_DAS_RM_SHRINK │
+ *              │         │
+ *              v         │
+ *       do the shrink    │
+ *              │         │
+ *              v         │
+ *          free state <──┘
+ *              │
+ *              v
+ *            done
+ *
+ */
+
+/*
+ * 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 {
+	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
+	XFS_DAS_RMTBLK,		      /* Removing remote blks */
+	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
+};
+
+/*
+ * Defines for xfs_delattr_context.flags
+ */
+#define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
+
+/*
+ * Context used for keeping track of delayed attribute operations
+ */
+struct xfs_delattr_context {
+	struct xfs_da_args      *da_args;
+
+	/* Used in xfs_attr_node_removename to roll through removing blocks */
+	struct xfs_da_state     *da_state;
+
+	/* Used to keep track of current state of delayed operation */
+	unsigned int            flags;
+	enum xfs_delattr_state  dela_state;
+};
+
 /*========================================================================
  * Function prototypes for the kernel.
  *========================================================================*/
@@ -91,6 +212,10 @@ 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);
+int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
 bool xfs_attr_namecheck(const void *name, size_t length);
+void xfs_delattr_context_init(struct xfs_delattr_context *dac,
+			      struct xfs_da_args *args);
 
 #endif	/* __XFS_ATTR_H__ */
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index d6ef69a..3780141 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -19,8 +19,8 @@
 #include "xfs_bmap_btree.h"
 #include "xfs_bmap.h"
 #include "xfs_attr_sf.h"
-#include "xfs_attr_remote.h"
 #include "xfs_attr.h"
+#include "xfs_attr_remote.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 48d8e9c..908521e7 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -674,10 +674,12 @@ xfs_attr_rmtval_invalidate(
  */
 int
 xfs_attr_rmtval_remove(
-	struct xfs_da_args      *args)
+	struct xfs_da_args		*args)
 {
-	int			error;
-	int			retval;
+	int				error;
+	struct xfs_delattr_context	dac  = {
+		.da_args	= args,
+	};
 
 	trace_xfs_attr_rmtval_remove(args);
 
@@ -685,31 +687,29 @@ xfs_attr_rmtval_remove(
 	 * Keep de-allocating extents until the remote-value region is gone.
 	 */
 	do {
-		retval = __xfs_attr_rmtval_remove(args);
-		if (retval && retval != -EAGAIN)
-			return retval;
+		error = __xfs_attr_rmtval_remove(&dac);
+		if (error != -EAGAIN)
+			break;
 
-		/*
-		 * Close out trans and start the next one in the chain.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
+		error = xfs_attr_trans_roll(&dac);
 		if (error)
 			return error;
-	} while (retval == -EAGAIN);
+	} while (true);
 
-	return 0;
+	return error;
 }
 
 /*
  * 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
+ * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the
  * transaction and re-call the function
  */
 int
 __xfs_attr_rmtval_remove(
-	struct xfs_da_args	*args)
+	struct xfs_delattr_context	*dac)
 {
-	int			error, done;
+	struct xfs_da_args		*args = dac->da_args;
+	int				error, done;
 
 	/*
 	 * Unmap value blocks for this attr.
@@ -719,12 +719,20 @@ __xfs_attr_rmtval_remove(
 	if (error)
 		return error;
 
-	error = xfs_defer_finish(&args->trans);
-	if (error)
-		return error;
-
-	if (!done)
+	/*
+	 * We don't need an explicit state here to pick up where we left off. We
+	 * can figure it out using the !done return code. Calling function only
+	 * needs to keep recalling this routine until we indicate to stop by
+	 * returning anything other than -EAGAIN. The actual value of
+	 * attr->xattri_dela_state may be some value reminiscent of the calling
+	 * function, but it's value is irrelevant with in the context of this
+	 * function. Once we are done here, the next state is set as needed
+	 * by the parent
+	 */
+	if (!done) {
+		dac->flags |= XFS_DAC_DEFER_FINISH;
 		return -EAGAIN;
+	}
 
 	return error;
 }
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index 9eee615..002fd30 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -14,5 +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);
+int __xfs_attr_rmtval_remove(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 bfad669..aaa7e66 100644
--- a/fs/xfs/xfs_attr_inactive.c
+++ b/fs/xfs/xfs_attr_inactive.c
@@ -15,10 +15,10 @@
 #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"
-#include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_quota.h"
 #include "xfs_dir2.h"
-- 
2.7.4


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

* [PATCH v16 11/11] xfs: Add delay ready attr set routines
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (9 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 10/11] xfs: Add delay ready attr remove routines Allison Henderson
@ 2021-03-26  0:33 ` Allison Henderson
  2021-04-01 16:57   ` Brian Foster
  2021-03-29 21:59 ` [PATCH v16 00/11] xfs: Delay Ready Attributes Darrick J. Wong
  11 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-03-26  0:33 UTC (permalink / raw)
  To: linux-xfs

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 35014 bytes --]

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. See xfs_attr.h for a more detailed diagram of the states.

Two new helper functions have been added: xfs_attr_rmtval_find_space 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.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
 fs/xfs/libxfs/xfs_attr.c        | 446 ++++++++++++++++++++++++----------------
 fs/xfs/libxfs/xfs_attr.h        | 241 +++++++++++++++++++++-
 fs/xfs/libxfs/xfs_attr_remote.c | 100 ++++++---
 fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
 fs/xfs/xfs_trace.h              |   1 -
 5 files changed, 582 insertions(+), 211 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 4a73691..6a86b62 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -53,15 +53,16 @@ STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args, struct xfs_buf *bp);
  */
 STATIC int xfs_attr_node_get(xfs_da_args_t *args);
 STATIC void xfs_attr_restore_rmt_blk(struct xfs_da_args *args);
-STATIC int xfs_attr_node_addname(struct xfs_da_args *args,
-				 struct xfs_da_state *state);
-STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
-				 struct xfs_da_state **state);
-STATIC int xfs_attr_node_addname_clear_incomplete(struct xfs_da_args *args);
+STATIC int xfs_attr_node_addname(struct xfs_delattr_context *dac);
+STATIC int xfs_attr_node_addname_find_attr(struct xfs_delattr_context *dac);
+STATIC int xfs_attr_node_addname_clear_incomplete(
+				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_set_iter(struct xfs_delattr_context *dac,
+			     struct xfs_buf **leaf_bp);
 
 int
 xfs_inode_hasattr(
@@ -225,7 +226,7 @@ xfs_attr_is_shortform(
  * also checks for a defer finish.  Transaction is finished and rolled as
  * needed, and returns true of false if the delayed operation should continue.
  */
-int
+STATIC int
 xfs_attr_trans_roll(
 	struct xfs_delattr_context	*dac)
 {
@@ -246,29 +247,55 @@ xfs_attr_trans_roll(
 	return error;
 }
 
+/*
+ * Set the attribute specified in @args.
+ */
+int
+xfs_attr_set_args(
+	struct xfs_da_args		*args)
+{
+	struct xfs_buf			*leaf_bp = NULL;
+	int				error = 0;
+	struct xfs_delattr_context	dac = {
+		.da_args	= args,
+	};
+
+	do {
+		error = xfs_attr_set_iter(&dac, &leaf_bp);
+		if (error != -EAGAIN)
+			break;
+
+		error = xfs_attr_trans_roll(&dac);
+		if (error)
+			return error;
+	} while (true);
+
+	return error;
+}
+
 STATIC int
 xfs_attr_set_fmt(
-	struct xfs_da_args	*args)
+	struct xfs_delattr_context	*dac,
+	struct xfs_buf			**leaf_bp)
 {
-	struct xfs_buf          *leaf_bp = NULL;
-	struct xfs_inode	*dp = args->dp;
-	int			error2, error = 0;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_inode		*dp = args->dp;
+	int				error = 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;
-	}
+
+	/* Should only be 0, -EEXIST or -ENOSPC */
+	if (error != -ENOSPC)
+		return error;
 
 	/*
 	 * 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);
+	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
 	if (error)
 		return error;
 
@@ -277,95 +304,139 @@ xfs_attr_set_fmt(
 	 * concurrent AIL push cannot grab the half-baked leaf buffer
 	 * and run into problems with the write verifier.
 	 */
-	xfs_trans_bhold(args->trans, leaf_bp);
-	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;
-	}
+	xfs_trans_bhold(args->trans, *leaf_bp);
 
+	/*
+	 * We're still in XFS_DAS_UNINIT state here.  We've converted
+	 * the attr fork to leaf format and will restart with the leaf
+	 * add.
+	 */
+	dac->flags |= XFS_DAC_DEFER_FINISH;
 	return -EAGAIN;
 }
 
 /*
  * Set the attribute specified in @args.
+ * This routine is meant to function as a delayed operation, and may return
+ * -EAGAIN when the transaction needs to be rolled.  Calling functions will need
+ * to handle this, and recall the function until a successful error code is
+ * returned.
  */
 int
-xfs_attr_set_args(
-	struct xfs_da_args	*args)
+xfs_attr_set_iter(
+	struct xfs_delattr_context	*dac,
+	struct xfs_buf			**leaf_bp)
 {
-	struct xfs_inode	*dp = args->dp;
-	struct xfs_buf		*bp = NULL;
-	struct xfs_da_state     *state = NULL;
-	int			forkoff, error = 0;
+	struct xfs_da_args              *args = dac->da_args;
+	struct xfs_inode		*dp = args->dp;
+	struct xfs_buf			*bp = NULL;
+	struct xfs_da_state		*state = NULL;
+	int				forkoff, error = 0;
 
-	/*
-	 * 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 (xfs_attr_is_shortform(dp)) {
-		error = xfs_attr_set_fmt(args);
-		if (error != -EAGAIN)
-			return error;
-	}
+	/* State machine switch */
+	switch (dac->dela_state) {
+	case XFS_DAS_UNINIT:
+		if (xfs_attr_is_shortform(dp))
+			return xfs_attr_set_fmt(dac, leaf_bp);
 
-	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
-		error = xfs_attr_leaf_try_add(args, bp);
-		if (error == -ENOSPC) {
-			/*
-			 * Promote the attribute list to the Btree format.
-			 */
-			error = xfs_attr3_leaf_to_node(args);
+		/*
+		 * After a shortform to leaf conversion, we need to hold the
+		 * leaf and cycle out the transaction.  When we get back,
+		 * we need to release the leaf to release the hold on the leaf
+		 * buffer.
+		 */
+		if (*leaf_bp != NULL) {
+			xfs_trans_bhold_release(args->trans, *leaf_bp);
+			*leaf_bp = NULL;
+		}
+
+		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+			error = xfs_attr_leaf_try_add(args, *leaf_bp);
+			if (error == -ENOSPC) {
+				/*
+				 * Promote the attribute list to the Btree
+				 * format.
+				 */
+				error = xfs_attr3_leaf_to_node(args);
+				if (error)
+					return error;
+
+				/*
+				 * Finish any deferred work items and roll the
+				 * transaction once more.  The goal here is to
+				 * call node_addname with the inode and
+				 * transaction in the same state (inode locked
+				 * and joined, transaction clean) no matter how
+				 * we got to this step.
+				 *
+				 * At this point, we are still in
+				 * XFS_DAS_UNINIT, but when we come back, we'll
+				 * be a node, so we'll fall down into the node
+				 * handling code below
+				 */
+				dac->flags |= XFS_DAC_DEFER_FINISH;
+				return -EAGAIN;
+			}
+			else if (error)
+				return error;
+		}
+		else {
+			error = xfs_attr_node_addname_find_attr(dac);
 			if (error)
 				return error;
 
-			/*
-			 * Finish any deferred work items and roll the transaction once
-			 * more.  The goal here is to call node_addname with the inode
-			 * and transaction in the same state (inode locked and joined,
-			 * transaction clean) no matter how we got to this step.
-			 */
-			error = xfs_defer_finish(&args->trans);
+			error = xfs_attr_node_addname(dac);
 			if (error)
 				return error;
 
 			/*
-			 * Commit the current trans (including the inode) and
-			 * start a new one.
+			 * If addname was successful, and we dont need to alloc
+			 * anymore blks, we're done.
 			 */
-			error = xfs_trans_roll_inode(&args->trans, dp);
-			if (error)
+			if (!args->rmtblkno && !args->rmtblkno2)
 				return error;
 
-			goto node;
-		}
-		else if (error) {
-			return error;
+			dac->dela_state = XFS_DAS_FOUND_NBLK;
+			return -EAGAIN;
 		}
 
-		/*
-		 * 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;
+		dac->dela_state = XFS_DAS_FOUND_LBLK;
+		return -EAGAIN;
 
+        case XFS_DAS_FOUND_LBLK:
 		/*
 		 * If there was an out-of-line value, allocate the blocks we
 		 * identified for its storage and copy the value.  This is done
 		 * after we create 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_rmtval_set(args);
+
+		/* Open coded xfs_attr_rmtval_set without trans handling */
+		if ((dac->flags & XFS_DAC_LEAF_ADDNAME_INIT) == 0) {
+			dac->flags |= XFS_DAC_LEAF_ADDNAME_INIT;
+			if (args->rmtblkno > 0) {
+				error = xfs_attr_rmtval_find_space(dac);
+				if (error)
+					return error;
+			}
+		}
+
+		/*
+		 * Roll through the "value", allocating blocks on disk as
+		 * required.
+		 */
+		if (dac->blkcnt > 0) {
+			error = xfs_attr_rmtval_set_blk(dac);
 			if (error)
 				return error;
+
+			return -EAGAIN;
 		}
 
+		error = xfs_attr_rmtval_set_value(args);
+		if (error)
+			return error;
+
 		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
 			/*
 			 * Added a "remote" value, just clear the incomplete
@@ -394,22 +465,26 @@ xfs_attr_set_args(
 		 * 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;
+	case XFS_DAS_FLIP_LFLAG:
 		/*
 		 * Dismantle the "old" attribute/value pair by removing a
 		 * "remote" value (if it exists).
 		 */
 		xfs_attr_restore_rmt_blk(args);
 
-		if (args->rmtblkno) {
-			error = xfs_attr_rmtval_invalidate(args);
-			if (error)
-				return error;
+		error = xfs_attr_rmtval_invalidate(args);
+		if (error)
+			return error;
+
+		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
+		dac->dela_state = XFS_DAS_RM_LBLK;
 
-			error = xfs_attr_rmtval_remove(args);
+		/* fallthrough */
+	case XFS_DAS_RM_LBLK:
+		if (args->rmtblkno) {
+			error = __xfs_attr_rmtval_remove(dac);
 			if (error)
 				return error;
 		}
@@ -434,91 +509,114 @@ xfs_attr_set_args(
 			/* bp is gone due to xfs_da_shrink_inode */
 
 		return error;
-	}
-node:
 
+	case XFS_DAS_FOUND_NBLK:
+		/*
+		 * If there was an out-of-line value, allocate the blocks we
+		 * identified for its storage and copy the value.  This is done
+		 * after we create the attribute so that we don't overflow the
+		 * maximum size of a transaction and/or hit a deadlock.
+		 */
+		if (args->rmtblkno > 0) {
+			/*
+			 * Open coded xfs_attr_rmtval_set without trans
+			 * handling
+			 */
+			error = xfs_attr_rmtval_find_space(dac);
+			if (error)
+				return error;
 
-	do {
-		error = xfs_attr_node_addname_find_attr(args, &state);
-		if (error)
-			return error;
-		error = xfs_attr_node_addname(args, state);
-	} while (error == -EAGAIN);
-	if (error)
-		return error;
+			/*
+			 * Roll through the "value", allocating blocks on disk
+			 * as required.  Set the state in case of -EAGAIN return
+			 * code
+			 */
+			dac->dela_state = XFS_DAS_ALLOC_NODE;
+		}
 
-	/*
-	 * 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;
+		/* fallthrough */
+	case XFS_DAS_ALLOC_NODE:
+		if (args->rmtblkno > 0) {
+			if (dac->blkcnt > 0) {
+				error = xfs_attr_rmtval_set_blk(dac);
+				if (error)
+					return error;
 
-	/*
-	 * If there was an out-of-line value, allocate the blocks we
-	 * identified for its storage and copy the value.  This is done
-	 * after we create 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_rmtval_set(args);
-		if (error)
-			return error;
-	}
+				return -EAGAIN;
+			}
+
+			error = xfs_attr_rmtval_set_value(args);
+			if (error)
+				return error;
+		}
+
+		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
+			/*
+			 * Added a "remote" value, just clear the incomplete
+			 * flag.
+			 */
+			if (args->rmtblkno > 0)
+				error = xfs_attr3_leaf_clearflag(args);
+			goto out;
+		}
 
-	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
 		/*
-		 * Added a "remote" value, just clear the incomplete flag.
+		 * If this is an atomic rename operation, we must "flip" the
+		 * incomplete flags on the "new" and "old" attribute/value pairs
+		 * so that one disappears and one appears atomically.  Then we
+		 * must remove the "old" attribute/value pair.
+		 *
+		 * In a separate transaction, set the incomplete flag on the
+		 * "old" attr and clear the incomplete flag on the "new" attr.
 		 */
-		if (args->rmtblkno > 0)
-			error = xfs_attr3_leaf_clearflag(args);
-		goto out;
-	}
-
-	/*
-	 * If this is an atomic rename operation, we must "flip" the incomplete
-	 * flags on the "new" and "old" attribute/value pairs so that one
-	 * disappears and one appears atomically.  Then we must remove the "old"
-	 * attribute/value pair.
-	 *
-	 * In a separate transaction, set the incomplete flag on the "old" attr
-	 * and clear the incomplete flag on the "new" attr.
-	 */
-	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;
+		error = xfs_attr3_leaf_flipflags(args);
+		if (error)
+			goto out;
+		/*
+		 * Commit the flag value change and start the next trans in
+		 * series
+		 */
+		dac->dela_state = XFS_DAS_FLIP_NFLAG;
+		return -EAGAIN;
 
-	/*
-	 * Dismantle the "old" attribute/value pair by removing a "remote" value
-	 * (if it exists).
-	 */
-	xfs_attr_restore_rmt_blk(args);
+	case XFS_DAS_FLIP_NFLAG:
+		/*
+		 * Dismantle the "old" attribute/value pair by removing a
+		 * "remote" value (if it exists).
+		 */
+		xfs_attr_restore_rmt_blk(args);
 
-	if (args->rmtblkno) {
 		error = xfs_attr_rmtval_invalidate(args);
 		if (error)
 			return error;
 
-		error = xfs_attr_rmtval_remove(args);
-		if (error)
-			return error;
-	}
+		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
+		dac->dela_state = XFS_DAS_RM_NBLK;
+
+		/* fallthrough */
+	case XFS_DAS_RM_NBLK:
+		if (args->rmtblkno) {
+			error = __xfs_attr_rmtval_remove(dac);
+			if (error)
+				return error;
+		}
+
+		error = xfs_attr_node_addname_clear_incomplete(dac);
 
-	error = xfs_attr_node_addname_clear_incomplete(args);
 out:
-	if (state)
-		xfs_da_state_free(state);
-	return error;
+		if (state)
+			xfs_da_state_free(state);
+		return error;
+
+	default:
+		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
+		break;
+	}
 
+	return error;
 }
 
+
 /*
  * Return EEXIST if attr is found, or ENOATTR if not
  */
@@ -984,18 +1082,18 @@ xfs_attr_node_hasname(
 
 STATIC int
 xfs_attr_node_addname_find_attr(
-	struct xfs_da_args	*args,
-	struct xfs_da_state     **state)
+	struct xfs_delattr_context	*dac)
 {
-	int			retval;
+	struct xfs_da_args		*args = dac->da_args;
+	int				retval;
 
 	/*
 	 * Search to see if name already exists, and get back a pointer
 	 * to where it should go.
 	 */
-	retval = xfs_attr_node_hasname(args, state);
+	retval = xfs_attr_node_hasname(args, &dac->da_state);
 	if (retval != -ENOATTR && retval != -EEXIST)
-		goto error;
+		return retval;
 
 	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
 		goto error;
@@ -1021,8 +1119,8 @@ xfs_attr_node_addname_find_attr(
 
 	return 0;
 error:
-	if (*state)
-		xfs_da_state_free(*state);
+	if (dac->da_state)
+		xfs_da_state_free(dac->da_state);
 	return retval;
 }
 
@@ -1035,20 +1133,24 @@ xfs_attr_node_addname_find_attr(
  *
  * "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_da_state	*state)
+	struct xfs_delattr_context	*dac)
 {
-	struct xfs_da_state_blk	*blk;
-	struct xfs_inode	*dp;
-	int			error;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_state		*state = dac->da_state;
+	struct xfs_da_state_blk		*blk;
+	int				error;
 
 	trace_xfs_attr_node_addname(args);
 
-	dp = args->dp;
-	blk = &state->path.blk[state->path.active-1];
+	blk = &state->path.blk[ state->path.active-1 ];
 	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
 
 	error = xfs_attr3_leaf_add(blk->bp, state->args);
@@ -1064,18 +1166,15 @@ 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.
+			 * Now that we have converted the leaf to a node, we can
+			 * roll the transaction, and try xfs_attr3_leaf_add
+			 * again on re-entry.  No need to set dela_state to do
+			 * this. dela_state is still unset by this function at
+			 * this point.
 			 */
-			error = xfs_trans_roll_inode(&args->trans, dp);
-			if (error)
-				goto out;
-
+			dac->flags |= XFS_DAC_DEFER_FINISH;
 			return -EAGAIN;
 		}
 
@@ -1088,9 +1187,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.
@@ -1105,8 +1202,9 @@ xfs_attr_node_addname(
 
 STATIC
 int xfs_attr_node_addname_clear_incomplete(
-	struct xfs_da_args		*args)
+	struct xfs_delattr_context	*dac)
 {
+	struct xfs_da_args		*args = dac->da_args;
 	struct xfs_da_state		*state = NULL;
 	struct xfs_da_state_blk		*blk;
 	int				retval = 0;
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 92a6a50..4e4233d 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -159,6 +159,233 @@ struct xfs_attr_list_context {
  *              v
  *            done
  *
+ *
+ * Below is a state machine diagram for attr set operations.
+ *
+ * It seems the challenge with understanding this system comes from trying to
+ * absorb the state machine all at once, when really one should only be looking
+ * at it with in the context of a single function. Once a state sensitive
+ * function is called, the idea is that it "takes ownership" of the
+ * state machine. It isn't concerned with the states that may have belonged to
+ * it's calling parent. Only the states relevant to itself or any other
+ * subroutines there in. Once a calling function hands off the state machine to
+ * a subroutine, it needs to respect the simple rule that it doesn't "own" the
+ * state machine anymore, and it's the responsibility of that calling function
+ * to propagate the -EAGAIN back up the call stack. Upon reentry, it is
+ * committed to re-calling that subroutine until it returns something other than
+ * -EAGAIN. Once that subroutine signals completion (by returning anything other
+ * than -EAGAIN), the calling function can resume using the state machine.
+ *
+ *  xfs_attr_set_iter()
+ *              │
+ *              v
+ *   ┌─y─ has an attr fork?
+ *   │          |
+ *   │          n
+ *   │          |
+ *   │          V
+ *   │       add a fork
+ *   │          │
+ *   └──────────┤
+ *              │
+ *              V
+ *   ┌─y─ is shortform?
+ *   │          │
+ *   │          V
+ *   │   xfs_attr_set_fmt
+ *   │          |
+ *   │          V
+ *   │ xfs_attr_try_sf_addname
+ *   │          │
+ *   │          V
+ *   │      had enough ──y──> done
+ *   │        space?
+ *   n          │
+ *   │          n
+ *   │          │
+ *   │          V
+ *   │   transform to leaf
+ *   │          │
+ *   │          V
+ *   │   hold the leaf buffer
+ *   │          │
+ *   │          V
+ *   │     return -EAGAIN
+ *   │      Re-enter in
+ *   │       leaf form
+ *   │
+ *   └─> release leaf buffer
+ *          if needed
+ *              │
+ *              V
+ *   ┌───n── fork has
+ *   │      only 1 blk?
+ *   │          │
+ *   │          y
+ *   │          │
+ *   │          v
+ *   │ xfs_attr_leaf_try_add()
+ *   │          │
+ *   │          v
+ *   │      had enough ──────────────y───────────────┐
+ *   │        space?                                 │
+ *   │          │                                    │
+ *   │          n                                    │
+ *   │          │                                    │
+ *   │          v                                    │
+ *   │    return -EAGAIN                             │
+ *   │      re-enter in                              │
+ *   │        node form                              │
+ *   │          │                                    │
+ *   └──────────┤                                    │
+ *              │                                    │
+ *              V                                    │
+ * xfs_attr_node_addname_find_attr                   │
+ *        determines if this                         │
+ *       is create or rename                         │
+ *     find space to store attr                      │
+ *              │                                    │
+ *              v                                    │
+ *     xfs_attr_node_addname                         │
+ *              │                                    │
+ *              v                                    │
+ *   fits in a node leaf? ────n─────┐                │
+ *              │     ^             v                │
+ *              │     │        single leaf node?     │
+ *              │     │          │            │      │
+ *              y     │          y            n      │
+ *              │     │          │            │      │
+ *              v     │          v            v      │
+ *            update  │     grow the leaf  split if  │
+ *           hashvals └─── return -EAGAIN   needed   │
+ *              │          retry leaf add     │      │
+ *              │            on reentry       │      │
+ *              ├─────────────────────────────┘      │
+ *              │                                    │
+ *              v                                    │
+ *         need to alloc                             │
+ *   ┌─y── or flip flag?                             │
+ *   │          │                                    │
+ *   │          n                                    │
+ *   │          │                                    │
+ *   │          v                                    │
+ *   │         done                                  │
+ *   │                                               │
+ *   │                                               │
+ *   │         XFS_DAS_FOUND_LBLK <──────────────────┘
+ *   │                  │
+ *   │                  V
+ *   │        xfs_attr_leaf_addname()
+ *   │                  │
+ *   │                  v
+ *   │      ┌──first time through?
+ *   │      │          │
+ *   │      │          y
+ *   │      │          │
+ *   │      n          v
+ *   │      │    if we have rmt blks
+ *   │      │    find space for them
+ *   │      │          │
+ *   │      └──────────┤
+ *   │                 │
+ *   │                 v
+ *   │            still have
+ *   │      ┌─n─ blks to alloc? <──┐
+ *   │      │          │           │
+ *   │      │          y           │
+ *   │      │          │           │
+ *   │      │          v           │
+ *   │      │     alloc one blk    │
+ *   │      │     return -EAGAIN ──┘
+ *   │      │    re-enter with one
+ *   │      │    less blk to alloc
+ *   │      │
+ *   │      │
+ *   │      └───> set the rmt
+ *   │               value
+ *   │                 │
+ *   │                 v
+ *   │               was this
+ *   │              a rename? ──n─┐
+ *   │                 │          │
+ *   │                 y          │
+ *   │                 │          │
+ *   │                 v          │
+ *   │           flip incomplete  │
+ *   │               flag         │
+ *   │                 │          │
+ *   │                 v          │
+ *   │         XFS_DAS_FLIP_LFLAG │
+ *   │                 │          │
+ *   │                 v          │
+ *   │               remove       │
+ *   │        ┌───> old name      │
+ *   │        │        │          │
+ *   │ XFS_DAS_RM_LBLK │          │
+ *   │        ^        │          │
+ *   │        │        v          │
+ *   │        └──y── more to      │
+ *   │               remove       │
+ *   │                 │          │
+ *   │                 n          │
+ *   │                 │          │
+ *   │                 v          │
+ *   │                done <──────┘
+ *   │
+ *   └──────> XFS_DAS_FOUND_NBLK
+ *                     │
+ *                     v
+ *       ┌─────n──  need to
+ *       │        alloc blks?
+ *       │             │
+ *       │             y
+ *       │             │
+ *       │             v
+ *       │        find space
+ *       │             │
+ *       │             v
+ *       │  ┌─>XFS_DAS_ALLOC_NODE
+ *       │  │          │
+ *       │  │          v
+ *       │  │      alloc blk
+ *       │  │          │
+ *       │  │          v
+ *       │  └──y── need to alloc
+ *       │         more blocks?
+ *       │             │
+ *       │             n
+ *       │             │
+ *       │             v
+ *       │      set the rmt value
+ *       │             │
+ *       │             v
+ *       │          was this
+ *       └────────> a rename? ──n─┐
+ *                     │          │
+ *                     y          │
+ *                     │          │
+ *                     v          │
+ *               flip incomplete  │
+ *                   flag         │
+ *                     │          │
+ *                     v          │
+ *             XFS_DAS_FLIP_NFLAG │
+ *                     │          │
+ *                     v          │
+ *                   remove       │
+ *        ┌────────> old name     │
+ *        │            │          │
+ *  XFS_DAS_RM_NBLK    │          │
+ *        ^            │          │
+ *        │            v          │
+ *        └──────y── more to      │
+ *                   remove       │
+ *                     │          │
+ *                     n          │
+ *                     │          │
+ *                     v          │
+ *                    done <──────┘
+ *
  */
 
 /*
@@ -174,12 +401,20 @@ enum xfs_delattr_state {
 	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
 	XFS_DAS_RMTBLK,		      /* Removing remote blks */
 	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
+	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
+	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
+	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
+	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 */
 };
 
 /*
  * Defines for xfs_delattr_context.flags
  */
 #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
+#define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
 
 /*
  * Context used for keeping track of delayed attribute operations
@@ -187,6 +422,11 @@ enum xfs_delattr_state {
 struct xfs_delattr_context {
 	struct xfs_da_args      *da_args;
 
+	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
+	struct xfs_bmbt_irec	map;
+	xfs_dablk_t		lblkno;
+	int			blkcnt;
+
 	/* Used in xfs_attr_node_removename to roll through removing blocks */
 	struct xfs_da_state     *da_state;
 
@@ -213,7 +453,6 @@ 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);
-int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
 bool xfs_attr_namecheck(const void *name, size_t length);
 void xfs_delattr_context_init(struct xfs_delattr_context *dac,
 			      struct xfs_da_args *args);
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 908521e7..fc71f10 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -439,9 +439,9 @@ xfs_attr_rmtval_get(
 
 /*
  * Find a "hole" in the attribute address space large enough for us to drop the
- * new attribute's value into
+ * new attributes value into
  */
-STATIC int
+int
 xfs_attr_rmt_find_hole(
 	struct xfs_da_args	*args)
 {
@@ -468,7 +468,7 @@ xfs_attr_rmt_find_hole(
 	return 0;
 }
 
-STATIC int
+int
 xfs_attr_rmtval_set_value(
 	struct xfs_da_args	*args)
 {
@@ -628,6 +628,69 @@ 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_find_space(
+	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->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 0;
+}
+
+/*
+ * 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.
  */
@@ -669,37 +732,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)
-{
-	int				error;
-	struct xfs_delattr_context	dac  = {
-		.da_args	= args,
-	};
-
-	trace_xfs_attr_rmtval_remove(args);
-
-	/*
-	 * Keep de-allocating extents until the remote-value region is gone.
-	 */
-	do {
-		error = __xfs_attr_rmtval_remove(&dac);
-		if (error != -EAGAIN)
-			break;
-
-		error = xfs_attr_trans_roll(&dac);
-		if (error)
-			return error;
-	} while (true);
-
-	return error;
-}
-
-/*
  * 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 re-call the function
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index 002fd30..8ad68d5 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -10,9 +10,12 @@ int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
 
 int xfs_attr_rmtval_get(struct xfs_da_args *args);
 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);
 int __xfs_attr_rmtval_remove(struct xfs_delattr_context *dac);
+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_find_space(struct xfs_delattr_context *dac);
 #endif /* __XFS_ATTR_REMOTE_H__ */
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index e74bbb6..0c16d46 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1944,7 +1944,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] 44+ messages in thread

* Re: [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d
  2021-03-26  0:32 ` [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d Allison Henderson
@ 2021-03-29  8:44   ` Chandan Babu R
  2021-04-02  9:47     ` Allison Henderson
  2021-04-01 15:43   ` Brian Foster
  1 sibling, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-03-29  8:44 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:02, Allison Henderson wrote:
> Originally we added this patch to help modularize the attr code in
> preparation for delayed attributes and the state machine it requires.
> However, later reviews found that this slightly alters the transaction
> handling as the helper function is ambiguous as to whether the
> transaction is diry or clean.  This may cause a dirty transaction to be
> included in the next roll, where previously it had not.  To preserve the
> existing code flow, we reverse apply this commit.

Indeed, In the mainline kernel, __xfs_attr_rmtval_remove() invokes
xfs_defer_finish() when the last remote block mapping has been removed. Also,
xfs_trans_roll_inode() is invoked before invoking xfs_attr_refillstate().

Hence,

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

--
chandan

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

* Re: [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform
  2021-03-26  0:33 ` [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform Allison Henderson
@ 2021-03-29  9:21   ` Chandan Babu R
  2021-04-02  9:47     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-03-29  9:21 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch hoists xfs_attr_set_shortform into the calling function. This
> will help keep all state management code in the same scope.
>

That looks simple enough.

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

> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 81 ++++++++++++++++--------------------------------
>  1 file changed, 27 insertions(+), 54 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 32c7447..5216f67 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -217,53 +217,6 @@ xfs_attr_is_shortform(
>  }
>  
>  /*
> - * Attempts to set an attr in shortform, or converts short form 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
> @@ -272,7 +225,7 @@ xfs_attr_set_args(
>  {
>  	struct xfs_inode	*dp = args->dp;
>  	struct xfs_buf          *leaf_bp = NULL;
> -	int			error = 0;
> +	int			error2, error = 0;
>  
>  	/*
>  	 * If the attribute list is already in leaf format, jump straight to
> @@ -281,16 +234,36 @@ xfs_attr_set_args(
>  	 * again.
>  	 */
>  	if (xfs_attr_is_shortform(dp)) {
> +		/*
> +		 * 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 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.
> +		 * 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.
>  		 */
> -		error = xfs_attr_set_shortform(args, &leaf_bp);
> -		if (error || !args->trans)
> +		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;
> +		}
>  	}
>  
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {


-- 
chandan

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

* Re: [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt
  2021-03-26  0:33 ` [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt Allison Henderson
@ 2021-03-29  9:37   ` Chandan Babu R
  2021-04-02  9:47     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-03-29  9:37 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch adds a helper function xfs_attr_set_fmt.  This will help
> isolate the code that will require state management from the portions
> that do not.  xfs_attr_set_fmt returns 0 when the attr has been set and
> no further action is needed.  It returns -EAGAIN when shortform has been
> transformed to leaf, and the calling function should proceed the set the
> attr in leaf form.

The previous behaviour is maintained across the changes made by this patch.

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

>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 79 ++++++++++++++++++++++++++++--------------------
>  1 file changed, 46 insertions(+), 33 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 5216f67..d46324a 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -216,6 +216,48 @@ xfs_attr_is_shortform(
>  		ip->i_afp->if_nextents == 0);
>  }
>  
> +STATIC int
> +xfs_attr_set_fmt(
> +	struct xfs_da_args	*args)
> +{
> +	struct xfs_buf          *leaf_bp = NULL;
> +	struct xfs_inode	*dp = args->dp;
> +	int			error2, error = 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.
> +	 */
> +	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 -EAGAIN;
> +}
> +
>  /*
>   * Set the attribute specified in @args.
>   */
> @@ -224,8 +266,7 @@ xfs_attr_set_args(
>  	struct xfs_da_args	*args)
>  {
>  	struct xfs_inode	*dp = args->dp;
> -	struct xfs_buf          *leaf_bp = NULL;
> -	int			error2, error = 0;
> +	int			error;
>  
>  	/*
>  	 * If the attribute list is already in leaf format, jump straight to
> @@ -234,36 +275,9 @@ xfs_attr_set_args(
>  	 * again.
>  	 */
>  	if (xfs_attr_is_shortform(dp)) {
> -		/*
> -		 * 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.
> -		 */
> -		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_fmt(args);
> +		if (error != -EAGAIN)
>  			return error;
> -		}
>  	}
>  
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> @@ -297,8 +311,7 @@ xfs_attr_set_args(
>  			return error;
>  	}
>  
> -	error = xfs_attr_node_addname(args);
> -	return error;
> +	return xfs_attr_node_addname(args);
>  }
>  
>  /*


-- 
chandan

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

* Re: [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete
  2021-03-26  0:33 ` [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete Allison Henderson
@ 2021-03-29 14:42   ` Chandan Babu R
  2021-04-02  9:00     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-03-29 14:42 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch separate xfs_attr_node_addname into two functions.  This will
> help to make it easier to hoist parts of xfs_attr_node_addname that need
> state management
>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index d46324a..531ff56 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -54,6 +54,7 @@ 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_addname_clear_incomplete(struct xfs_da_args *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);
> @@ -1061,6 +1062,25 @@ xfs_attr_node_addname(
>  			return error;
>  	}
>
> +	error = xfs_attr_node_addname_clear_incomplete(args);
> +out:
> +	if (state)
> +		xfs_da_state_free(state);
> +	if (error)
> +		return error;
> +	return retval;

Lets say the user is performing a xattr rename operation and the call to
xfs_attr3_leaf_add() resulted in returning -ENOSPC. xfs_attr_node_addname()
would later allocate a new leaf and insert the new instance of xattr
name/value into this leaf. However, 'retval' will continue to have -ENOSPC as
its value which is incorrectly returned by the above return statement.

--
chandan

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

* Re: [PATCH v16 00/11] xfs: Delay Ready Attributes
  2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
                   ` (10 preceding siblings ...)
  2021-03-26  0:33 ` [PATCH v16 11/11] xfs: Add delay ready attr set routines Allison Henderson
@ 2021-03-29 21:59 ` Darrick J. Wong
  2021-04-02  9:49   ` Allison Henderson
  11 siblings, 1 reply; 44+ messages in thread
From: Darrick J. Wong @ 2021-03-29 21:59 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Mar 25, 2021 at 05:32:57PM -0700, Allison Henderson wrote:
> Hi all,
> 
> This set is a subset of a larger series for Dealyed Attributes. Which is a
> subset of a yet larger series for 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 a delayed
> operation.  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 delayed attribute portion.
> We will introduce parent pointers in a later set.
> 
> In this version I have reduced the set back to the "Delay Ready Attrs" sub series to
> avoid reviewer burn out, but the extended series is available to view in the inlcuded
> git hub links, which extend all the way through parent pointers.  Feel free to review
> as much as feels reasonable.  The set as a whole is a bit much to digest at once, so
> working through it in progressive subsets seems like a reasonable way to manage its
> dev efforts.
> 
> Lastly, in the last revision folks asked for some stress testing on the set.  On my
> system, I found that in an fsstress test with all patches applied, we spend at most
> %0.17 of the time in the attr routines, compared to at most %0.12 with out the set applied.
> Both can fluctuate quite a bit depending on the other operations going on that seem to
> occupy most of the activity.  For the most part though, I do not find these results to be
> particularly concerning.  Though folks are certainly welcome to try it out on their own 
> system to see how the results might differ.
> 
> Updates since v15: Mostly just review feed back from the previous revision.  I've
> tracked changes below to help reviews recall the changes discussed

Hmm... so I ran fstests against this on an otherwise default V5
filesystem, and saw three new regressions:

xfs/125 spat out this from the final repair run:

Phase 1 - find and verify superblock...
Phase 2 - using internal log
	- zero log...
	- scan filesystem freespace and inode maps...
	- found root inode chunk
Phase 3 - for each AG...
	- scan (but don't clear) agi unlinked lists...
	- process known inodes and perform inode discovery...
	- agno = 0
attribute entry #32 in attr block 2, inode 134 is INCOMPLETE
problem with attribute contents in inode 134
would clear attr fork
bad nblocks 8 for inode 134, would reset to 0
bad anextents 4 for inode 134, would reset to 0
	- agno = 1
	- agno = 2
	- agno = 3
	- process newly discovered inodes...
Phase 4 - check for duplicate blocks...
	- setting up duplicate extent list...
	- check for inodes claiming duplicate blocks...
	- agno = 0
	- agno = 1
	- agno = 2
	- agno = 3
No modify flag set, skipping phase 5
Phase 6 - check inode connectivity...
	- traversing filesystem ...
	- traversal finished ...
	- moving disconnected inodes to lost+found ...
Phase 7 - verify link counts...
No modify flag set, skipping filesystem flush and exiting.
xfs_repair should not fail

And xfs/434 and xfs/436 both complained about memory leaks stemming from
an xfs_da_state that xfs/125 didn't free correctly:

[ 1247.150683] =============================================================================
[ 1247.151799] BUG xfs_da_state (Tainted: G    B   W        ): Objects remaining in xfs_da_state on __kmem_cache_shutdown()
[ 1247.153246] -----------------------------------------------------------------------------
[ 1247.153246] 
[ 1247.154528] INFO: Slab 0xffffea00002e9280 objects=17 used=11 fp=0xffff88800ba4b4a0 flags=0xfff80000010200
[ 1247.155764] CPU: 2 PID: 50257 Comm: modprobe Tainted: G    B   W         5.12.0-rc4-djwx #rc4
[ 1247.156849] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-1ubuntu1.1 04/01/2014
[ 1247.157996] Call Trace:
[ 1247.158330]  dump_stack+0x64/0x7c
[ 1247.158767]  slab_err+0xb7/0xdc
[ 1247.159196]  ? printk+0x58/0x6f
[ 1247.159615]  __kmem_cache_shutdown.cold+0x39/0x15e
[ 1247.160248]  kmem_cache_destroy+0x3f/0x110
[ 1247.160779]  xfs_destroy_zones+0xbe/0xe2 [xfs]
[ 1247.161462]  exit_xfs_fs+0x5f/0x9b4 [xfs]
[ 1247.162065]  __do_sys_delete_module.constprop.0+0x145/0x220
[ 1247.162740]  do_syscall_64+0x2d/0x40
[ 1247.163197]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 1247.163810] RIP: 0033:0x7fd91cfe4bcb
[ 1247.164262] Code: 73 01 c3 48 8b 0d c5 82 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 95 82 0c 00 f7 d8 64 89 01 48
[ 1247.166352] RSP: 002b:00007fff89097038 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
[ 1247.167217] RAX: ffffffffffffffda RBX: 0000558b8e105cc0 RCX: 00007fd91cfe4bcb
[ 1247.167998] RDX: 0000000000000000 RSI: 0000000000000800 RDI: 0000558b8e105d28
[ 1247.168781] RBP: 0000558b8e105cc0 R08: 0000000000000000 R09: 0000000000000000
[ 1247.169562] R10: 00007fd91d060ac0 R11: 0000000000000206 R12: 0000558b8e105d28
[ 1247.170351] R13: 0000000000000000 R14: 0000558b8e105d28 R15: 0000558b8e105cc0

From a quick bisect, all of thse problem originates in the last patch.

--D

> xfs: Reverse apply 72b97ea40d
>   NEW
> 
> xfs: Add helper xfs_attr_node_remove_step
>   DROPPED
> 
> xfs: Add xfs_attr_node_remove_cleanup
>   No change
> 
> xfs: Hoist transaction handling in xfs_attr_node_remove_step
>   DROPPED
> 
> xfs: Hoist xfs_attr_set_shortform
>   No change
> 
> xfs: Add helper xfs_attr_set_fmt
>   Fixed helper to return error when defer_finish fails
> 
> xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
>   Renamed xfs_attr_node_addname_work to xfs_attr_node_addname_clear_incomplete
> 
> xfs: Add helper xfs_attr_node_addname_find_attr
>   Renamed goto out, to goto error
> 
> xfs: Hoist xfs_attr_node_addname
>   Removed unused retval variable
>   Removed extra state free in xfs_attr_node_addname
> 
> xfs: Hoist xfs_attr_leaf_addname
>   Fixed spelling typos
> 
> xfs: Hoist node transaction handling
>   Added consistent braces to if/else statement
> 
> xfs: Add delay ready attr remove routines
>   Typo fixes
>   Merged xfs_attr_remove_iter with xfs_attr_node_removename_iter
>   Added state XFS_DAS_RMTBLK
>   Flow chart updated
> 
> xfs: Add delay ready attr set routines
>   Rebase adjustments
>   Typo fixes
> 
> 
> Extended Series Changes
> ------------------------
> xfs: Add state machine tracepoints
>   Rebase adjustments
>   xfs_attr_node_remove_rmt_return removed to match earlier refactoring changes
>   trace_xfs_attr_node_removename_iter_return becomes
>   trace_xfs_attr_remove_iter_return to match earlier refactoring changes
> 
> xfs: Rename __xfs_attr_rmtval_remove
>   No change
> 
> xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
>   Added kmem_alloc_large fall back
>  
> xfs: Set up infrastructure for deferred attribute operations
>   Typo fixes
>   Rename xfs_trans_attr to xfs_trans_attr_finish_update
>   Added helper function xfs_attri_validate
>   Split patch into infrastructure and implementation patches
>   Added XFS_ERROR_REPORT in xlog_recover_attri_commit_pass2:
> 
> xfs: Implement for deferred attribute operations
>   NEW
> 
> xfs: Skip flip flags for delayed attrs
>   Did a performance analysis
> 
> xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
>   Typo fixes
> 
> xfs: Remove unused xfs_attr_*_args
>   Rebase adjustments
> 
> xfs: Add delayed attributes error tag
>   Added errortag include
> 
> xfs: Merge xfs_delattr_context into xfs_attr_item
>   Typo fixes
> 
> 
> This series can be viewed on github here:
> https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v16
> 
> As well as the extended delayed attribute and parent pointer series:
> https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v16_extended
> 
> And the test cases:
> https://github.com/allisonhenderson/xfs_work/tree/pptr_xfstestsv2
> 
> 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_v16
> https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v16_extended
> 
> To run the xfs attributes tests run:
> check -g attr
> 
> To run as delayed attributes run:
> export MOUNT_OPTIONS="-o 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 Henderson (11):
>   xfs: Reverse apply 72b97ea40d
>   xfs: Add xfs_attr_node_remove_cleanup
>   xfs: Hoist xfs_attr_set_shortform
>   xfs: Add helper xfs_attr_set_fmt
>   xfs: Separate xfs_attr_node_addname and
>     xfs_attr_node_addname_clear_incomplete
>   xfs: Add helper xfs_attr_node_addname_find_attr
>   xfs: Hoist xfs_attr_node_addname
>   xfs: Hoist xfs_attr_leaf_addname
>   xfs: Hoist node transaction handling
>   xfs: Add delay ready attr remove routines
>   xfs: Add delay ready attr set routines
> 
>  fs/xfs/libxfs/xfs_attr.c        | 903 ++++++++++++++++++++++++----------------
>  fs/xfs/libxfs/xfs_attr.h        | 364 ++++++++++++++++
>  fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
>  fs/xfs/libxfs/xfs_attr_remote.c | 126 ++++--
>  fs/xfs/libxfs/xfs_attr_remote.h |   7 +-
>  fs/xfs/xfs_attr_inactive.c      |   2 +-
>  fs/xfs/xfs_trace.h              |   1 -
>  7 files changed, 998 insertions(+), 407 deletions(-)
> 
> -- 
> 2.7.4
> 

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

* Re: [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d
  2021-03-26  0:32 ` [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d Allison Henderson
  2021-03-29  8:44   ` Chandan Babu R
@ 2021-04-01 15:43   ` Brian Foster
  2021-04-02  9:49     ` Allison Henderson
  1 sibling, 1 reply; 44+ messages in thread
From: Brian Foster @ 2021-04-01 15:43 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Mar 25, 2021 at 05:32:58PM -0700, Allison Henderson wrote:
> Originally we added this patch to help modularize the attr code in
> preparation for delayed attributes and the state machine it requires.
> However, later reviews found that this slightly alters the transaction
> handling as the helper function is ambiguous as to whether the
> transaction is diry or clean.  This may cause a dirty transaction to be
> included in the next roll, where previously it had not.  To preserve the
> existing code flow, we reverse apply this commit.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---

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

>  fs/xfs/libxfs/xfs_attr.c | 28 +++++++++-------------------
>  1 file changed, 9 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 472b303..b42144e 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1202,24 +1202,6 @@ int xfs_attr_node_removename_setup(
>  	return 0;
>  }
>  
> -STATIC int
> -xfs_attr_node_remove_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.
> -	 */
> -	return xfs_attr_refillstate(state);
> -}
> -
>  /*
>   * Remove a name from a B-tree attribute list.
>   *
> @@ -1248,7 +1230,15 @@ xfs_attr_node_removename(
>  	 * overflow the maximum size of a transaction and/or hit a deadlock.
>  	 */
>  	if (args->rmtblkno > 0) {
> -		error = xfs_attr_node_remove_rmt(args, state);
> +		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);
>  		if (error)
>  			goto out;
>  	}
> -- 
> 2.7.4
> 


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

* Re: [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname
  2021-03-26  0:33 ` [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
@ 2021-04-01 15:43   ` Brian Foster
  2021-04-02  9:01     ` Allison Henderson
  2021-04-02  4:40   ` Chandan Babu R
  1 sibling, 1 reply; 44+ messages in thread
From: Brian Foster @ 2021-04-01 15:43 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Mar 25, 2021 at 05:33:05PM -0700, Allison Henderson wrote:
> This patch hoists xfs_attr_leaf_addname into the calling function.  The
> goal being to get all the code that will require state management into
> the same scope. This isn't particuarly aesthetic right away, but it is a
> preliminary step to merging in the state machine code.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 209 ++++++++++++++++++++++-------------------------
>  1 file changed, 96 insertions(+), 113 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 5b5410f..16f10ac 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -729,115 +821,6 @@ xfs_attr_leaf_try_add(
>  	return retval;
>  }
>  
> -
> -/*
> - * 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);
> -

Did this tracepoint disappear for a reason?

Brian

> -	error = xfs_attr_leaf_try_add(args, bp);
> -	if (error)
> -		return error;
> -
> -	/*
> -	 * 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;
> -
> -	/*
> -	 * If there was an out-of-line value, allocate the blocks we
> -	 * identified for its storage and copy the value.  This is done
> -	 * after we create 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_rmtval_set(args);
> -		if (error)
> -			return error;
> -	}
> -
> -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
> -		/*
> -		 * Added a "remote" value, just clear the incomplete flag.
> -		 */
> -		if (args->rmtblkno > 0)
> -			error = xfs_attr3_leaf_clearflag(args);
> -
> -		return error;
> -	}
> -
> -	/*
> -	 * If this is an atomic rename operation, we must "flip" the incomplete
> -	 * flags on the "new" and "old" attribute/value pairs so that one
> -	 * disappears and one appears atomically.  Then we must remove the "old"
> -	 * attribute/value pair.
> -	 *
> -	 * In a separate transaction, set the incomplete flag on the "old" attr
> -	 * and clear the incomplete flag on the "new" attr.
> -	 */
> -
> -	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 a "remote" value
> -	 * (if it exists).
> -	 */
> -	xfs_attr_restore_rmt_blk(args);
> -
> -	if (args->rmtblkno) {
> -		error = xfs_attr_rmtval_invalidate(args);
> -		if (error)
> -			return error;
> -
> -		error = xfs_attr_rmtval_remove(args);
> -		if (error)
> -			return error;
> -	}
> -
> -	/*
> -	 * Read in the block containing the "old" attr, then remove the "old"
> -	 * attr from that block (neat, huh!)
> -	 */
> -	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
> -				   &bp);
> -	if (error)
> -		return error;
> -
> -	xfs_attr3_leaf_remove(bp, args);
> -
> -	/*
> -	 * If the result is small enough, shrink it all into the inode.
> -	 */
> -	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 */
> -
> -	return error;
> -}
> -
>  /*
>   * Return EEXIST if attr is found, or ENOATTR if not
>   */
> -- 
> 2.7.4
> 


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

* Re: [PATCH v16 10/11] xfs: Add delay ready attr remove routines
  2021-03-26  0:33 ` [PATCH v16 10/11] xfs: Add delay ready attr remove routines Allison Henderson
@ 2021-04-01 16:55   ` Brian Foster
  2021-04-02  9:42     ` Allison Henderson
  2021-04-02  7:59   ` Chandan Babu R
  1 sibling, 1 reply; 44+ messages in thread
From: Brian Foster @ 2021-04-01 16:55 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Mar 25, 2021 at 05:33:07PM -0700, Allison Henderson 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 is merged with
> xfs_attr_node_removename become a new function, xfs_attr_remove_iter.
> This new version uses a sort of state machine like switch to keep track
> of where it was when EAGAIN was returned. A new version of
> xfs_attr_remove_args consists of a simple loop to refresh the
> transaction until the operation is completed. A new XFS_DAC_DEFER_FINISH
> flag is used to finish the transaction where ever the existing code used
> to.
> 
> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> version __xfs_attr_rmtval_remove. We will rename
> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> done.
> 
> xfs_attr_rmtval_remove itself is still in use by the set routines (used
> during a rename).  For reasons of preserving existing function, we
> modify xfs_attr_rmtval_remove to call xfs_defer_finish when the flag is
> set.  Similar to how xfs_attr_remove_args does here.  Once we transition
> the set routines to be delay ready, xfs_attr_rmtval_remove is no longer
> used and will be removed.
> 
> 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.  See xfs_attr.h for a more
> detailed diagram of the states.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c        | 206 +++++++++++++++++++++++++++-------------
>  fs/xfs/libxfs/xfs_attr.h        | 125 ++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
>  fs/xfs/libxfs/xfs_attr_remote.c |  48 ++++++----
>  fs/xfs/libxfs/xfs_attr_remote.h |   2 +-
>  fs/xfs/xfs_attr_inactive.c      |   2 +-
>  6 files changed, 297 insertions(+), 88 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 41accd5..4a73691 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -221,6 +220,32 @@ xfs_attr_is_shortform(
>  		ip->i_afp->if_nextents == 0);
>  }
>  
> +/*
> + * Checks to see if a delayed attribute transaction should be rolled.  If so,
> + * also checks for a defer finish.  Transaction is finished and rolled as
> + * needed, and returns true of false if the delayed operation should continue.
> + */

Outdated comment wrt to the return value.

> +int
> +xfs_attr_trans_roll(
> +	struct xfs_delattr_context	*dac)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	int				error;
> +
> +	if (dac->flags & XFS_DAC_DEFER_FINISH) {
> +		/*
> +		 * The caller wants us to finish all the deferred ops so that we
> +		 * avoid pinning the log tail with a large number of deferred
> +		 * ops.
> +		 */
> +		dac->flags &= ~XFS_DAC_DEFER_FINISH;
> +		error = xfs_defer_finish(&args->trans);
> +	} else
> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +
> +	return error;
> +}
> +
>  STATIC int
>  xfs_attr_set_fmt(
>  	struct xfs_da_args	*args)
...
> @@ -1232,70 +1264,114 @@ xfs_attr_node_remove_cleanup(
>  }
>  
>  /*
> - * Remove a name from a B-tree attribute list.
> + * Remove the attribute specified in @args.
>   *
>   * 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 in-line 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)
> +int
> +xfs_attr_remove_iter(
> +	struct xfs_delattr_context	*dac)
>  {
> -	struct xfs_da_state	*state;
> -	int			retval, error;
> -	struct xfs_inode	*dp = args->dp;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_da_state		*state = dac->da_state;
> +	int				retval, error;
> +	struct xfs_inode		*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
>  
> -	error = xfs_attr_node_removename_setup(args, &state);
> -	if (error)
> -		goto out;
> +	switch (dac->dela_state) {
> +	case XFS_DAS_UNINIT:
> +		if (!xfs_inode_hasattr(dp))
> +			return -ENOATTR;
>  
> -	/*
> -	 * 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_rmtval_remove(args);
> -		if (error)
> -			goto out;
> +		if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
> +			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
> +			return xfs_attr_shortform_remove(args);
> +		}
> +
> +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> +			return xfs_attr_leaf_removename(args);
> +
> +	/* fallthrough */
> +	case XFS_DAS_RMTBLK:
> +		dac->dela_state = XFS_DAS_RMTBLK;
> +
> +		if (!dac->da_state) {
> +			error = xfs_attr_node_removename_setup(dac);
> +			if (error)
> +				goto out;

Do we need the goto here if _removename_setup() frees state on error (or
is the latter change necessary)?

> +		}
> +		state = dac->da_state;

Also, can this fold into the above if (!da_state) branch? Or maybe the
whole setup branch pulled up into the UNINIT state? Not a big deal, but
it does look a little out of place in the RMTBLK state.

>  
>  		/*
> -		 * Refill the state structure with buffers, the prior calls
> -		 * released our buffers.
> +		 * 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.
>  		 */
> -		error = xfs_attr_refillstate(state);
> -		if (error)
> -			goto out;
> -	}
> -	retval = xfs_attr_node_remove_cleanup(args, state);
> +		if (args->rmtblkno > 0) {
> +			/*
> +			 * May return -EAGAIN. Remove blocks until
> +			 * args->rmtblkno == 0
> +			 */
> +			error = __xfs_attr_rmtval_remove(dac);
> +			if (error)
> +				break;

I feel that the difference between a break and goto out might confuse
some of the error handling. Right now, it looks like the exit path
handles either scenario, so we could presumably do something like the
following at the end of the function:

	if (error != -EAGAIN && state)
		xfs_da_state_free(state);
	return error;

... and just ditch the label. Alternatively we could retain the label above
the state check, but just use it consistently throughout the function.

Other than those few nits, this one looks pretty good to me.

Brian

> +
> +			/*
> +			 * Refill the state structure with buffers, the prior
> +			 * calls released our buffers.
> +			 */
> +			ASSERT(args->rmtblkno == 0);
> +			error = xfs_attr_refillstate(state);
> +			if (error)
> +				goto out;
> +
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			return -EAGAIN;
> +		}
> +
> +		retval = xfs_attr_node_remove_cleanup(args, state);
>  
> -	/*
> -	 * Check to see if the tree needs to be collapsed.
> -	 */
> -	if (retval && (state->path.active > 1)) {
> -		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.
> +		 * Check to see if the tree needs to be collapsed. Set the flag
> +		 * to indicate that the calling function needs to move the
> +		 * shrink operation
>  		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> -			goto out;
> -	}
> +		if (retval && (state->path.active > 1)) {
> +			error = xfs_da3_join(state);
> +			if (error)
> +				goto out;
>  
> -	/*
> -	 * 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);
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			dac->dela_state = XFS_DAS_RM_SHRINK;
> +			return -EAGAIN;
> +		}
> +
> +		/* fallthrough */
> +	case 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);
> +
> +		break;
> +	default:
> +		ASSERT(0);
> +		error = -EINVAL;
> +		goto out;
> +	}
>  
> +	if (error == -EAGAIN)
> +		return error;
>  out:
>  	if (state)
>  		xfs_da_state_free(state);
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 3e97a93..92a6a50 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -74,6 +74,127 @@ struct xfs_attr_list_context {
>  };
>  
>  
> +/*
> + * ========================================================================
> + * Structure used to pass context around among the delayed routines.
> + * ========================================================================
> + */
> +
> +/*
> + * 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()
> + *              │
> + *              v
> + *        have attr to remove? ──n──> done
> + *              │
> + *              y
> + *              │
> + *              v
> + *        are we short form? ──y──> xfs_attr_shortform_remove ──> done
> + *              │
> + *              n
> + *              │
> + *              V
> + *        are we leaf form? ──y──> xfs_attr_leaf_removename ──> done
> + *              │
> + *              n
> + *              │
> + *              V
> + *   ┌── need to setup state?
> + *   │          │
> + *   n          y
> + *   │          │
> + *   │          v
> + *   │ find attr and get state
> + *   │    attr has blks? ───n────???
> + *   │          │                v
> + *   │          │         find and invalidate
> + *   │          y         the blocks. mark
> + *   │          │         attr incomplete
> + *   │          ├────────────────┘
> + *   └──────────┤
> + *              │
> + *              v
> + *      Have blks to remove? ─────y────???
> + *              │       ^      remove the blks
> + *              │       │              │
> + *              │       │              v
> + *              │       │        refill the state
> + *              n       │              │
> + *              │       │              v
> + *              │       │         XFS_DAS_RMTBLK
> + *              │       └─────  re-enter with one
> + *              │               less blk to remove
> + *              │
> + *              v
> + *       remove leaf and
> + *       update hash with
> + *   xfs_attr_node_remove_cleanup
> + *              │
> + *              v
> + *           need to
> + *        shrink tree? ─n─???
> + *              │         │
> + *              y         │
> + *              │         │
> + *              v         │
> + *          join leaf     │
> + *              │         │
> + *              v         │
> + *      XFS_DAS_RM_SHRINK │
> + *              │         │
> + *              v         │
> + *       do the shrink    │
> + *              │         │
> + *              v         │
> + *          free state <──┘
> + *              │
> + *              v
> + *            done
> + *
> + */
> +
> +/*
> + * 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 {
> +	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
> +	XFS_DAS_RMTBLK,		      /* Removing remote blks */
> +	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
> +};
> +
> +/*
> + * Defines for xfs_delattr_context.flags
> + */
> +#define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
> +
> +/*
> + * Context used for keeping track of delayed attribute operations
> + */
> +struct xfs_delattr_context {
> +	struct xfs_da_args      *da_args;
> +
> +	/* Used in xfs_attr_node_removename to roll through removing blocks */
> +	struct xfs_da_state     *da_state;
> +
> +	/* Used to keep track of current state of delayed operation */
> +	unsigned int            flags;
> +	enum xfs_delattr_state  dela_state;
> +};
> +
>  /*========================================================================
>   * Function prototypes for the kernel.
>   *========================================================================*/
> @@ -91,6 +212,10 @@ 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);
> +int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
>  bool xfs_attr_namecheck(const void *name, size_t length);
> +void xfs_delattr_context_init(struct xfs_delattr_context *dac,
> +			      struct xfs_da_args *args);
>  
>  #endif	/* __XFS_ATTR_H__ */
> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> index d6ef69a..3780141 100644
> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> @@ -19,8 +19,8 @@
>  #include "xfs_bmap_btree.h"
>  #include "xfs_bmap.h"
>  #include "xfs_attr_sf.h"
> -#include "xfs_attr_remote.h"
>  #include "xfs_attr.h"
> +#include "xfs_attr_remote.h"
>  #include "xfs_attr_leaf.h"
>  #include "xfs_error.h"
>  #include "xfs_trace.h"
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index 48d8e9c..908521e7 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -674,10 +674,12 @@ xfs_attr_rmtval_invalidate(
>   */
>  int
>  xfs_attr_rmtval_remove(
> -	struct xfs_da_args      *args)
> +	struct xfs_da_args		*args)
>  {
> -	int			error;
> -	int			retval;
> +	int				error;
> +	struct xfs_delattr_context	dac  = {
> +		.da_args	= args,
> +	};
>  
>  	trace_xfs_attr_rmtval_remove(args);
>  
> @@ -685,31 +687,29 @@ xfs_attr_rmtval_remove(
>  	 * Keep de-allocating extents until the remote-value region is gone.
>  	 */
>  	do {
> -		retval = __xfs_attr_rmtval_remove(args);
> -		if (retval && retval != -EAGAIN)
> -			return retval;
> +		error = __xfs_attr_rmtval_remove(&dac);
> +		if (error != -EAGAIN)
> +			break;
>  
> -		/*
> -		 * Close out trans and start the next one in the chain.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +		error = xfs_attr_trans_roll(&dac);
>  		if (error)
>  			return error;
> -	} while (retval == -EAGAIN);
> +	} while (true);
>  
> -	return 0;
> +	return error;
>  }
>  
>  /*
>   * 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
> + * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the
>   * transaction and re-call the function
>   */
>  int
>  __xfs_attr_rmtval_remove(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac)
>  {
> -	int			error, done;
> +	struct xfs_da_args		*args = dac->da_args;
> +	int				error, done;
>  
>  	/*
>  	 * Unmap value blocks for this attr.
> @@ -719,12 +719,20 @@ __xfs_attr_rmtval_remove(
>  	if (error)
>  		return error;
>  
> -	error = xfs_defer_finish(&args->trans);
> -	if (error)
> -		return error;
> -
> -	if (!done)
> +	/*
> +	 * We don't need an explicit state here to pick up where we left off. We
> +	 * can figure it out using the !done return code. Calling function only
> +	 * needs to keep recalling this routine until we indicate to stop by
> +	 * returning anything other than -EAGAIN. The actual value of
> +	 * attr->xattri_dela_state may be some value reminiscent of the calling
> +	 * function, but it's value is irrelevant with in the context of this
> +	 * function. Once we are done here, the next state is set as needed
> +	 * by the parent
> +	 */
> +	if (!done) {
> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>  		return -EAGAIN;
> +	}
>  
>  	return error;
>  }
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> index 9eee615..002fd30 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.h
> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> @@ -14,5 +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);
> +int __xfs_attr_rmtval_remove(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 bfad669..aaa7e66 100644
> --- a/fs/xfs/xfs_attr_inactive.c
> +++ b/fs/xfs/xfs_attr_inactive.c
> @@ -15,10 +15,10 @@
>  #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"
> -#include "xfs_attr.h"
>  #include "xfs_attr_leaf.h"
>  #include "xfs_quota.h"
>  #include "xfs_dir2.h"
> -- 
> 2.7.4
> 


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

* Re: [PATCH v16 11/11] xfs: Add delay ready attr set routines
  2021-03-26  0:33 ` [PATCH v16 11/11] xfs: Add delay ready attr set routines Allison Henderson
@ 2021-04-01 16:57   ` Brian Foster
  2021-04-02  9:01     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Brian Foster @ 2021-04-01 16:57 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Mar 25, 2021 at 05:33:08PM -0700, Allison Henderson 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. See xfs_attr.h for a more detailed diagram of the states.
> 
> Two new helper functions have been added: xfs_attr_rmtval_find_space 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> ---
>  fs/xfs/libxfs/xfs_attr.c        | 446 ++++++++++++++++++++++++----------------
>  fs/xfs/libxfs/xfs_attr.h        | 241 +++++++++++++++++++++-
>  fs/xfs/libxfs/xfs_attr_remote.c | 100 ++++++---
>  fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
>  fs/xfs/xfs_trace.h              |   1 -
>  5 files changed, 582 insertions(+), 211 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4a73691..6a86b62 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -246,29 +247,55 @@ xfs_attr_trans_roll(
>  	return error;
>  }
>  
> +/*
> + * Set the attribute specified in @args.
> + */
> +int
> +xfs_attr_set_args(
> +	struct xfs_da_args		*args)
> +{
> +	struct xfs_buf			*leaf_bp = NULL;
> +	int				error = 0;
> +	struct xfs_delattr_context	dac = {
> +		.da_args	= args,
> +	};
> +
> +	do {
> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
> +		if (error != -EAGAIN)
> +			break;
> +
> +		error = xfs_attr_trans_roll(&dac);
> +		if (error)
> +			return error;
> +	} while (true);
> +
> +	return error;
> +}
> +
>  STATIC int
>  xfs_attr_set_fmt(
> -	struct xfs_da_args	*args)
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_buf			**leaf_bp)
>  {
> -	struct xfs_buf          *leaf_bp = NULL;
> -	struct xfs_inode	*dp = args->dp;
> -	int			error2, error = 0;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	int				error = 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;
> -	}
> +
> +	/* Should only be 0, -EEXIST or -ENOSPC */
> +	if (error != -ENOSPC)
> +		return error;

Ok, so it looks like the commit that goes away here is replaced by one
up the call stack.

>  
>  	/*
>  	 * 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);
> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
>  	if (error)
>  		return error;
>  
> @@ -277,95 +304,139 @@ xfs_attr_set_fmt(
>  	 * concurrent AIL push cannot grab the half-baked leaf buffer
>  	 * and run into problems with the write verifier.
>  	 */
> -	xfs_trans_bhold(args->trans, leaf_bp);
> -	error = xfs_defer_finish(&args->trans);
> -	xfs_trans_bhold_release(args->trans, leaf_bp);
> -	if (error) {
> -		xfs_trans_brelse(args->trans, leaf_bp);

What about the xfs_trans_brelse() in the error case that seems to
disappear?

> -		return error;
> -	}
> +	xfs_trans_bhold(args->trans, *leaf_bp);
>  
> +	/*
> +	 * We're still in XFS_DAS_UNINIT state here.  We've converted
> +	 * the attr fork to leaf format and will restart with the leaf
> +	 * add.
> +	 */
> +	dac->flags |= XFS_DAC_DEFER_FINISH;
>  	return -EAGAIN;
>  }
>  
>  /*
>   * Set the attribute specified in @args.
> + * This routine is meant to function as a delayed operation, and may return
> + * -EAGAIN when the transaction needs to be rolled.  Calling functions will need
> + * to handle this, and recall the function until a successful error code is
> + * returned.
>   */
>  int
> -xfs_attr_set_args(
> -	struct xfs_da_args	*args)
> +xfs_attr_set_iter(
> +	struct xfs_delattr_context	*dac,
> +	struct xfs_buf			**leaf_bp)
>  {
> -	struct xfs_inode	*dp = args->dp;
> -	struct xfs_buf		*bp = NULL;
> -	struct xfs_da_state     *state = NULL;
> -	int			forkoff, error = 0;
> +	struct xfs_da_args              *args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
> +	struct xfs_buf			*bp = NULL;
> +	struct xfs_da_state		*state = NULL;
> +	int				forkoff, error = 0;
>  
> -	/*
> -	 * 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 (xfs_attr_is_shortform(dp)) {
> -		error = xfs_attr_set_fmt(args);
> -		if (error != -EAGAIN)
> -			return error;
> -	}
> +	/* State machine switch */
> +	switch (dac->dela_state) {
> +	case XFS_DAS_UNINIT:
> +		if (xfs_attr_is_shortform(dp))
> +			return xfs_attr_set_fmt(dac, leaf_bp);
>  
> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> -		error = xfs_attr_leaf_try_add(args, bp);
> -		if (error == -ENOSPC) {
> -			/*
> -			 * Promote the attribute list to the Btree format.
> -			 */
> -			error = xfs_attr3_leaf_to_node(args);
> +		/*
> +		 * After a shortform to leaf conversion, we need to hold the
> +		 * leaf and cycle out the transaction.  When we get back,
> +		 * we need to release the leaf to release the hold on the leaf
> +		 * buffer.
> +		 */
> +		if (*leaf_bp != NULL) {
> +			xfs_trans_bhold_release(args->trans, *leaf_bp);
> +			*leaf_bp = NULL;
> +		}
> +
> +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> +			error = xfs_attr_leaf_try_add(args, *leaf_bp);
> +			if (error == -ENOSPC) {
> +				/*
> +				 * Promote the attribute list to the Btree
> +				 * format.
> +				 */
> +				error = xfs_attr3_leaf_to_node(args);
> +				if (error)
> +					return error;
> +
> +				/*
> +				 * Finish any deferred work items and roll the
> +				 * transaction once more.  The goal here is to
> +				 * call node_addname with the inode and
> +				 * transaction in the same state (inode locked
> +				 * and joined, transaction clean) no matter how
> +				 * we got to this step.
> +				 *
> +				 * At this point, we are still in
> +				 * XFS_DAS_UNINIT, but when we come back, we'll
> +				 * be a node, so we'll fall down into the node
> +				 * handling code below
> +				 */
> +				dac->flags |= XFS_DAC_DEFER_FINISH;
> +				return -EAGAIN;
> +			}
> +			else if (error)

Nit:			} else if (error)

> +				return error;
> +		}
> +		else {

Nit:		} else {

> +			error = xfs_attr_node_addname_find_attr(dac);
>  			if (error)
>  				return error;
>  
> -			/*
> -			 * Finish any deferred work items and roll the transaction once
> -			 * more.  The goal here is to call node_addname with the inode
> -			 * and transaction in the same state (inode locked and joined,
> -			 * transaction clean) no matter how we got to this step.
> -			 */
> -			error = xfs_defer_finish(&args->trans);
> +			error = xfs_attr_node_addname(dac);
>  			if (error)
>  				return error;

Ok, so these couple of node calls get peeled out of the loop that
existed prior to this patch and xfs_attr_node_addname() returns -EAGAIN
to trigger reentry, if necessary.

>  
>  			/*
> -			 * Commit the current trans (including the inode) and
> -			 * start a new one.
> +			 * If addname was successful, and we dont need to alloc
> +			 * anymore blks, we're done.
>  			 */
> -			error = xfs_trans_roll_inode(&args->trans, dp);
> -			if (error)
> +			if (!args->rmtblkno && !args->rmtblkno2)
>  				return error;

Is this check new? What about clearing flags and whatnot?

>  
> -			goto node;
> -		}
> -		else if (error) {
> -			return error;
> +			dac->dela_state = XFS_DAS_FOUND_NBLK;
> +			return -EAGAIN;
>  		}
>  
> -		/*
> -		 * 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;
> +		dac->dela_state = XFS_DAS_FOUND_LBLK;
> +		return -EAGAIN;

Is there a reason the node branch sets the state in the branch but the
leaf branch falls out to here? I.e., could we not be consistent and have

	if (one_block()) {
		...
		dac->dela_state = XFS_DAS_FOUND_LBLK;
	} else {
		...
		dac->dela_state = XFS_DAS_FOUND_NBLK;
	}
	
	return -EAGAIN;

>  
> +        case XFS_DAS_FOUND_LBLK:
>  		/*
>  		 * If there was an out-of-line value, allocate the blocks we
>  		 * identified for its storage and copy the value.  This is done
>  		 * after we create 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_rmtval_set(args);
> +
> +		/* Open coded xfs_attr_rmtval_set without trans handling */
> +		if ((dac->flags & XFS_DAC_LEAF_ADDNAME_INIT) == 0) {
> +			dac->flags |= XFS_DAC_LEAF_ADDNAME_INIT;
> +			if (args->rmtblkno > 0) {
> +				error = xfs_attr_rmtval_find_space(dac);
> +				if (error)
> +					return error;
> +			}
> +		}
> +
> +		/*
> +		 * Roll through the "value", allocating blocks on disk as
> +		 * required.
> +		 */
> +		if (dac->blkcnt > 0) {
> +			error = xfs_attr_rmtval_set_blk(dac);
>  			if (error)
>  				return error;
> +
> +			return -EAGAIN;
>  		}
>  
> +		error = xfs_attr_rmtval_set_value(args);
> +		if (error)
> +			return error;
> +
>  		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>  			/*
>  			 * Added a "remote" value, just clear the incomplete
> @@ -394,22 +465,26 @@ xfs_attr_set_args(
>  		 * 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;
> +	case XFS_DAS_FLIP_LFLAG:
>  		/*
>  		 * Dismantle the "old" attribute/value pair by removing a
>  		 * "remote" value (if it exists).
>  		 */
>  		xfs_attr_restore_rmt_blk(args);
>  
> -		if (args->rmtblkno) {
> -			error = xfs_attr_rmtval_invalidate(args);
> -			if (error)
> -				return error;
> +		error = xfs_attr_rmtval_invalidate(args);
> +		if (error)
> +			return error;
> +
> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
> +		dac->dela_state = XFS_DAS_RM_LBLK;
>  
> -			error = xfs_attr_rmtval_remove(args);
> +		/* fallthrough */
> +	case XFS_DAS_RM_LBLK:
> +		if (args->rmtblkno) {
> +			error = __xfs_attr_rmtval_remove(dac);
>  			if (error)
>  				return error;
>  		}

This looks like a similar scenario as before where
xfs_attr_rmtval_remove() would have returned with a clean transaction
after the final unmap, but here __xfs_attr_rmtval_remove() just returns
0 if done == 1. We probably need to roll one more time out of this
branch and land in a subsequent state..?

> @@ -434,91 +509,114 @@ xfs_attr_set_args(
>  			/* bp is gone due to xfs_da_shrink_inode */
>  
>  		return error;
> -	}
> -node:
>  
> +	case XFS_DAS_FOUND_NBLK:
> +		/*
> +		 * If there was an out-of-line value, allocate the blocks we
> +		 * identified for its storage and copy the value.  This is done
> +		 * after we create the attribute so that we don't overflow the
> +		 * maximum size of a transaction and/or hit a deadlock.
> +		 */
> +		if (args->rmtblkno > 0) {
> +			/*
> +			 * Open coded xfs_attr_rmtval_set without trans
> +			 * handling
> +			 */
> +			error = xfs_attr_rmtval_find_space(dac);
> +			if (error)
> +				return error;
>  
> -	do {
> -		error = xfs_attr_node_addname_find_attr(args, &state);
> -		if (error)
> -			return error;
> -		error = xfs_attr_node_addname(args, state);
> -	} while (error == -EAGAIN);
> -	if (error)
> -		return error;
> +			/*
> +			 * Roll through the "value", allocating blocks on disk
> +			 * as required.  Set the state in case of -EAGAIN return
> +			 * code
> +			 */
> +			dac->dela_state = XFS_DAS_ALLOC_NODE;
> +		}
>  
> -	/*
> -	 * 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;
> +		/* fallthrough */
> +	case XFS_DAS_ALLOC_NODE:
> +		if (args->rmtblkno > 0) {
> +			if (dac->blkcnt > 0) {
> +				error = xfs_attr_rmtval_set_blk(dac);
> +				if (error)
> +					return error;
>  
> -	/*
> -	 * If there was an out-of-line value, allocate the blocks we
> -	 * identified for its storage and copy the value.  This is done
> -	 * after we create 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_rmtval_set(args);
> -		if (error)
> -			return error;
> -	}
> +				return -EAGAIN;
> +			}
> +
> +			error = xfs_attr_rmtval_set_value(args);
> +			if (error)
> +				return error;
> +		}
> +
> +		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
> +			/*
> +			 * Added a "remote" value, just clear the incomplete
> +			 * flag.
> +			 */
> +			if (args->rmtblkno > 0)
> +				error = xfs_attr3_leaf_clearflag(args);
> +			goto out;
> +		}
>  
> -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>  		/*
> -		 * Added a "remote" value, just clear the incomplete flag.
> +		 * If this is an atomic rename operation, we must "flip" the
> +		 * incomplete flags on the "new" and "old" attribute/value pairs
> +		 * so that one disappears and one appears atomically.  Then we
> +		 * must remove the "old" attribute/value pair.
> +		 *
> +		 * In a separate transaction, set the incomplete flag on the
> +		 * "old" attr and clear the incomplete flag on the "new" attr.
>  		 */
> -		if (args->rmtblkno > 0)
> -			error = xfs_attr3_leaf_clearflag(args);
> -		goto out;
> -	}
> -
> -	/*
> -	 * If this is an atomic rename operation, we must "flip" the incomplete
> -	 * flags on the "new" and "old" attribute/value pairs so that one
> -	 * disappears and one appears atomically.  Then we must remove the "old"
> -	 * attribute/value pair.
> -	 *
> -	 * In a separate transaction, set the incomplete flag on the "old" attr
> -	 * and clear the incomplete flag on the "new" attr.
> -	 */
> -	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;
> +		error = xfs_attr3_leaf_flipflags(args);
> +		if (error)
> +			goto out;
> +		/*
> +		 * Commit the flag value change and start the next trans in
> +		 * series
> +		 */
> +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> +		return -EAGAIN;
>  
> -	/*
> -	 * Dismantle the "old" attribute/value pair by removing a "remote" value
> -	 * (if it exists).
> -	 */
> -	xfs_attr_restore_rmt_blk(args);
> +	case XFS_DAS_FLIP_NFLAG:
> +		/*
> +		 * Dismantle the "old" attribute/value pair by removing a
> +		 * "remote" value (if it exists).
> +		 */
> +		xfs_attr_restore_rmt_blk(args);
>  
> -	if (args->rmtblkno) {
>  		error = xfs_attr_rmtval_invalidate(args);
>  		if (error)
>  			return error;
>  
> -		error = xfs_attr_rmtval_remove(args);
> -		if (error)
> -			return error;
> -	}
> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
> +		dac->dela_state = XFS_DAS_RM_NBLK;
> +
> +		/* fallthrough */
> +	case XFS_DAS_RM_NBLK:
> +		if (args->rmtblkno) {
> +			error = __xfs_attr_rmtval_remove(dac);
> +			if (error)
> +				return error;
> +		}

Similar thing here with __xfs_attr_rmtval_remove()..?

> +
> +		error = xfs_attr_node_addname_clear_incomplete(dac);
>  
> -	error = xfs_attr_node_addname_clear_incomplete(args);
>  out:
> -	if (state)
> -		xfs_da_state_free(state);
> -	return error;
> +		if (state)
> +			xfs_da_state_free(state);
> +		return error;

Can we avoid this out label landing inside the switch statement? That
looks like a landmine. Even if we just duplicated an 'done_out' path
after the last return in the function, I think that would be preferable.

All previous feedback aside, I think this patch now looks much more
digestable in general. Most of the state code is isolated to the _iter()
function and so it's much easier to follow along and compare against the
current code flow. I did still have some thoughts with regard to further
cleanups, possibly clearing up some the logic and/or tweaking the states
and whatnot, but I think this is at a point where it might be reasonable
to make such changes on top of this patch instead of continuing to make
significant changes to it. If I get a chance perhaps I'll take a closer
look at that once the remaining kinks are worked out..

Brian

> +
> +	default:
> +		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
> +		break;
> +	}
>  
> +	return error;
>  }
>  
> +
>  /*
>   * Return EEXIST if attr is found, or ENOATTR if not
>   */
> @@ -984,18 +1082,18 @@ xfs_attr_node_hasname(
>  
>  STATIC int
>  xfs_attr_node_addname_find_attr(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state     **state)
> +	struct xfs_delattr_context	*dac)
>  {
> -	int			retval;
> +	struct xfs_da_args		*args = dac->da_args;
> +	int				retval;
>  
>  	/*
>  	 * Search to see if name already exists, and get back a pointer
>  	 * to where it should go.
>  	 */
> -	retval = xfs_attr_node_hasname(args, state);
> +	retval = xfs_attr_node_hasname(args, &dac->da_state);
>  	if (retval != -ENOATTR && retval != -EEXIST)
> -		goto error;
> +		return retval;
>  
>  	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>  		goto error;
> @@ -1021,8 +1119,8 @@ xfs_attr_node_addname_find_attr(
>  
>  	return 0;
>  error:
> -	if (*state)
> -		xfs_da_state_free(*state);
> +	if (dac->da_state)
> +		xfs_da_state_free(dac->da_state);
>  	return retval;
>  }
>  
> @@ -1035,20 +1133,24 @@ xfs_attr_node_addname_find_attr(
>   *
>   * "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_da_state	*state)
> +	struct xfs_delattr_context	*dac)
>  {
> -	struct xfs_da_state_blk	*blk;
> -	struct xfs_inode	*dp;
> -	int			error;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_da_state		*state = dac->da_state;
> +	struct xfs_da_state_blk		*blk;
> +	int				error;
>  
>  	trace_xfs_attr_node_addname(args);
>  
> -	dp = args->dp;
> -	blk = &state->path.blk[state->path.active-1];
> +	blk = &state->path.blk[ state->path.active-1 ];
>  	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  
>  	error = xfs_attr3_leaf_add(blk->bp, state->args);
> @@ -1064,18 +1166,15 @@ 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.
> +			 * Now that we have converted the leaf to a node, we can
> +			 * roll the transaction, and try xfs_attr3_leaf_add
> +			 * again on re-entry.  No need to set dela_state to do
> +			 * this. dela_state is still unset by this function at
> +			 * this point.
>  			 */
> -			error = xfs_trans_roll_inode(&args->trans, dp);
> -			if (error)
> -				goto out;
> -
> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>  			return -EAGAIN;
>  		}
>  
> @@ -1088,9 +1187,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.
> @@ -1105,8 +1202,9 @@ xfs_attr_node_addname(
>  
>  STATIC
>  int xfs_attr_node_addname_clear_incomplete(
> -	struct xfs_da_args		*args)
> +	struct xfs_delattr_context	*dac)
>  {
> +	struct xfs_da_args		*args = dac->da_args;
>  	struct xfs_da_state		*state = NULL;
>  	struct xfs_da_state_blk		*blk;
>  	int				retval = 0;
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 92a6a50..4e4233d 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -159,6 +159,233 @@ struct xfs_attr_list_context {
>   *              v
>   *            done
>   *
> + *
> + * Below is a state machine diagram for attr set operations.
> + *
> + * It seems the challenge with understanding this system comes from trying to
> + * absorb the state machine all at once, when really one should only be looking
> + * at it with in the context of a single function. Once a state sensitive
> + * function is called, the idea is that it "takes ownership" of the
> + * state machine. It isn't concerned with the states that may have belonged to
> + * it's calling parent. Only the states relevant to itself or any other
> + * subroutines there in. Once a calling function hands off the state machine to
> + * a subroutine, it needs to respect the simple rule that it doesn't "own" the
> + * state machine anymore, and it's the responsibility of that calling function
> + * to propagate the -EAGAIN back up the call stack. Upon reentry, it is
> + * committed to re-calling that subroutine until it returns something other than
> + * -EAGAIN. Once that subroutine signals completion (by returning anything other
> + * than -EAGAIN), the calling function can resume using the state machine.
> + *
> + *  xfs_attr_set_iter()
> + *              │
> + *              v
> + *   ┌─y─ has an attr fork?
> + *   │          |
> + *   │          n
> + *   │          |
> + *   │          V
> + *   │       add a fork
> + *   │          │
> + *   └──────────┤
> + *              │
> + *              V
> + *   ┌─y─ is shortform?
> + *   │          │
> + *   │          V
> + *   │   xfs_attr_set_fmt
> + *   │          |
> + *   │          V
> + *   │ xfs_attr_try_sf_addname
> + *   │          │
> + *   │          V
> + *   │      had enough ──y──> done
> + *   │        space?
> + *   n          │
> + *   │          n
> + *   │          │
> + *   │          V
> + *   │   transform to leaf
> + *   │          │
> + *   │          V
> + *   │   hold the leaf buffer
> + *   │          │
> + *   │          V
> + *   │     return -EAGAIN
> + *   │      Re-enter in
> + *   │       leaf form
> + *   │
> + *   └─> release leaf buffer
> + *          if needed
> + *              │
> + *              V
> + *   ┌───n── fork has
> + *   │      only 1 blk?
> + *   │          │
> + *   │          y
> + *   │          │
> + *   │          v
> + *   │ xfs_attr_leaf_try_add()
> + *   │          │
> + *   │          v
> + *   │      had enough ──────────────y───────────────???
> + *   │        space?                                 │
> + *   │          │                                    │
> + *   │          n                                    │
> + *   │          │                                    │
> + *   │          v                                    │
> + *   │    return -EAGAIN                             │
> + *   │      re-enter in                              │
> + *   │        node form                              │
> + *   │          │                                    │
> + *   └──────────┤                                    │
> + *              │                                    │
> + *              V                                    │
> + * xfs_attr_node_addname_find_attr                   │
> + *        determines if this                         │
> + *       is create or rename                         │
> + *     find space to store attr                      │
> + *              │                                    │
> + *              v                                    │
> + *     xfs_attr_node_addname                         │
> + *              │                                    │
> + *              v                                    │
> + *   fits in a node leaf? ────n─────???                │
> + *              │     ^             v                │
> + *              │     │        single leaf node?     │
> + *              │     │          │            │      │
> + *              y     │          y            n      │
> + *              │     │          │            │      │
> + *              v     │          v            v      │
> + *            update  │     grow the leaf  split if  │
> + *           hashvals └─── return -EAGAIN   needed   │
> + *              │          retry leaf add     │      │
> + *              │            on reentry       │      │
> + *              ├─────────────────────────────┘      │
> + *              │                                    │
> + *              v                                    │
> + *         need to alloc                             │
> + *   ┌─y── or flip flag?                             │
> + *   │          │                                    │
> + *   │          n                                    │
> + *   │          │                                    │
> + *   │          v                                    │
> + *   │         done                                  │
> + *   │                                               │
> + *   │                                               │
> + *   │         XFS_DAS_FOUND_LBLK <──────────────────┘
> + *   │                  │
> + *   │                  V
> + *   │        xfs_attr_leaf_addname()
> + *   │                  │
> + *   │                  v
> + *   │      ┌──first time through?
> + *   │      │          │
> + *   │      │          y
> + *   │      │          │
> + *   │      n          v
> + *   │      │    if we have rmt blks
> + *   │      │    find space for them
> + *   │      │          │
> + *   │      └──────────┤
> + *   │                 │
> + *   │                 v
> + *   │            still have
> + *   │      ┌─n─ blks to alloc? <──???
> + *   │      │          │           │
> + *   │      │          y           │
> + *   │      │          │           │
> + *   │      │          v           │
> + *   │      │     alloc one blk    │
> + *   │      │     return -EAGAIN ──┘
> + *   │      │    re-enter with one
> + *   │      │    less blk to alloc
> + *   │      │
> + *   │      │
> + *   │      └───> set the rmt
> + *   │               value
> + *   │                 │
> + *   │                 v
> + *   │               was this
> + *   │              a rename? ──n─???
> + *   │                 │          │
> + *   │                 y          │
> + *   │                 │          │
> + *   │                 v          │
> + *   │           flip incomplete  │
> + *   │               flag         │
> + *   │                 │          │
> + *   │                 v          │
> + *   │         XFS_DAS_FLIP_LFLAG │
> + *   │                 │          │
> + *   │                 v          │
> + *   │               remove       │
> + *   │        ┌───> old name      │
> + *   │        │        │          │
> + *   │ XFS_DAS_RM_LBLK │          │
> + *   │        ^        │          │
> + *   │        │        v          │
> + *   │        └──y── more to      │
> + *   │               remove       │
> + *   │                 │          │
> + *   │                 n          │
> + *   │                 │          │
> + *   │                 v          │
> + *   │                done <──────┘
> + *   │
> + *   └──────> XFS_DAS_FOUND_NBLK
> + *                     │
> + *                     v
> + *       ┌─────n──  need to
> + *       │        alloc blks?
> + *       │             │
> + *       │             y
> + *       │             │
> + *       │             v
> + *       │        find space
> + *       │             │
> + *       │             v
> + *       │  ┌─>XFS_DAS_ALLOC_NODE
> + *       │  │          │
> + *       │  │          v
> + *       │  │      alloc blk
> + *       │  │          │
> + *       │  │          v
> + *       │  └──y── need to alloc
> + *       │         more blocks?
> + *       │             │
> + *       │             n
> + *       │             │
> + *       │             v
> + *       │      set the rmt value
> + *       │             │
> + *       │             v
> + *       │          was this
> + *       └────────> a rename? ──n─???
> + *                     │          │
> + *                     y          │
> + *                     │          │
> + *                     v          │
> + *               flip incomplete  │
> + *                   flag         │
> + *                     │          │
> + *                     v          │
> + *             XFS_DAS_FLIP_NFLAG │
> + *                     │          │
> + *                     v          │
> + *                   remove       │
> + *        ┌────────> old name     │
> + *        │            │          │
> + *  XFS_DAS_RM_NBLK    │          │
> + *        ^            │          │
> + *        │            v          │
> + *        └──────y── more to      │
> + *                   remove       │
> + *                     │          │
> + *                     n          │
> + *                     │          │
> + *                     v          │
> + *                    done <──────┘
> + *
>   */
>  
>  /*
> @@ -174,12 +401,20 @@ enum xfs_delattr_state {
>  	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
>  	XFS_DAS_RMTBLK,		      /* Removing remote blks */
>  	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
> +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
> +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
> +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
> +	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 */
>  };
>  
>  /*
>   * Defines for xfs_delattr_context.flags
>   */
>  #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
> +#define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
>  
>  /*
>   * Context used for keeping track of delayed attribute operations
> @@ -187,6 +422,11 @@ enum xfs_delattr_state {
>  struct xfs_delattr_context {
>  	struct xfs_da_args      *da_args;
>  
> +	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
> +	struct xfs_bmbt_irec	map;
> +	xfs_dablk_t		lblkno;
> +	int			blkcnt;
> +
>  	/* Used in xfs_attr_node_removename to roll through removing blocks */
>  	struct xfs_da_state     *da_state;
>  
> @@ -213,7 +453,6 @@ 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);
> -int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
>  bool xfs_attr_namecheck(const void *name, size_t length);
>  void xfs_delattr_context_init(struct xfs_delattr_context *dac,
>  			      struct xfs_da_args *args);
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index 908521e7..fc71f10 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -439,9 +439,9 @@ xfs_attr_rmtval_get(
>  
>  /*
>   * Find a "hole" in the attribute address space large enough for us to drop the
> - * new attribute's value into
> + * new attributes value into
>   */
> -STATIC int
> +int
>  xfs_attr_rmt_find_hole(
>  	struct xfs_da_args	*args)
>  {
> @@ -468,7 +468,7 @@ xfs_attr_rmt_find_hole(
>  	return 0;
>  }
>  
> -STATIC int
> +int
>  xfs_attr_rmtval_set_value(
>  	struct xfs_da_args	*args)
>  {
> @@ -628,6 +628,69 @@ 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_find_space(
> +	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->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 0;
> +}
> +
> +/*
> + * 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.
>   */
> @@ -669,37 +732,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)
> -{
> -	int				error;
> -	struct xfs_delattr_context	dac  = {
> -		.da_args	= args,
> -	};
> -
> -	trace_xfs_attr_rmtval_remove(args);
> -
> -	/*
> -	 * Keep de-allocating extents until the remote-value region is gone.
> -	 */
> -	do {
> -		error = __xfs_attr_rmtval_remove(&dac);
> -		if (error != -EAGAIN)
> -			break;
> -
> -		error = xfs_attr_trans_roll(&dac);
> -		if (error)
> -			return error;
> -	} while (true);
> -
> -	return error;
> -}
> -
> -/*
>   * 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 re-call the function
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> index 002fd30..8ad68d5 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.h
> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> @@ -10,9 +10,12 @@ int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
>  
>  int xfs_attr_rmtval_get(struct xfs_da_args *args);
>  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);
>  int __xfs_attr_rmtval_remove(struct xfs_delattr_context *dac);
> +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_find_space(struct xfs_delattr_context *dac);
>  #endif /* __XFS_ATTR_REMOTE_H__ */
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index e74bbb6..0c16d46 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -1944,7 +1944,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] 44+ messages in thread

* Re: [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-03-26  0:33 ` [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
@ 2021-04-02  4:05   ` Chandan Babu R
  2021-04-02  9:50     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-04-02  4:05 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch separates the first half of xfs_attr_node_addname into a
> helper function xfs_attr_node_addname_find_attr.  It also replaces the
> restart goto with with an EAGAIN return code driven by a loop in the
> calling function.  This looks odd now, but will clean up nicly once we
> introduce the state machine.  It will also enable hoisting the last
> state out of xfs_attr_node_addname with out having to plumb in a "done"
> parameter to know if we need to move to the next state or not.
>

Looks good to me.

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

> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 86 ++++++++++++++++++++++++++++++------------------
>  1 file changed, 54 insertions(+), 32 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 531ff56..16159f6 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -52,7 +52,10 @@ 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_da_args *args,
> +				 struct xfs_da_state *state);
> +STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
> +				 struct xfs_da_state **state);
>  STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
>  STATIC int xfs_attr_node_addname_clear_incomplete(struct xfs_da_args *args);
>  STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
> @@ -267,6 +270,7 @@ xfs_attr_set_args(
>  	struct xfs_da_args	*args)
>  {
>  	struct xfs_inode	*dp = args->dp;
> +	struct xfs_da_state     *state;
>  	int			error;
>  
>  	/*
> @@ -312,7 +316,14 @@ xfs_attr_set_args(
>  			return error;
>  	}
>  
> -	return xfs_attr_node_addname(args);
> +	do {
> +		error = xfs_attr_node_addname_find_attr(args, &state);
> +		if (error)
> +			return error;
> +		error = xfs_attr_node_addname(args, state);
> +	} while (error == -EAGAIN);
> +
> +	return error;
>  }
>  
>  /*
> @@ -885,47 +896,26 @@ xfs_attr_node_hasname(
>   * External routines when attribute list size > geo->blksize
>   *========================================================================*/
>  
> -/*
> - * Add a name to a Btree-format attribute list.
> - *
> - * This will involve walking down the Btree, and may involve splitting
> - * leaf nodes and even splitting intermediate nodes up to and including
> - * the root node (a special case of an intermediate node).
> - *
> - * "Remote" attribute values confuse the issue and atomic rename operations
> - * add a whole extra layer of confusion on top of that.
> - */
>  STATIC int
> -xfs_attr_node_addname(
> -	struct xfs_da_args	*args)
> +xfs_attr_node_addname_find_attr(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state     **state)
>  {
> -	struct xfs_da_state	*state;
> -	struct xfs_da_state_blk	*blk;
> -	struct xfs_inode	*dp;
> -	int			retval, error;
> -
> -	trace_xfs_attr_node_addname(args);
> +	int			retval;
>  
>  	/*
> -	 * Fill in bucket of arguments/results/context to carry around.
> -	 */
> -	dp = args->dp;
> -restart:
> -	/*
>  	 * Search to see if name already exists, and get back a pointer
>  	 * to where it should go.
>  	 */
> -	retval = xfs_attr_node_hasname(args, &state);
> +	retval = xfs_attr_node_hasname(args, state);
>  	if (retval != -ENOATTR && retval != -EEXIST)
> -		goto out;
> +		goto error;
>  
> -	blk = &state->path.blk[ state->path.active-1 ];
> -	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>  	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
> -		goto out;
> +		goto error;
>  	if (retval == -EEXIST) {
>  		if (args->attr_flags & XATTR_CREATE)
> -			goto out;
> +			goto error;
>  
>  		trace_xfs_attr_node_replace(args);
>  
> @@ -943,6 +933,38 @@ xfs_attr_node_addname(
>  		args->rmtvaluelen = 0;
>  	}
>  
> +	return 0;
> +error:
> +	if (*state)
> +		xfs_da_state_free(*state);
> +	return retval;
> +}
> +
> +/*
> + * Add a name to a Btree-format attribute list.
> + *
> + * This will involve walking down the Btree, and may involve splitting
> + * leaf nodes and even splitting intermediate nodes up to and including
> + * the root node (a special case of an intermediate node).
> + *
> + * "Remote" attribute values confuse the issue and atomic rename operations
> + * add a whole extra layer of confusion on top of that.
> + */
> +STATIC int
> +xfs_attr_node_addname(
> +	struct xfs_da_args	*args,
> +	struct xfs_da_state	*state)
> +{
> +	struct xfs_da_state_blk	*blk;
> +	struct xfs_inode	*dp;
> +	int			retval, error;
> +
> +	trace_xfs_attr_node_addname(args);
> +
> +	dp = args->dp;
> +	blk = &state->path.blk[state->path.active-1];
> +	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> +
>  	retval = xfs_attr3_leaf_add(blk->bp, state->args);
>  	if (retval == -ENOSPC) {
>  		if (state->path.active == 1) {
> @@ -968,7 +990,7 @@ xfs_attr_node_addname(
>  			if (error)
>  				goto out;
>  
> -			goto restart;
> +			return -EAGAIN;
>  		}
>  
>  		/*


-- 
chandan

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

* Re: [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname
  2021-03-26  0:33 ` [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname Allison Henderson
@ 2021-04-02  4:26   ` Chandan Babu R
  2021-04-02  9:01     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-04-02  4:26 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch hoists the later half of xfs_attr_node_addname into
> the calling function.  We do this because it is this area that
> will need the most state management, and we want to keep such
> code in the same scope as much as possible
>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 161 +++++++++++++++++++++++------------------------
>  1 file changed, 78 insertions(+), 83 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 16159f6..5b5410f 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -52,6 +52,7 @@ 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 void xfs_attr_restore_rmt_blk(struct xfs_da_args *args);
>  STATIC int xfs_attr_node_addname(struct xfs_da_args *args,
>  				 struct xfs_da_state *state);
>  STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
> @@ -270,8 +271,8 @@ xfs_attr_set_args(
>  	struct xfs_da_args	*args)
>  {
>  	struct xfs_inode	*dp = args->dp;
> -	struct xfs_da_state     *state;
> -	int			error;
> +	struct xfs_da_state     *state = NULL;
> +	int			error = 0;
>
>  	/*
>  	 * If the attribute list is already in leaf format, jump straight to
> @@ -322,8 +323,79 @@ xfs_attr_set_args(
>  			return error;
>  		error = xfs_attr_node_addname(args, state);
>  	} while (error == -EAGAIN);
> +	if (error)
> +		return error;

Memory pointed to by 'state' is leaked if the call to either xfs_da3_split()
or xfs_defer_finish() inside xfs_attr_node_addname() return an error.

--
chandan

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

* Re: [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname
  2021-03-26  0:33 ` [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
  2021-04-01 15:43   ` Brian Foster
@ 2021-04-02  4:40   ` Chandan Babu R
  2021-04-02  9:50     ` Allison Henderson
  1 sibling, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-04-02  4:40 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch hoists xfs_attr_leaf_addname into the calling function.  The
> goal being to get all the code that will require state management into
> the same scope. This isn't particuarly aesthetic right away, but it is a
> preliminary step to merging in the state machine code.

The changes look good to me.

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

>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>

-- 
chandan

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

* Re: [PATCH v16 09/11] xfs: Hoist node transaction handling
  2021-03-26  0:33 ` [PATCH v16 09/11] xfs: Hoist node transaction handling Allison Henderson
@ 2021-04-02  5:04   ` Chandan Babu R
  2021-04-02  9:51     ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-04-02  5:04 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson wrote:
> This patch basically hoists the node transaction handling around the
> leaf code we just hoisted.  This will helps setup this area for the
> state machine since the goto is easily replaced with a state since it
> ends with a transaction roll.
>

Looks good to me.

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

> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>

-- 
chandan

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

* Re: [PATCH v16 10/11] xfs: Add delay ready attr remove routines
  2021-03-26  0:33 ` [PATCH v16 10/11] xfs: Add delay ready attr remove routines Allison Henderson
  2021-04-01 16:55   ` Brian Foster
@ 2021-04-02  7:59   ` Chandan Babu R
  2021-04-02  9:45     ` Allison Henderson
  1 sibling, 1 reply; 44+ messages in thread
From: Chandan Babu R @ 2021-04-02  7:59 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On 26 Mar 2021 at 06:03, Allison Henderson 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 is merged with
> xfs_attr_node_removename become a new function, xfs_attr_remove_iter.
> This new version uses a sort of state machine like switch to keep track
> of where it was when EAGAIN was returned. A new version of
> xfs_attr_remove_args consists of a simple loop to refresh the
> transaction until the operation is completed. A new XFS_DAC_DEFER_FINISH
> flag is used to finish the transaction where ever the existing code used
> to.
>
> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> version __xfs_attr_rmtval_remove. We will rename
> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> done.
>
> xfs_attr_rmtval_remove itself is still in use by the set routines (used
> during a rename).  For reasons of preserving existing function, we
> modify xfs_attr_rmtval_remove to call xfs_defer_finish when the flag is
> set.  Similar to how xfs_attr_remove_args does here.  Once we transition
> the set routines to be delay ready, xfs_attr_rmtval_remove is no longer
> used and will be removed.
>
> 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.  See xfs_attr.h for a more
> detailed diagram of the states.
>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c        | 206 +++++++++++++++++++++++++++-------------
>  fs/xfs/libxfs/xfs_attr.h        | 125 ++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
>  fs/xfs/libxfs/xfs_attr_remote.c |  48 ++++++----
>  fs/xfs/libxfs/xfs_attr_remote.h |   2 +-

[...]

>  STATIC
>  int xfs_attr_node_removename_setup(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state	**state)
> +	struct xfs_delattr_context	*dac)
>  {
> -	int			error;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_da_state		**state = &dac->da_state;
> +	int				error;
>
>  	error = xfs_attr_node_hasname(args, state);
>  	if (error != -EEXIST)
>  		return error;
> +	error = 0;
>
>  	ASSERT((*state)->path.blk[(*state)->path.active - 1].bp != NULL);
>  	ASSERT((*state)->path.blk[(*state)->path.active - 1].magic ==
> @@ -1204,10 +1233,13 @@ int xfs_attr_node_removename_setup(
>  	if (args->rmtblkno > 0) {
>  		error = xfs_attr_leaf_mark_incomplete(args, *state);
>  		if (error)
> -			return error;
> +			goto out;
>
> -		return xfs_attr_rmtval_invalidate(args);
> +		error = xfs_attr_rmtval_invalidate(args);
>  	}
> +out:
> +	if (error)
> +		xfs_da_state_free(*state);
>
>  	return 0;

If the call to xfs_attr_rmtval_invalidate() returned a non-zero value, the
above change would cause xfs_attr_node_removename_setup() to incorrectly
return success.

>  }
> @@ -1232,70 +1264,114 @@ xfs_attr_node_remove_cleanup(
>  }
>

--
chandan

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

* Re: [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete
  2021-03-29 14:42   ` Chandan Babu R
@ 2021-04-02  9:00     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:00 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 3/29/21 7:42 AM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch separate xfs_attr_node_addname into two functions.  This will
>> help to make it easier to hoist parts of xfs_attr_node_addname that need
>> state management
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 20 ++++++++++++++++++++
>>   1 file changed, 20 insertions(+)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index d46324a..531ff56 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -54,6 +54,7 @@ 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_addname_clear_incomplete(struct xfs_da_args *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);
>> @@ -1061,6 +1062,25 @@ xfs_attr_node_addname(
>>   			return error;
>>   	}
>>
>> +	error = xfs_attr_node_addname_clear_incomplete(args);
>> +out:
>> +	if (state)
>> +		xfs_da_state_free(state);
>> +	if (error)
>> +		return error;
>> +	return retval;
> 
> Lets say the user is performing a xattr rename operation and the call to
> xfs_attr3_leaf_add() resulted in returning -ENOSPC. xfs_attr_node_addname()
> would later allocate a new leaf and insert the new instance of xattr
> name/value into this leaf. However, 'retval' will continue to have -ENOSPC as
> its value which is incorrectly returned by the above return statement.

ok, I think we pull out reval in patch 7, but probably a quick retval = 
error; assignment at the end of the -ENOSPC scope would be right for 
this patch.

Allison
> 
> --
> chandan
> 

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

* Re: [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname
  2021-04-01 15:43   ` Brian Foster
@ 2021-04-02  9:01     ` Allison Henderson
  2021-04-05 13:15       ` Brian Foster
  0 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:01 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/1/21 8:43 AM, Brian Foster wrote:
> On Thu, Mar 25, 2021 at 05:33:05PM -0700, Allison Henderson wrote:
>> This patch hoists xfs_attr_leaf_addname into the calling function.  The
>> goal being to get all the code that will require state management into
>> the same scope. This isn't particuarly aesthetic right away, but it is a
>> preliminary step to merging in the state machine code.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 209 ++++++++++++++++++++++-------------------------
>>   1 file changed, 96 insertions(+), 113 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 5b5410f..16f10ac 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -729,115 +821,6 @@ xfs_attr_leaf_try_add(
>>   	return retval;
>>   }
>>   
>> -
>> -/*
>> - * 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);
>> -
> 
> Did this tracepoint disappear for a reason?
I thought the trace made sense to mark the entry of this function, but 
then when hoisted, looked sort of out of place.  It certainly wouldn't 
hurt it to put it back if people prefer.  I don't see it used anywhere 
else, and I don't think the calling function has it's own trace scheme 
either?  Should I translate trace_xfs_attr_leaf_addname to
trace_xfs_attr_set_args and hoist it up?

Allison

> 
> Brian
> 
>> -	error = xfs_attr_leaf_try_add(args, bp);
>> -	if (error)
>> -		return error;
>> -
>> -	/*
>> -	 * 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;
>> -
>> -	/*
>> -	 * If there was an out-of-line value, allocate the blocks we
>> -	 * identified for its storage and copy the value.  This is done
>> -	 * after we create 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_rmtval_set(args);
>> -		if (error)
>> -			return error;
>> -	}
>> -
>> -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>> -		/*
>> -		 * Added a "remote" value, just clear the incomplete flag.
>> -		 */
>> -		if (args->rmtblkno > 0)
>> -			error = xfs_attr3_leaf_clearflag(args);
>> -
>> -		return error;
>> -	}
>> -
>> -	/*
>> -	 * If this is an atomic rename operation, we must "flip" the incomplete
>> -	 * flags on the "new" and "old" attribute/value pairs so that one
>> -	 * disappears and one appears atomically.  Then we must remove the "old"
>> -	 * attribute/value pair.
>> -	 *
>> -	 * In a separate transaction, set the incomplete flag on the "old" attr
>> -	 * and clear the incomplete flag on the "new" attr.
>> -	 */
>> -
>> -	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 a "remote" value
>> -	 * (if it exists).
>> -	 */
>> -	xfs_attr_restore_rmt_blk(args);
>> -
>> -	if (args->rmtblkno) {
>> -		error = xfs_attr_rmtval_invalidate(args);
>> -		if (error)
>> -			return error;
>> -
>> -		error = xfs_attr_rmtval_remove(args);
>> -		if (error)
>> -			return error;
>> -	}
>> -
>> -	/*
>> -	 * Read in the block containing the "old" attr, then remove the "old"
>> -	 * attr from that block (neat, huh!)
>> -	 */
>> -	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
>> -				   &bp);
>> -	if (error)
>> -		return error;
>> -
>> -	xfs_attr3_leaf_remove(bp, args);
>> -
>> -	/*
>> -	 * If the result is small enough, shrink it all into the inode.
>> -	 */
>> -	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 */
>> -
>> -	return error;
>> -}
>> -
>>   /*
>>    * Return EEXIST if attr is found, or ENOATTR if not
>>    */
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname
  2021-04-02  4:26   ` Chandan Babu R
@ 2021-04-02  9:01     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:01 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 4/1/21 9:26 PM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch hoists the later half of xfs_attr_node_addname into
>> the calling function.  We do this because it is this area that
>> will need the most state management, and we want to keep such
>> code in the same scope as much as possible
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 161 +++++++++++++++++++++++------------------------
>>   1 file changed, 78 insertions(+), 83 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 16159f6..5b5410f 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -52,6 +52,7 @@ 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 void xfs_attr_restore_rmt_blk(struct xfs_da_args *args);
>>   STATIC int xfs_attr_node_addname(struct xfs_da_args *args,
>>   				 struct xfs_da_state *state);
>>   STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
>> @@ -270,8 +271,8 @@ xfs_attr_set_args(
>>   	struct xfs_da_args	*args)
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>> -	struct xfs_da_state     *state;
>> -	int			error;
>> +	struct xfs_da_state     *state = NULL;
>> +	int			error = 0;
>>
>>   	/*
>>   	 * If the attribute list is already in leaf format, jump straight to
>> @@ -322,8 +323,79 @@ xfs_attr_set_args(
>>   			return error;
>>   		error = xfs_attr_node_addname(args, state);
>>   	} while (error == -EAGAIN);
>> +	if (error)
>> +		return error;
> 
> Memory pointed to by 'state' is leaked if the call to either xfs_da3_split()
> or xfs_defer_finish() inside xfs_attr_node_addname() return an error.
Ok, we pulled it out because Darrick had run into a double free on his 
set up, but I think maybe it makes more sense to keep it here and set 
the pointer to null if it is freed.  Thx!

Allison

> 
> --
> chandan
> 

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

* Re: [PATCH v16 11/11] xfs: Add delay ready attr set routines
  2021-04-01 16:57   ` Brian Foster
@ 2021-04-02  9:01     ` Allison Henderson
  2021-04-05 13:17       ` Brian Foster
  0 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:01 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/1/21 9:57 AM, Brian Foster wrote:
> On Thu, Mar 25, 2021 at 05:33:08PM -0700, Allison Henderson 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. See xfs_attr.h for a more detailed diagram of the states.
>>
>> Two new helper functions have been added: xfs_attr_rmtval_find_space 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c        | 446 ++++++++++++++++++++++++----------------
>>   fs/xfs/libxfs/xfs_attr.h        | 241 +++++++++++++++++++++-
>>   fs/xfs/libxfs/xfs_attr_remote.c | 100 ++++++---
>>   fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
>>   fs/xfs/xfs_trace.h              |   1 -
>>   5 files changed, 582 insertions(+), 211 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4a73691..6a86b62 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -246,29 +247,55 @@ xfs_attr_trans_roll(
>>   	return error;
>>   }
>>   
>> +/*
>> + * Set the attribute specified in @args.
>> + */
>> +int
>> +xfs_attr_set_args(
>> +	struct xfs_da_args		*args)
>> +{
>> +	struct xfs_buf			*leaf_bp = NULL;
>> +	int				error = 0;
>> +	struct xfs_delattr_context	dac = {
>> +		.da_args	= args,
>> +	};
>> +
>> +	do {
>> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
>> +		if (error != -EAGAIN)
>> +			break;
>> +
>> +		error = xfs_attr_trans_roll(&dac);
>> +		if (error)
>> +			return error;
>> +	} while (true);
>> +
>> +	return error;
>> +}
>> +
>>   STATIC int
>>   xfs_attr_set_fmt(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_buf			**leaf_bp)
>>   {
>> -	struct xfs_buf          *leaf_bp = NULL;
>> -	struct xfs_inode	*dp = args->dp;
>> -	int			error2, error = 0;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>> +	int				error = 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;
>> -	}
>> +
>> +	/* Should only be 0, -EEXIST or -ENOSPC */
>> +	if (error != -ENOSPC)
>> +		return error;
> 
> Ok, so it looks like the commit that goes away here is replaced by one
> up the call stack.
> 
>>   
>>   	/*
>>   	 * 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);
>> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
>>   	if (error)
>>   		return error;
>>   
>> @@ -277,95 +304,139 @@ xfs_attr_set_fmt(
>>   	 * concurrent AIL push cannot grab the half-baked leaf buffer
>>   	 * and run into problems with the write verifier.
>>   	 */
>> -	xfs_trans_bhold(args->trans, leaf_bp);
>> -	error = xfs_defer_finish(&args->trans);
>> -	xfs_trans_bhold_release(args->trans, leaf_bp);
>> -	if (error) {
>> -		xfs_trans_brelse(args->trans, leaf_bp);
> 
> What about the xfs_trans_brelse() in the error case that seems to
> disappear?
Hmm, maybe we could add some handler in xfs_attr_set_args such that if 
the roll fails, we release the leaf_bp if it is not null?
> 
>> -		return error;
>> -	}
>> +	xfs_trans_bhold(args->trans, *leaf_bp);
>>   
>> +	/*
>> +	 * We're still in XFS_DAS_UNINIT state here.  We've converted
>> +	 * the attr fork to leaf format and will restart with the leaf
>> +	 * add.
>> +	 */
>> +	dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	return -EAGAIN;
>>   }
>>   
>>   /*
>>    * Set the attribute specified in @args.
>> + * This routine is meant to function as a delayed operation, and may return
>> + * -EAGAIN when the transaction needs to be rolled.  Calling functions will need
>> + * to handle this, and recall the function until a successful error code is
>> + * returned.
>>    */
>>   int
>> -xfs_attr_set_args(
>> -	struct xfs_da_args	*args)
>> +xfs_attr_set_iter(
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_buf			**leaf_bp)
>>   {
>> -	struct xfs_inode	*dp = args->dp;
>> -	struct xfs_buf		*bp = NULL;
>> -	struct xfs_da_state     *state = NULL;
>> -	int			forkoff, error = 0;
>> +	struct xfs_da_args              *args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>> +	struct xfs_buf			*bp = NULL;
>> +	struct xfs_da_state		*state = NULL;
>> +	int				forkoff, error = 0;
>>   
>> -	/*
>> -	 * 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 (xfs_attr_is_shortform(dp)) {
>> -		error = xfs_attr_set_fmt(args);
>> -		if (error != -EAGAIN)
>> -			return error;
>> -	}
>> +	/* State machine switch */
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_UNINIT:
>> +		if (xfs_attr_is_shortform(dp))
>> +			return xfs_attr_set_fmt(dac, leaf_bp);
>>   
>> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> -		error = xfs_attr_leaf_try_add(args, bp);
>> -		if (error == -ENOSPC) {
>> -			/*
>> -			 * Promote the attribute list to the Btree format.
>> -			 */
>> -			error = xfs_attr3_leaf_to_node(args);
>> +		/*
>> +		 * After a shortform to leaf conversion, we need to hold the
>> +		 * leaf and cycle out the transaction.  When we get back,
>> +		 * we need to release the leaf to release the hold on the leaf
>> +		 * buffer.
>> +		 */
>> +		if (*leaf_bp != NULL) {
>> +			xfs_trans_bhold_release(args->trans, *leaf_bp);
>> +			*leaf_bp = NULL;
>> +		}
>> +
>> +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> +			error = xfs_attr_leaf_try_add(args, *leaf_bp);
>> +			if (error == -ENOSPC) {
>> +				/*
>> +				 * Promote the attribute list to the Btree
>> +				 * format.
>> +				 */
>> +				error = xfs_attr3_leaf_to_node(args);
>> +				if (error)
>> +					return error;
>> +
>> +				/*
>> +				 * Finish any deferred work items and roll the
>> +				 * transaction once more.  The goal here is to
>> +				 * call node_addname with the inode and
>> +				 * transaction in the same state (inode locked
>> +				 * and joined, transaction clean) no matter how
>> +				 * we got to this step.
>> +				 *
>> +				 * At this point, we are still in
>> +				 * XFS_DAS_UNINIT, but when we come back, we'll
>> +				 * be a node, so we'll fall down into the node
>> +				 * handling code below
>> +				 */
>> +				dac->flags |= XFS_DAC_DEFER_FINISH;
>> +				return -EAGAIN;
>> +			}
>> +			else if (error)
> 
> Nit:			} else if (error)
> 
Sure, will fix

>> +				return error;
>> +		}
>> +		else {
> 
> Nit:		} else {
will fix

> 
>> +			error = xfs_attr_node_addname_find_attr(dac);
>>   			if (error)
>>   				return error;
>>   
>> -			/*
>> -			 * Finish any deferred work items and roll the transaction once
>> -			 * more.  The goal here is to call node_addname with the inode
>> -			 * and transaction in the same state (inode locked and joined,
>> -			 * transaction clean) no matter how we got to this step.
>> -			 */
>> -			error = xfs_defer_finish(&args->trans);
>> +			error = xfs_attr_node_addname(dac);
>>   			if (error)
>>   				return error;
> 
> Ok, so these couple of node calls get peeled out of the loop that
> existed prior to this patch and xfs_attr_node_addname() returns -EAGAIN
> to trigger reentry, if necessary.
Right, that is the idea

> 
>>   
>>   			/*
>> -			 * Commit the current trans (including the inode) and
>> -			 * start a new one.
>> +			 * If addname was successful, and we dont need to alloc
>> +			 * anymore blks, we're done.
>>   			 */
>> -			error = xfs_trans_roll_inode(&args->trans, dp);
>> -			if (error)
>> +			if (!args->rmtblkno && !args->rmtblkno2)
>>   				return error;
> 
> Is this check new? What about clearing flags and whatnot?
gosh, I remember putting this in, and now I'm struggling to remember the 
why that lead to it.  It's needed later in the set, but at this point 
its sort of an optimization.  In the xfs_attr_leaf_try_add, in the case 
of a rename, the blocks are saved for later processing.  If no blocks 
were saved, there's nothing to flip, so the idea is we can stop here. 
The extra go around isnt a big deal at this point, but it's a problem in 
delayed attrs, because you end up with an extra empty log entry.  in the 
extended set, we fail an assertion with out it:
Assertion failed: !list_empty(&cil->xc_cil), file: fs/xfs/xfs_log_cil.c, 
line: 907

On my set up, we seem to get away without it at this point in the set, 
but it does have to go in eventually.  I think I must have worked 
through this bug at one time, and then placed the fix in this patch, as 
it seems to be a requirement for becoming "delay ready"



> 
>>   
>> -			goto node;
>> -		}
>> -		else if (error) {
>> -			return error;
>> +			dac->dela_state = XFS_DAS_FOUND_NBLK;
>> +			return -EAGAIN;
>>   		}
>>   
>> -		/*
>> -		 * 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;
>> +		dac->dela_state = XFS_DAS_FOUND_LBLK;
>> +		return -EAGAIN;
> 
> Is there a reason the node branch sets the state in the branch but the
> leaf branch falls out to here? I.e., could we not be consistent and have
> 
> 	if (one_block()) {
> 		...
> 		dac->dela_state = XFS_DAS_FOUND_LBLK;
> 	} else {
> 		...
> 		dac->dela_state = XFS_DAS_FOUND_NBLK;
> 	}
> 	
> 	return -EAGAIN;
It's just sort of left over from its originally linear code flow that 
just sort of fell through in the the leaf logic. I think what you are 
proposing is logically analogous tho.  Does your example mean to add an 
extra if/else at the end here?  Or to tuck the return up into the the 
existing if/else?  Both work, I am fine with either.

> 
>>   
>> +        case XFS_DAS_FOUND_LBLK:
>>   		/*
>>   		 * If there was an out-of-line value, allocate the blocks we
>>   		 * identified for its storage and copy the value.  This is done
>>   		 * after we create 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_rmtval_set(args);
>> +
>> +		/* Open coded xfs_attr_rmtval_set without trans handling */
>> +		if ((dac->flags & XFS_DAC_LEAF_ADDNAME_INIT) == 0) {
>> +			dac->flags |= XFS_DAC_LEAF_ADDNAME_INIT;
>> +			if (args->rmtblkno > 0) {
>> +				error = xfs_attr_rmtval_find_space(dac);
>> +				if (error)
>> +					return error;
>> +			}
>> +		}
>> +
>> +		/*
>> +		 * Roll through the "value", allocating blocks on disk as
>> +		 * required.
>> +		 */
>> +		if (dac->blkcnt > 0) {
>> +			error = xfs_attr_rmtval_set_blk(dac);
>>   			if (error)
>>   				return error;
>> +
>> +			return -EAGAIN;
>>   		}
>>   
>> +		error = xfs_attr_rmtval_set_value(args);
>> +		if (error)
>> +			return error;
>> +
>>   		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>>   			/*
>>   			 * Added a "remote" value, just clear the incomplete
>> @@ -394,22 +465,26 @@ xfs_attr_set_args(
>>   		 * 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;
>> +	case XFS_DAS_FLIP_LFLAG:
>>   		/*
>>   		 * Dismantle the "old" attribute/value pair by removing a
>>   		 * "remote" value (if it exists).
>>   		 */
>>   		xfs_attr_restore_rmt_blk(args);
>>   
>> -		if (args->rmtblkno) {
>> -			error = xfs_attr_rmtval_invalidate(args);
>> -			if (error)
>> -				return error;
>> +		error = xfs_attr_rmtval_invalidate(args);
>> +		if (error)
>> +			return error;
>> +
>> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
>> +		dac->dela_state = XFS_DAS_RM_LBLK;
>>   
>> -			error = xfs_attr_rmtval_remove(args);
>> +		/* fallthrough */
>> +	case XFS_DAS_RM_LBLK:
>> +		if (args->rmtblkno) {
>> +			error = __xfs_attr_rmtval_remove(dac);
>>   			if (error)
>>   				return error;
>>   		}
> 
> This looks like a similar scenario as before where
> xfs_attr_rmtval_remove() would have returned with a clean transaction
> after the final unmap, but here __xfs_attr_rmtval_remove() just returns
> 0 if done == 1. We probably need to roll one more time out of this
> branch and land in a subsequent state..?
I see, ok will add extra -EAGAIN here.


> 
>> @@ -434,91 +509,114 @@ xfs_attr_set_args(
>>   			/* bp is gone due to xfs_da_shrink_inode */
>>   
>>   		return error;
>> -	}
>> -node:
>>   
>> +	case XFS_DAS_FOUND_NBLK:
>> +		/*
>> +		 * If there was an out-of-line value, allocate the blocks we
>> +		 * identified for its storage and copy the value.  This is done
>> +		 * after we create the attribute so that we don't overflow the
>> +		 * maximum size of a transaction and/or hit a deadlock.
>> +		 */
>> +		if (args->rmtblkno > 0) {
>> +			/*
>> +			 * Open coded xfs_attr_rmtval_set without trans
>> +			 * handling
>> +			 */
>> +			error = xfs_attr_rmtval_find_space(dac);
>> +			if (error)
>> +				return error;
>>   
>> -	do {
>> -		error = xfs_attr_node_addname_find_attr(args, &state);
>> -		if (error)
>> -			return error;
>> -		error = xfs_attr_node_addname(args, state);
>> -	} while (error == -EAGAIN);
>> -	if (error)
>> -		return error;
>> +			/*
>> +			 * Roll through the "value", allocating blocks on disk
>> +			 * as required.  Set the state in case of -EAGAIN return
>> +			 * code
>> +			 */
>> +			dac->dela_state = XFS_DAS_ALLOC_NODE;
>> +		}
>>   
>> -	/*
>> -	 * 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;
>> +		/* fallthrough */
>> +	case XFS_DAS_ALLOC_NODE:
>> +		if (args->rmtblkno > 0) {
>> +			if (dac->blkcnt > 0) {
>> +				error = xfs_attr_rmtval_set_blk(dac);
>> +				if (error)
>> +					return error;
>>   
>> -	/*
>> -	 * If there was an out-of-line value, allocate the blocks we
>> -	 * identified for its storage and copy the value.  This is done
>> -	 * after we create 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_rmtval_set(args);
>> -		if (error)
>> -			return error;
>> -	}
>> +				return -EAGAIN;
>> +			}
>> +
>> +			error = xfs_attr_rmtval_set_value(args);
>> +			if (error)
>> +				return error;
>> +		}
>> +
>> +		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>> +			/*
>> +			 * Added a "remote" value, just clear the incomplete
>> +			 * flag.
>> +			 */
>> +			if (args->rmtblkno > 0)
>> +				error = xfs_attr3_leaf_clearflag(args);
>> +			goto out;
>> +		}
>>   
>> -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>>   		/*
>> -		 * Added a "remote" value, just clear the incomplete flag.
>> +		 * If this is an atomic rename operation, we must "flip" the
>> +		 * incomplete flags on the "new" and "old" attribute/value pairs
>> +		 * so that one disappears and one appears atomically.  Then we
>> +		 * must remove the "old" attribute/value pair.
>> +		 *
>> +		 * In a separate transaction, set the incomplete flag on the
>> +		 * "old" attr and clear the incomplete flag on the "new" attr.
>>   		 */
>> -		if (args->rmtblkno > 0)
>> -			error = xfs_attr3_leaf_clearflag(args);
>> -		goto out;
>> -	}
>> -
>> -	/*
>> -	 * If this is an atomic rename operation, we must "flip" the incomplete
>> -	 * flags on the "new" and "old" attribute/value pairs so that one
>> -	 * disappears and one appears atomically.  Then we must remove the "old"
>> -	 * attribute/value pair.
>> -	 *
>> -	 * In a separate transaction, set the incomplete flag on the "old" attr
>> -	 * and clear the incomplete flag on the "new" attr.
>> -	 */
>> -	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;
>> +		error = xfs_attr3_leaf_flipflags(args);
>> +		if (error)
>> +			goto out;
>> +		/*
>> +		 * Commit the flag value change and start the next trans in
>> +		 * series
>> +		 */
>> +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
>> +		return -EAGAIN;
>>   
>> -	/*
>> -	 * Dismantle the "old" attribute/value pair by removing a "remote" value
>> -	 * (if it exists).
>> -	 */
>> -	xfs_attr_restore_rmt_blk(args);
>> +	case XFS_DAS_FLIP_NFLAG:
>> +		/*
>> +		 * Dismantle the "old" attribute/value pair by removing a
>> +		 * "remote" value (if it exists).
>> +		 */
>> +		xfs_attr_restore_rmt_blk(args);
>>   
>> -	if (args->rmtblkno) {
>>   		error = xfs_attr_rmtval_invalidate(args);
>>   		if (error)
>>   			return error;
>>   
>> -		error = xfs_attr_rmtval_remove(args);
>> -		if (error)
>> -			return error;
>> -	}
>> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
>> +		dac->dela_state = XFS_DAS_RM_NBLK;
>> +
>> +		/* fallthrough */
>> +	case XFS_DAS_RM_NBLK:
>> +		if (args->rmtblkno) {
>> +			error = __xfs_attr_rmtval_remove(dac);
>> +			if (error)
>> +				return error;
>> +		}
> 
> Similar thing here with __xfs_attr_rmtval_remove()..?
Sure, will add one more -EAGAIN

> 
>> +
>> +		error = xfs_attr_node_addname_clear_incomplete(dac);
>>   
>> -	error = xfs_attr_node_addname_clear_incomplete(args);
>>   out:
>> -	if (state)
>> -		xfs_da_state_free(state);
>> -	return error;
>> +		if (state)
>> +			xfs_da_state_free(state);
>> +		return error;
> 
> Can we avoid this out label landing inside the switch statement? That
> looks like a landmine. Even if we just duplicated an 'done_out' path
> after the last return in the function, I think that would be preferable.
Sure, can do, that seems like a simple thing to tack on

> 
> All previous feedback aside, I think this patch now looks much more
> digestable in general. Most of the state code is isolated to the _iter()
> function and so it's much easier to follow along and compare against the
> current code flow. I did still have some thoughts with regard to further
> cleanups, possibly clearing up some the logic and/or tweaking the states
> and whatnot, but I think this is at a point where it might be reasonable
> to make such changes on top of this patch instead of continuing to make
> significant changes to it. If I get a chance perhaps I'll take a closer
> look at that once the remaining kinks are worked out..
> 
> Brian
Ok, I will get these last bits updated here.  I still need to check into 
the issues Darrick is seeing on his set up, but it sounds like we've 
found an arrangement people like.  And yes, I think switching to 
cleanups on top is a good next step.  Thanks for the reviews!

Allison

> 
>> +
>> +	default:
>> +		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
>> +		break;
>> +	}
>>   
>> +	return error;
>>   }
>>   
>> +
>>   /*
>>    * Return EEXIST if attr is found, or ENOATTR if not
>>    */
>> @@ -984,18 +1082,18 @@ xfs_attr_node_hasname(
>>   
>>   STATIC int
>>   xfs_attr_node_addname_find_attr(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state     **state)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	int			retval;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	int				retval;
>>   
>>   	/*
>>   	 * Search to see if name already exists, and get back a pointer
>>   	 * to where it should go.
>>   	 */
>> -	retval = xfs_attr_node_hasname(args, state);
>> +	retval = xfs_attr_node_hasname(args, &dac->da_state);
>>   	if (retval != -ENOATTR && retval != -EEXIST)
>> -		goto error;
>> +		return retval;
>>   
>>   	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>>   		goto error;
>> @@ -1021,8 +1119,8 @@ xfs_attr_node_addname_find_attr(
>>   
>>   	return 0;
>>   error:
>> -	if (*state)
>> -		xfs_da_state_free(*state);
>> +	if (dac->da_state)
>> +		xfs_da_state_free(dac->da_state);
>>   	return retval;
>>   }
>>   
>> @@ -1035,20 +1133,24 @@ xfs_attr_node_addname_find_attr(
>>    *
>>    * "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_da_state	*state)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	struct xfs_da_state_blk	*blk;
>> -	struct xfs_inode	*dp;
>> -	int			error;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_da_state		*state = dac->da_state;
>> +	struct xfs_da_state_blk		*blk;
>> +	int				error;
>>   
>>   	trace_xfs_attr_node_addname(args);
>>   
>> -	dp = args->dp;
>> -	blk = &state->path.blk[state->path.active-1];
>> +	blk = &state->path.blk[ state->path.active-1 ];
>>   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   
>>   	error = xfs_attr3_leaf_add(blk->bp, state->args);
>> @@ -1064,18 +1166,15 @@ 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.
>> +			 * Now that we have converted the leaf to a node, we can
>> +			 * roll the transaction, and try xfs_attr3_leaf_add
>> +			 * again on re-entry.  No need to set dela_state to do
>> +			 * this. dela_state is still unset by this function at
>> +			 * this point.
>>   			 */
>> -			error = xfs_trans_roll_inode(&args->trans, dp);
>> -			if (error)
>> -				goto out;
>> -
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>   			return -EAGAIN;
>>   		}
>>   
>> @@ -1088,9 +1187,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.
>> @@ -1105,8 +1202,9 @@ xfs_attr_node_addname(
>>   
>>   STATIC
>>   int xfs_attr_node_addname_clear_incomplete(
>> -	struct xfs_da_args		*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> +	struct xfs_da_args		*args = dac->da_args;
>>   	struct xfs_da_state		*state = NULL;
>>   	struct xfs_da_state_blk		*blk;
>>   	int				retval = 0;
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 92a6a50..4e4233d 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -159,6 +159,233 @@ struct xfs_attr_list_context {
>>    *              v
>>    *            done
>>    *
>> + *
>> + * Below is a state machine diagram for attr set operations.
>> + *
>> + * It seems the challenge with understanding this system comes from trying to
>> + * absorb the state machine all at once, when really one should only be looking
>> + * at it with in the context of a single function. Once a state sensitive
>> + * function is called, the idea is that it "takes ownership" of the
>> + * state machine. It isn't concerned with the states that may have belonged to
>> + * it's calling parent. Only the states relevant to itself or any other
>> + * subroutines there in. Once a calling function hands off the state machine to
>> + * a subroutine, it needs to respect the simple rule that it doesn't "own" the
>> + * state machine anymore, and it's the responsibility of that calling function
>> + * to propagate the -EAGAIN back up the call stack. Upon reentry, it is
>> + * committed to re-calling that subroutine until it returns something other than
>> + * -EAGAIN. Once that subroutine signals completion (by returning anything other
>> + * than -EAGAIN), the calling function can resume using the state machine.
>> + *
>> + *  xfs_attr_set_iter()
>> + *              │
>> + *              v
>> + *   ┌─y─ has an attr fork?
>> + *   │          |
>> + *   │          n
>> + *   │          |
>> + *   │          V
>> + *   │       add a fork
>> + *   │          │
>> + *   └──────────┤
>> + *              │
>> + *              V
>> + *   ┌─y─ is shortform?
>> + *   │          │
>> + *   │          V
>> + *   │   xfs_attr_set_fmt
>> + *   │          |
>> + *   │          V
>> + *   │ xfs_attr_try_sf_addname
>> + *   │          │
>> + *   │          V
>> + *   │      had enough ──y──> done
>> + *   │        space?
>> + *   n          │
>> + *   │          n
>> + *   │          │
>> + *   │          V
>> + *   │   transform to leaf
>> + *   │          │
>> + *   │          V
>> + *   │   hold the leaf buffer
>> + *   │          │
>> + *   │          V
>> + *   │     return -EAGAIN
>> + *   │      Re-enter in
>> + *   │       leaf form
>> + *   │
>> + *   └─> release leaf buffer
>> + *          if needed
>> + *              │
>> + *              V
>> + *   ┌───n── fork has
>> + *   │      only 1 blk?
>> + *   │          │
>> + *   │          y
>> + *   │          │
>> + *   │          v
>> + *   │ xfs_attr_leaf_try_add()
>> + *   │          │
>> + *   │          v
>> + *   │      had enough ──────────────y───────────────???
>> + *   │        space?                                 │
>> + *   │          │                                    │
>> + *   │          n                                    │
>> + *   │          │                                    │
>> + *   │          v                                    │
>> + *   │    return -EAGAIN                             │
>> + *   │      re-enter in                              │
>> + *   │        node form                              │
>> + *   │          │                                    │
>> + *   └──────────┤                                    │
>> + *              │                                    │
>> + *              V                                    │
>> + * xfs_attr_node_addname_find_attr                   │
>> + *        determines if this                         │
>> + *       is create or rename                         │
>> + *     find space to store attr                      │
>> + *              │                                    │
>> + *              v                                    │
>> + *     xfs_attr_node_addname                         │
>> + *              │                                    │
>> + *              v                                    │
>> + *   fits in a node leaf? ────n─────???                │
>> + *              │     ^             v                │
>> + *              │     │        single leaf node?     │
>> + *              │     │          │            │      │
>> + *              y     │          y            n      │
>> + *              │     │          │            │      │
>> + *              v     │          v            v      │
>> + *            update  │     grow the leaf  split if  │
>> + *           hashvals └─── return -EAGAIN   needed   │
>> + *              │          retry leaf add     │      │
>> + *              │            on reentry       │      │
>> + *              ├─────────────────────────────┘      │
>> + *              │                                    │
>> + *              v                                    │
>> + *         need to alloc                             │
>> + *   ┌─y── or flip flag?                             │
>> + *   │          │                                    │
>> + *   │          n                                    │
>> + *   │          │                                    │
>> + *   │          v                                    │
>> + *   │         done                                  │
>> + *   │                                               │
>> + *   │                                               │
>> + *   │         XFS_DAS_FOUND_LBLK <──────────────────┘
>> + *   │                  │
>> + *   │                  V
>> + *   │        xfs_attr_leaf_addname()
>> + *   │                  │
>> + *   │                  v
>> + *   │      ┌──first time through?
>> + *   │      │          │
>> + *   │      │          y
>> + *   │      │          │
>> + *   │      n          v
>> + *   │      │    if we have rmt blks
>> + *   │      │    find space for them
>> + *   │      │          │
>> + *   │      └──────────┤
>> + *   │                 │
>> + *   │                 v
>> + *   │            still have
>> + *   │      ┌─n─ blks to alloc? <──???
>> + *   │      │          │           │
>> + *   │      │          y           │
>> + *   │      │          │           │
>> + *   │      │          v           │
>> + *   │      │     alloc one blk    │
>> + *   │      │     return -EAGAIN ──┘
>> + *   │      │    re-enter with one
>> + *   │      │    less blk to alloc
>> + *   │      │
>> + *   │      │
>> + *   │      └───> set the rmt
>> + *   │               value
>> + *   │                 │
>> + *   │                 v
>> + *   │               was this
>> + *   │              a rename? ──n─???
>> + *   │                 │          │
>> + *   │                 y          │
>> + *   │                 │          │
>> + *   │                 v          │
>> + *   │           flip incomplete  │
>> + *   │               flag         │
>> + *   │                 │          │
>> + *   │                 v          │
>> + *   │         XFS_DAS_FLIP_LFLAG │
>> + *   │                 │          │
>> + *   │                 v          │
>> + *   │               remove       │
>> + *   │        ┌───> old name      │
>> + *   │        │        │          │
>> + *   │ XFS_DAS_RM_LBLK │          │
>> + *   │        ^        │          │
>> + *   │        │        v          │
>> + *   │        └──y── more to      │
>> + *   │               remove       │
>> + *   │                 │          │
>> + *   │                 n          │
>> + *   │                 │          │
>> + *   │                 v          │
>> + *   │                done <──────┘
>> + *   │
>> + *   └──────> XFS_DAS_FOUND_NBLK
>> + *                     │
>> + *                     v
>> + *       ┌─────n──  need to
>> + *       │        alloc blks?
>> + *       │             │
>> + *       │             y
>> + *       │             │
>> + *       │             v
>> + *       │        find space
>> + *       │             │
>> + *       │             v
>> + *       │  ┌─>XFS_DAS_ALLOC_NODE
>> + *       │  │          │
>> + *       │  │          v
>> + *       │  │      alloc blk
>> + *       │  │          │
>> + *       │  │          v
>> + *       │  └──y── need to alloc
>> + *       │         more blocks?
>> + *       │             │
>> + *       │             n
>> + *       │             │
>> + *       │             v
>> + *       │      set the rmt value
>> + *       │             │
>> + *       │             v
>> + *       │          was this
>> + *       └────────> a rename? ──n─???
>> + *                     │          │
>> + *                     y          │
>> + *                     │          │
>> + *                     v          │
>> + *               flip incomplete  │
>> + *                   flag         │
>> + *                     │          │
>> + *                     v          │
>> + *             XFS_DAS_FLIP_NFLAG │
>> + *                     │          │
>> + *                     v          │
>> + *                   remove       │
>> + *        ┌────────> old name     │
>> + *        │            │          │
>> + *  XFS_DAS_RM_NBLK    │          │
>> + *        ^            │          │
>> + *        │            v          │
>> + *        └──────y── more to      │
>> + *                   remove       │
>> + *                     │          │
>> + *                     n          │
>> + *                     │          │
>> + *                     v          │
>> + *                    done <──────┘
>> + *
>>    */
>>   
>>   /*
>> @@ -174,12 +401,20 @@ enum xfs_delattr_state {
>>   	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
>>   	XFS_DAS_RMTBLK,		      /* Removing remote blks */
>>   	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
>> +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
>> +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
>> +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
>> +	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 */
>>   };
>>   
>>   /*
>>    * Defines for xfs_delattr_context.flags
>>    */
>>   #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
>> +#define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
>>   
>>   /*
>>    * Context used for keeping track of delayed attribute operations
>> @@ -187,6 +422,11 @@ enum xfs_delattr_state {
>>   struct xfs_delattr_context {
>>   	struct xfs_da_args      *da_args;
>>   
>> +	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
>> +	struct xfs_bmbt_irec	map;
>> +	xfs_dablk_t		lblkno;
>> +	int			blkcnt;
>> +
>>   	/* Used in xfs_attr_node_removename to roll through removing blocks */
>>   	struct xfs_da_state     *da_state;
>>   
>> @@ -213,7 +453,6 @@ 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);
>> -int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
>>   bool xfs_attr_namecheck(const void *name, size_t length);
>>   void xfs_delattr_context_init(struct xfs_delattr_context *dac,
>>   			      struct xfs_da_args *args);
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index 908521e7..fc71f10 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>> @@ -439,9 +439,9 @@ xfs_attr_rmtval_get(
>>   
>>   /*
>>    * Find a "hole" in the attribute address space large enough for us to drop the
>> - * new attribute's value into
>> + * new attributes value into
>>    */
>> -STATIC int
>> +int
>>   xfs_attr_rmt_find_hole(
>>   	struct xfs_da_args	*args)
>>   {
>> @@ -468,7 +468,7 @@ xfs_attr_rmt_find_hole(
>>   	return 0;
>>   }
>>   
>> -STATIC int
>> +int
>>   xfs_attr_rmtval_set_value(
>>   	struct xfs_da_args	*args)
>>   {
>> @@ -628,6 +628,69 @@ 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_find_space(
>> +	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->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 0;
>> +}
>> +
>> +/*
>> + * 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.
>>    */
>> @@ -669,37 +732,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)
>> -{
>> -	int				error;
>> -	struct xfs_delattr_context	dac  = {
>> -		.da_args	= args,
>> -	};
>> -
>> -	trace_xfs_attr_rmtval_remove(args);
>> -
>> -	/*
>> -	 * Keep de-allocating extents until the remote-value region is gone.
>> -	 */
>> -	do {
>> -		error = __xfs_attr_rmtval_remove(&dac);
>> -		if (error != -EAGAIN)
>> -			break;
>> -
>> -		error = xfs_attr_trans_roll(&dac);
>> -		if (error)
>> -			return error;
>> -	} while (true);
>> -
>> -	return error;
>> -}
>> -
>> -/*
>>    * 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 re-call the function
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
>> index 002fd30..8ad68d5 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.h
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
>> @@ -10,9 +10,12 @@ int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
>>   
>>   int xfs_attr_rmtval_get(struct xfs_da_args *args);
>>   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);
>>   int __xfs_attr_rmtval_remove(struct xfs_delattr_context *dac);
>> +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_find_space(struct xfs_delattr_context *dac);
>>   #endif /* __XFS_ATTR_REMOTE_H__ */
>> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
>> index e74bbb6..0c16d46 100644
>> --- a/fs/xfs/xfs_trace.h
>> +++ b/fs/xfs/xfs_trace.h
>> @@ -1944,7 +1944,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] 44+ messages in thread

* Re: [PATCH v16 10/11] xfs: Add delay ready attr remove routines
  2021-04-01 16:55   ` Brian Foster
@ 2021-04-02  9:42     ` Allison Henderson
  2021-04-05 13:15       ` Brian Foster
  0 siblings, 1 reply; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:42 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/1/21 9:55 AM, Brian Foster wrote:
> On Thu, Mar 25, 2021 at 05:33:07PM -0700, Allison Henderson 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 is merged with
>> xfs_attr_node_removename become a new function, xfs_attr_remove_iter.
>> This new version uses a sort of state machine like switch to keep track
>> of where it was when EAGAIN was returned. A new version of
>> xfs_attr_remove_args consists of a simple loop to refresh the
>> transaction until the operation is completed. A new XFS_DAC_DEFER_FINISH
>> flag is used to finish the transaction where ever the existing code used
>> to.
>>
>> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
>> version __xfs_attr_rmtval_remove. We will rename
>> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
>> done.
>>
>> xfs_attr_rmtval_remove itself is still in use by the set routines (used
>> during a rename).  For reasons of preserving existing function, we
>> modify xfs_attr_rmtval_remove to call xfs_defer_finish when the flag is
>> set.  Similar to how xfs_attr_remove_args does here.  Once we transition
>> the set routines to be delay ready, xfs_attr_rmtval_remove is no longer
>> used and will be removed.
>>
>> 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.  See xfs_attr.h for a more
>> detailed diagram of the states.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c        | 206 +++++++++++++++++++++++++++-------------
>>   fs/xfs/libxfs/xfs_attr.h        | 125 ++++++++++++++++++++++++
>>   fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
>>   fs/xfs/libxfs/xfs_attr_remote.c |  48 ++++++----
>>   fs/xfs/libxfs/xfs_attr_remote.h |   2 +-
>>   fs/xfs/xfs_attr_inactive.c      |   2 +-
>>   6 files changed, 297 insertions(+), 88 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 41accd5..4a73691 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -221,6 +220,32 @@ xfs_attr_is_shortform(
>>   		ip->i_afp->if_nextents == 0);
>>   }
>>   
>> +/*
>> + * Checks to see if a delayed attribute transaction should be rolled.  If so,
>> + * also checks for a defer finish.  Transaction is finished and rolled as
>> + * needed, and returns true of false if the delayed operation should continue.
>> + */
> 
> Outdated comment wrt to the return value.
Ok, will drop last line here

> 
>> +int
>> +xfs_attr_trans_roll(
>> +	struct xfs_delattr_context	*dac)
>> +{
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	int				error;
>> +
>> +	if (dac->flags & XFS_DAC_DEFER_FINISH) {
>> +		/*
>> +		 * The caller wants us to finish all the deferred ops so that we
>> +		 * avoid pinning the log tail with a large number of deferred
>> +		 * ops.
>> +		 */
>> +		dac->flags &= ~XFS_DAC_DEFER_FINISH;
>> +		error = xfs_defer_finish(&args->trans);
>> +	} else
>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> +
>> +	return error;
>> +}
>> +
>>   STATIC int
>>   xfs_attr_set_fmt(
>>   	struct xfs_da_args	*args)
> ...
>> @@ -1232,70 +1264,114 @@ xfs_attr_node_remove_cleanup(
>>   }
>>   
>>   /*
>> - * Remove a name from a B-tree attribute list.
>> + * Remove the attribute specified in @args.
>>    *
>>    * 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 in-line 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)
>> +int
>> +xfs_attr_remove_iter(
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	struct xfs_da_state	*state;
>> -	int			retval, error;
>> -	struct xfs_inode	*dp = args->dp;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_da_state		*state = dac->da_state;
>> +	int				retval, error;
>> +	struct xfs_inode		*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>>   
>> -	error = xfs_attr_node_removename_setup(args, &state);
>> -	if (error)
>> -		goto out;
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_UNINIT:
>> +		if (!xfs_inode_hasattr(dp))
>> +			return -ENOATTR;
>>   
>> -	/*
>> -	 * 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_rmtval_remove(args);
>> -		if (error)
>> -			goto out;
>> +		if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
>> +			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
>> +			return xfs_attr_shortform_remove(args);
>> +		}
>> +
>> +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>> +			return xfs_attr_leaf_removename(args);
>> +
>> +	/* fallthrough */
>> +	case XFS_DAS_RMTBLK:
>> +		dac->dela_state = XFS_DAS_RMTBLK;
>> +
>> +		if (!dac->da_state) {
>> +			error = xfs_attr_node_removename_setup(dac);
>> +			if (error)
>> +				goto out;
> 
> Do we need the goto here if _removename_setup() frees state on error (or
> is the latter change necessary)?
I think we can safely return here.  Will update

> 
>> +		}
>> +		state = dac->da_state;
> 
> Also, can this fold into the above if (!da_state) branch? Or maybe the
> whole setup branch pulled up into the UNINIT state? Not a big deal, but
> it does look a little out of place in the RMTBLK state.
Sure, it should be ok, there isnt any EAGAINs here, so it shouldnt make 
a difference

> 
>>   
>>   		/*
>> -		 * Refill the state structure with buffers, the prior calls
>> -		 * released our buffers.
>> +		 * 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.
>>   		 */
>> -		error = xfs_attr_refillstate(state);
>> -		if (error)
>> -			goto out;
>> -	}
>> -	retval = xfs_attr_node_remove_cleanup(args, state);
>> +		if (args->rmtblkno > 0) {
>> +			/*
>> +			 * May return -EAGAIN. Remove blocks until
>> +			 * args->rmtblkno == 0
>> +			 */
>> +			error = __xfs_attr_rmtval_remove(dac);
>> +			if (error)
>> +				break;
> 
> I feel that the difference between a break and goto out might confuse
> some of the error handling. Right now, it looks like the exit path
> handles either scenario, so we could presumably do something like the
> following at the end of the function:
> 
> 	if (error != -EAGAIN && state)
> 		xfs_da_state_free(state);
> 	return error;
> 
> ... and just ditch the label. Alternatively we could retain the label above
> the state check, but just use it consistently throughout the function.
> 
Either will work?  I think I'd prefer the gotos over the breaks though, 
I just think it reads easier.  The switch is sort of big, so I think the 
gotos make it a little more clear in that we're exiting the function 
without having to skim all the way to the bottom.

> Other than those few nits, this one looks pretty good to me.
Great, will update.  Thanks!

Allison

> 
> Brian
> 
>> +
>> +			/*
>> +			 * Refill the state structure with buffers, the prior
>> +			 * calls released our buffers.
>> +			 */
>> +			ASSERT(args->rmtblkno == 0);
>> +			error = xfs_attr_refillstate(state);
>> +			if (error)
>> +				goto out;
>> +
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			return -EAGAIN;
>> +		}
>> +
>> +		retval = xfs_attr_node_remove_cleanup(args, state);
>>   
>> -	/*
>> -	 * Check to see if the tree needs to be collapsed.
>> -	 */
>> -	if (retval && (state->path.active > 1)) {
>> -		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.
>> +		 * Check to see if the tree needs to be collapsed. Set the flag
>> +		 * to indicate that the calling function needs to move the
>> +		 * shrink operation
>>   		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -		if (error)
>> -			goto out;
>> -	}
>> +		if (retval && (state->path.active > 1)) {
>> +			error = xfs_da3_join(state);
>> +			if (error)
>> +				goto out;
>>   
>> -	/*
>> -	 * 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);
>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			dac->dela_state = XFS_DAS_RM_SHRINK;
>> +			return -EAGAIN;
>> +		}
>> +
>> +		/* fallthrough */
>> +	case 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);
>> +
>> +		break;
>> +	default:
>> +		ASSERT(0);
>> +		error = -EINVAL;
>> +		goto out;
>> +	}
>>   
>> +	if (error == -EAGAIN)
>> +		return error;
>>   out:
>>   	if (state)
>>   		xfs_da_state_free(state);
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 3e97a93..92a6a50 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -74,6 +74,127 @@ struct xfs_attr_list_context {
>>   };
>>   
>>   
>> +/*
>> + * ========================================================================
>> + * Structure used to pass context around among the delayed routines.
>> + * ========================================================================
>> + */
>> +
>> +/*
>> + * 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()
>> + *              │
>> + *              v
>> + *        have attr to remove? ──n──> done
>> + *              │
>> + *              y
>> + *              │
>> + *              v
>> + *        are we short form? ──y──> xfs_attr_shortform_remove ──> done
>> + *              │
>> + *              n
>> + *              │
>> + *              V
>> + *        are we leaf form? ──y──> xfs_attr_leaf_removename ──> done
>> + *              │
>> + *              n
>> + *              │
>> + *              V
>> + *   ┌── need to setup state?
>> + *   │          │
>> + *   n          y
>> + *   │          │
>> + *   │          v
>> + *   │ find attr and get state
>> + *   │    attr has blks? ───n────???
>> + *   │          │                v
>> + *   │          │         find and invalidate
>> + *   │          y         the blocks. mark
>> + *   │          │         attr incomplete
>> + *   │          ├────────────────┘
>> + *   └──────────┤
>> + *              │
>> + *              v
>> + *      Have blks to remove? ─────y────???
>> + *              │       ^      remove the blks
>> + *              │       │              │
>> + *              │       │              v
>> + *              │       │        refill the state
>> + *              n       │              │
>> + *              │       │              v
>> + *              │       │         XFS_DAS_RMTBLK
>> + *              │       └─────  re-enter with one
>> + *              │               less blk to remove
>> + *              │
>> + *              v
>> + *       remove leaf and
>> + *       update hash with
>> + *   xfs_attr_node_remove_cleanup
>> + *              │
>> + *              v
>> + *           need to
>> + *        shrink tree? ─n─???
>> + *              │         │
>> + *              y         │
>> + *              │         │
>> + *              v         │
>> + *          join leaf     │
>> + *              │         │
>> + *              v         │
>> + *      XFS_DAS_RM_SHRINK │
>> + *              │         │
>> + *              v         │
>> + *       do the shrink    │
>> + *              │         │
>> + *              v         │
>> + *          free state <──┘
>> + *              │
>> + *              v
>> + *            done
>> + *
>> + */
>> +
>> +/*
>> + * 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 {
>> +	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
>> +	XFS_DAS_RMTBLK,		      /* Removing remote blks */
>> +	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
>> +};
>> +
>> +/*
>> + * Defines for xfs_delattr_context.flags
>> + */
>> +#define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
>> +
>> +/*
>> + * Context used for keeping track of delayed attribute operations
>> + */
>> +struct xfs_delattr_context {
>> +	struct xfs_da_args      *da_args;
>> +
>> +	/* Used in xfs_attr_node_removename to roll through removing blocks */
>> +	struct xfs_da_state     *da_state;
>> +
>> +	/* Used to keep track of current state of delayed operation */
>> +	unsigned int            flags;
>> +	enum xfs_delattr_state  dela_state;
>> +};
>> +
>>   /*========================================================================
>>    * Function prototypes for the kernel.
>>    *========================================================================*/
>> @@ -91,6 +212,10 @@ 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);
>> +int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
>>   bool xfs_attr_namecheck(const void *name, size_t length);
>> +void xfs_delattr_context_init(struct xfs_delattr_context *dac,
>> +			      struct xfs_da_args *args);
>>   
>>   #endif	/* __XFS_ATTR_H__ */
>> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
>> index d6ef69a..3780141 100644
>> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
>> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
>> @@ -19,8 +19,8 @@
>>   #include "xfs_bmap_btree.h"
>>   #include "xfs_bmap.h"
>>   #include "xfs_attr_sf.h"
>> -#include "xfs_attr_remote.h"
>>   #include "xfs_attr.h"
>> +#include "xfs_attr_remote.h"
>>   #include "xfs_attr_leaf.h"
>>   #include "xfs_error.h"
>>   #include "xfs_trace.h"
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index 48d8e9c..908521e7 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>> @@ -674,10 +674,12 @@ xfs_attr_rmtval_invalidate(
>>    */
>>   int
>>   xfs_attr_rmtval_remove(
>> -	struct xfs_da_args      *args)
>> +	struct xfs_da_args		*args)
>>   {
>> -	int			error;
>> -	int			retval;
>> +	int				error;
>> +	struct xfs_delattr_context	dac  = {
>> +		.da_args	= args,
>> +	};
>>   
>>   	trace_xfs_attr_rmtval_remove(args);
>>   
>> @@ -685,31 +687,29 @@ xfs_attr_rmtval_remove(
>>   	 * Keep de-allocating extents until the remote-value region is gone.
>>   	 */
>>   	do {
>> -		retval = __xfs_attr_rmtval_remove(args);
>> -		if (retval && retval != -EAGAIN)
>> -			return retval;
>> +		error = __xfs_attr_rmtval_remove(&dac);
>> +		if (error != -EAGAIN)
>> +			break;
>>   
>> -		/*
>> -		 * Close out trans and start the next one in the chain.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> +		error = xfs_attr_trans_roll(&dac);
>>   		if (error)
>>   			return error;
>> -	} while (retval == -EAGAIN);
>> +	} while (true);
>>   
>> -	return 0;
>> +	return error;
>>   }
>>   
>>   /*
>>    * 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
>> + * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the
>>    * transaction and re-call the function
>>    */
>>   int
>>   __xfs_attr_rmtval_remove(
>> -	struct xfs_da_args	*args)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	int			error, done;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	int				error, done;
>>   
>>   	/*
>>   	 * Unmap value blocks for this attr.
>> @@ -719,12 +719,20 @@ __xfs_attr_rmtval_remove(
>>   	if (error)
>>   		return error;
>>   
>> -	error = xfs_defer_finish(&args->trans);
>> -	if (error)
>> -		return error;
>> -
>> -	if (!done)
>> +	/*
>> +	 * We don't need an explicit state here to pick up where we left off. We
>> +	 * can figure it out using the !done return code. Calling function only
>> +	 * needs to keep recalling this routine until we indicate to stop by
>> +	 * returning anything other than -EAGAIN. The actual value of
>> +	 * attr->xattri_dela_state may be some value reminiscent of the calling
>> +	 * function, but it's value is irrelevant with in the context of this
>> +	 * function. Once we are done here, the next state is set as needed
>> +	 * by the parent
>> +	 */
>> +	if (!done) {
>> +		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   		return -EAGAIN;
>> +	}
>>   
>>   	return error;
>>   }
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
>> index 9eee615..002fd30 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.h
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
>> @@ -14,5 +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);
>> +int __xfs_attr_rmtval_remove(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 bfad669..aaa7e66 100644
>> --- a/fs/xfs/xfs_attr_inactive.c
>> +++ b/fs/xfs/xfs_attr_inactive.c
>> @@ -15,10 +15,10 @@
>>   #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"
>> -#include "xfs_attr.h"
>>   #include "xfs_attr_leaf.h"
>>   #include "xfs_quota.h"
>>   #include "xfs_dir2.h"
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v16 10/11] xfs: Add delay ready attr remove routines
  2021-04-02  7:59   ` Chandan Babu R
@ 2021-04-02  9:45     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:45 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 4/2/21 12:59 AM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson 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 is merged with
>> xfs_attr_node_removename become a new function, xfs_attr_remove_iter.
>> This new version uses a sort of state machine like switch to keep track
>> of where it was when EAGAIN was returned. A new version of
>> xfs_attr_remove_args consists of a simple loop to refresh the
>> transaction until the operation is completed. A new XFS_DAC_DEFER_FINISH
>> flag is used to finish the transaction where ever the existing code used
>> to.
>>
>> Calls to xfs_attr_rmtval_remove are replaced with the delay ready
>> version __xfs_attr_rmtval_remove. We will rename
>> __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
>> done.
>>
>> xfs_attr_rmtval_remove itself is still in use by the set routines (used
>> during a rename).  For reasons of preserving existing function, we
>> modify xfs_attr_rmtval_remove to call xfs_defer_finish when the flag is
>> set.  Similar to how xfs_attr_remove_args does here.  Once we transition
>> the set routines to be delay ready, xfs_attr_rmtval_remove is no longer
>> used and will be removed.
>>
>> 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.  See xfs_attr.h for a more
>> detailed diagram of the states.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c        | 206 +++++++++++++++++++++++++++-------------
>>   fs/xfs/libxfs/xfs_attr.h        | 125 ++++++++++++++++++++++++
>>   fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
>>   fs/xfs/libxfs/xfs_attr_remote.c |  48 ++++++----
>>   fs/xfs/libxfs/xfs_attr_remote.h |   2 +-
> 
> [...]
> 
>>   STATIC
>>   int xfs_attr_node_removename_setup(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state	**state)
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	int			error;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_da_state		**state = &dac->da_state;
>> +	int				error;
>>
>>   	error = xfs_attr_node_hasname(args, state);
>>   	if (error != -EEXIST)
>>   		return error;
>> +	error = 0;
>>
>>   	ASSERT((*state)->path.blk[(*state)->path.active - 1].bp != NULL);
>>   	ASSERT((*state)->path.blk[(*state)->path.active - 1].magic ==
>> @@ -1204,10 +1233,13 @@ int xfs_attr_node_removename_setup(
>>   	if (args->rmtblkno > 0) {
>>   		error = xfs_attr_leaf_mark_incomplete(args, *state);
>>   		if (error)
>> -			return error;
>> +			goto out;
>>
>> -		return xfs_attr_rmtval_invalidate(args);
>> +		error = xfs_attr_rmtval_invalidate(args);
>>   	}
>> +out:
>> +	if (error)
>> +		xfs_da_state_free(*state);
>>
>>   	return 0;
> 
> If the call to xfs_attr_rmtval_invalidate() returned a non-zero value, the
> above change would cause xfs_attr_node_removename_setup() to incorrectly
> return success.
Ok, will update the return.  Thx for the catch!

Allison

> 
>>   }
>> @@ -1232,70 +1264,114 @@ xfs_attr_node_remove_cleanup(
>>   }
>>
> 
> --
> chandan
> 

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

* Re: [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt
  2021-03-29  9:37   ` Chandan Babu R
@ 2021-04-02  9:47     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:47 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 3/29/21 2:37 AM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch adds a helper function xfs_attr_set_fmt.  This will help
>> isolate the code that will require state management from the portions
>> that do not.  xfs_attr_set_fmt returns 0 when the attr has been set and
>> no further action is needed.  It returns -EAGAIN when shortform has been
>> transformed to leaf, and the calling function should proceed the set the
>> attr in leaf form.
> 
> The previous behaviour is maintained across the changes made by this patch.
> 
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Great, thanks for the review!
Allison

> 
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
>>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 79 ++++++++++++++++++++++++++++--------------------
>>   1 file changed, 46 insertions(+), 33 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 5216f67..d46324a 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -216,6 +216,48 @@ xfs_attr_is_shortform(
>>   		ip->i_afp->if_nextents == 0);
>>   }
>>   
>> +STATIC int
>> +xfs_attr_set_fmt(
>> +	struct xfs_da_args	*args)
>> +{
>> +	struct xfs_buf          *leaf_bp = NULL;
>> +	struct xfs_inode	*dp = args->dp;
>> +	int			error2, error = 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.
>> +	 */
>> +	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 -EAGAIN;
>> +}
>> +
>>   /*
>>    * Set the attribute specified in @args.
>>    */
>> @@ -224,8 +266,7 @@ xfs_attr_set_args(
>>   	struct xfs_da_args	*args)
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>> -	struct xfs_buf          *leaf_bp = NULL;
>> -	int			error2, error = 0;
>> +	int			error;
>>   
>>   	/*
>>   	 * If the attribute list is already in leaf format, jump straight to
>> @@ -234,36 +275,9 @@ xfs_attr_set_args(
>>   	 * again.
>>   	 */
>>   	if (xfs_attr_is_shortform(dp)) {
>> -		/*
>> -		 * 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.
>> -		 */
>> -		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_fmt(args);
>> +		if (error != -EAGAIN)
>>   			return error;
>> -		}
>>   	}
>>   
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> @@ -297,8 +311,7 @@ xfs_attr_set_args(
>>   			return error;
>>   	}
>>   
>> -	error = xfs_attr_node_addname(args);
>> -	return error;
>> +	return xfs_attr_node_addname(args);
>>   }
>>   
>>   /*
> 
> 

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

* Re: [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform
  2021-03-29  9:21   ` Chandan Babu R
@ 2021-04-02  9:47     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:47 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 3/29/21 2:21 AM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch hoists xfs_attr_set_shortform into the calling function. This
>> will help keep all state management code in the same scope.
>>
> 
> That looks simple enough.
> 
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Thanks!
Allison

> 
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 81 ++++++++++++++++--------------------------------
>>   1 file changed, 27 insertions(+), 54 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 32c7447..5216f67 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -217,53 +217,6 @@ xfs_attr_is_shortform(
>>   }
>>   
>>   /*
>> - * Attempts to set an attr in shortform, or converts short form 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
>> @@ -272,7 +225,7 @@ xfs_attr_set_args(
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>>   	struct xfs_buf          *leaf_bp = NULL;
>> -	int			error = 0;
>> +	int			error2, error = 0;
>>   
>>   	/*
>>   	 * If the attribute list is already in leaf format, jump straight to
>> @@ -281,16 +234,36 @@ xfs_attr_set_args(
>>   	 * again.
>>   	 */
>>   	if (xfs_attr_is_shortform(dp)) {
>> +		/*
>> +		 * 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 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.
>> +		 * 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.
>>   		 */
>> -		error = xfs_attr_set_shortform(args, &leaf_bp);
>> -		if (error || !args->trans)
>> +		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;
>> +		}
>>   	}
>>   
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> 
> 

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

* Re: [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d
  2021-03-29  8:44   ` Chandan Babu R
@ 2021-04-02  9:47     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:47 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 3/29/21 1:44 AM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:02, Allison Henderson wrote:
>> Originally we added this patch to help modularize the attr code in
>> preparation for delayed attributes and the state machine it requires.
>> However, later reviews found that this slightly alters the transaction
>> handling as the helper function is ambiguous as to whether the
>> transaction is diry or clean.  This may cause a dirty transaction to be
>> included in the next roll, where previously it had not.  To preserve the
>> existing code flow, we reverse apply this commit.
> 
> Indeed, In the mainline kernel, __xfs_attr_rmtval_remove() invokes
> xfs_defer_finish() when the last remote block mapping has been removed. Also,
> xfs_trans_roll_inode() is invoked before invoking xfs_attr_refillstate().
> 
> Hence,
> 
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Ok, thank you!
Allison

> 
> --
> chandan
> 

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

* Re: [PATCH v16 00/11] xfs: Delay Ready Attributes
  2021-03-29 21:59 ` [PATCH v16 00/11] xfs: Delay Ready Attributes Darrick J. Wong
@ 2021-04-02  9:49   ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:49 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 3/29/21 2:59 PM, Darrick J. Wong wrote:
> On Thu, Mar 25, 2021 at 05:32:57PM -0700, Allison Henderson wrote:
>> Hi all,
>>
>> This set is a subset of a larger series for Dealyed Attributes. Which is a
>> subset of a yet larger series for 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 a delayed
>> operation.  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 delayed attribute portion.
>> We will introduce parent pointers in a later set.
>>
>> In this version I have reduced the set back to the "Delay Ready Attrs" sub series to
>> avoid reviewer burn out, but the extended series is available to view in the inlcuded
>> git hub links, which extend all the way through parent pointers.  Feel free to review
>> as much as feels reasonable.  The set as a whole is a bit much to digest at once, so
>> working through it in progressive subsets seems like a reasonable way to manage its
>> dev efforts.
>>
>> Lastly, in the last revision folks asked for some stress testing on the set.  On my
>> system, I found that in an fsstress test with all patches applied, we spend at most
>> %0.17 of the time in the attr routines, compared to at most %0.12 with out the set applied.
>> Both can fluctuate quite a bit depending on the other operations going on that seem to
>> occupy most of the activity.  For the most part though, I do not find these results to be
>> particularly concerning.  Though folks are certainly welcome to try it out on their own
>> system to see how the results might differ.
>>
>> Updates since v15: Mostly just review feed back from the previous revision.  I've
>> tracked changes below to help reviews recall the changes discussed
> 
> Hmm... so I ran fstests against this on an otherwise default V5
> filesystem, and saw three new regressions:
> 
> xfs/125 spat out this from the final repair run:
> 
> Phase 1 - find and verify superblock...
> Phase 2 - using internal log
> 	- zero log...
> 	- scan filesystem freespace and inode maps...
> 	- found root inode chunk
> Phase 3 - for each AG...
> 	- scan (but don't clear) agi unlinked lists...
> 	- process known inodes and perform inode discovery...
> 	- agno = 0
> attribute entry #32 in attr block 2, inode 134 is INCOMPLETE
> problem with attribute contents in inode 134
> would clear attr fork
> bad nblocks 8 for inode 134, would reset to 0
> bad anextents 4 for inode 134, would reset to 0
> 	- agno = 1
> 	- agno = 2
> 	- agno = 3
> 	- process newly discovered inodes...
> Phase 4 - check for duplicate blocks...
> 	- setting up duplicate extent list...
> 	- check for inodes claiming duplicate blocks...
> 	- agno = 0
> 	- agno = 1
> 	- agno = 2
> 	- agno = 3
> No modify flag set, skipping phase 5
> Phase 6 - check inode connectivity...
> 	- traversing filesystem ...
> 	- traversal finished ...
> 	- moving disconnected inodes to lost+found ...
> Phase 7 - verify link counts...
> No modify flag set, skipping filesystem flush and exiting.
> xfs_repair should not fail
> 
> And xfs/434 and xfs/436 both complained about memory leaks stemming from
> an xfs_da_state that xfs/125 didn't free correctly:
> 
> [ 1247.150683] =============================================================================
> [ 1247.151799] BUG xfs_da_state (Tainted: G    B   W        ): Objects remaining in xfs_da_state on __kmem_cache_shutdown()
> [ 1247.153246] -----------------------------------------------------------------------------
> [ 1247.153246]
> [ 1247.154528] INFO: Slab 0xffffea00002e9280 objects=17 used=11 fp=0xffff88800ba4b4a0 flags=0xfff80000010200
> [ 1247.155764] CPU: 2 PID: 50257 Comm: modprobe Tainted: G    B   W         5.12.0-rc4-djwx #rc4
> [ 1247.156849] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-1ubuntu1.1 04/01/2014
> [ 1247.157996] Call Trace:
> [ 1247.158330]  dump_stack+0x64/0x7c
> [ 1247.158767]  slab_err+0xb7/0xdc
> [ 1247.159196]  ? printk+0x58/0x6f
> [ 1247.159615]  __kmem_cache_shutdown.cold+0x39/0x15e
> [ 1247.160248]  kmem_cache_destroy+0x3f/0x110
> [ 1247.160779]  xfs_destroy_zones+0xbe/0xe2 [xfs]
> [ 1247.161462]  exit_xfs_fs+0x5f/0x9b4 [xfs]
> [ 1247.162065]  __do_sys_delete_module.constprop.0+0x145/0x220
> [ 1247.162740]  do_syscall_64+0x2d/0x40
> [ 1247.163197]  entry_SYSCALL_64_after_hwframe+0x44/0xae
> [ 1247.163810] RIP: 0033:0x7fd91cfe4bcb
> [ 1247.164262] Code: 73 01 c3 48 8b 0d c5 82 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 95 82 0c 00 f7 d8 64 89 01 48
> [ 1247.166352] RSP: 002b:00007fff89097038 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
> [ 1247.167217] RAX: ffffffffffffffda RBX: 0000558b8e105cc0 RCX: 00007fd91cfe4bcb
> [ 1247.167998] RDX: 0000000000000000 RSI: 0000000000000800 RDI: 0000558b8e105d28
> [ 1247.168781] RBP: 0000558b8e105cc0 R08: 0000000000000000 R09: 0000000000000000
> [ 1247.169562] R10: 00007fd91d060ac0 R11: 0000000000000206 R12: 0000558b8e105d28
> [ 1247.170351] R13: 0000000000000000 R14: 0000558b8e105d28 R15: 0000558b8e105cc0
> 
>  From a quick bisect, all of thse problem originates in the last patch.
Alrighty, I will see if I can recreate these bugs and get that figured 
out.  Thanks!

Allison
> 
> --D
> 
>> xfs: Reverse apply 72b97ea40d
>>    NEW
>>
>> xfs: Add helper xfs_attr_node_remove_step
>>    DROPPED
>>
>> xfs: Add xfs_attr_node_remove_cleanup
>>    No change
>>
>> xfs: Hoist transaction handling in xfs_attr_node_remove_step
>>    DROPPED
>>
>> xfs: Hoist xfs_attr_set_shortform
>>    No change
>>
>> xfs: Add helper xfs_attr_set_fmt
>>    Fixed helper to return error when defer_finish fails
>>
>> xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
>>    Renamed xfs_attr_node_addname_work to xfs_attr_node_addname_clear_incomplete
>>
>> xfs: Add helper xfs_attr_node_addname_find_attr
>>    Renamed goto out, to goto error
>>
>> xfs: Hoist xfs_attr_node_addname
>>    Removed unused retval variable
>>    Removed extra state free in xfs_attr_node_addname
>>
>> xfs: Hoist xfs_attr_leaf_addname
>>    Fixed spelling typos
>>
>> xfs: Hoist node transaction handling
>>    Added consistent braces to if/else statement
>>
>> xfs: Add delay ready attr remove routines
>>    Typo fixes
>>    Merged xfs_attr_remove_iter with xfs_attr_node_removename_iter
>>    Added state XFS_DAS_RMTBLK
>>    Flow chart updated
>>
>> xfs: Add delay ready attr set routines
>>    Rebase adjustments
>>    Typo fixes
>>
>>
>> Extended Series Changes
>> ------------------------
>> xfs: Add state machine tracepoints
>>    Rebase adjustments
>>    xfs_attr_node_remove_rmt_return removed to match earlier refactoring changes
>>    trace_xfs_attr_node_removename_iter_return becomes
>>    trace_xfs_attr_remove_iter_return to match earlier refactoring changes
>>
>> xfs: Rename __xfs_attr_rmtval_remove
>>    No change
>>
>> xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
>>    Added kmem_alloc_large fall back
>>   
>> xfs: Set up infrastructure for deferred attribute operations
>>    Typo fixes
>>    Rename xfs_trans_attr to xfs_trans_attr_finish_update
>>    Added helper function xfs_attri_validate
>>    Split patch into infrastructure and implementation patches
>>    Added XFS_ERROR_REPORT in xlog_recover_attri_commit_pass2:
>>
>> xfs: Implement for deferred attribute operations
>>    NEW
>>
>> xfs: Skip flip flags for delayed attrs
>>    Did a performance analysis
>>
>> xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
>>    Typo fixes
>>
>> xfs: Remove unused xfs_attr_*_args
>>    Rebase adjustments
>>
>> xfs: Add delayed attributes error tag
>>    Added errortag include
>>
>> xfs: Merge xfs_delattr_context into xfs_attr_item
>>    Typo fixes
>>
>>
>> This series can be viewed on github here:
>> https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v16__;!!GqivPVa7Brio!Ol0JaXyUI3K5MEGrBo_eMHzcTqcuIL9p25-XSZftWgn4bmbxeX_AJf7Hl-kP6ecxUskY$
>>
>> As well as the extended delayed attribute and parent pointer series:
>> https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v16_extended__;!!GqivPVa7Brio!Ol0JaXyUI3K5MEGrBo_eMHzcTqcuIL9p25-XSZftWgn4bmbxeX_AJf7Hl-kP6QsZXJc5$
>>
>> And the test cases:
>> https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs_work/tree/pptr_xfstestsv2__;!!GqivPVa7Brio!Ol0JaXyUI3K5MEGrBo_eMHzcTqcuIL9p25-XSZftWgn4bmbxeX_AJf7Hl-kP6fAPHdk4$
>>
>> In order to run the test cases, you will need have the corresponding xfsprogs
>> changes as well.  Which can be found here:
>> https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v16__;!!GqivPVa7Brio!Ol0JaXyUI3K5MEGrBo_eMHzcTqcuIL9p25-XSZftWgn4bmbxeX_AJf7Hl-kP6ToIBbr7$
>> https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v16_extended__;!!GqivPVa7Brio!Ol0JaXyUI3K5MEGrBo_eMHzcTqcuIL9p25-XSZftWgn4bmbxeX_AJf7Hl-kP6cfTeHmh$
>>
>> To run the xfs attributes tests run:
>> check -g attr
>>
>> To run as delayed attributes run:
>> export MOUNT_OPTIONS="-o 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 Henderson (11):
>>    xfs: Reverse apply 72b97ea40d
>>    xfs: Add xfs_attr_node_remove_cleanup
>>    xfs: Hoist xfs_attr_set_shortform
>>    xfs: Add helper xfs_attr_set_fmt
>>    xfs: Separate xfs_attr_node_addname and
>>      xfs_attr_node_addname_clear_incomplete
>>    xfs: Add helper xfs_attr_node_addname_find_attr
>>    xfs: Hoist xfs_attr_node_addname
>>    xfs: Hoist xfs_attr_leaf_addname
>>    xfs: Hoist node transaction handling
>>    xfs: Add delay ready attr remove routines
>>    xfs: Add delay ready attr set routines
>>
>>   fs/xfs/libxfs/xfs_attr.c        | 903 ++++++++++++++++++++++++----------------
>>   fs/xfs/libxfs/xfs_attr.h        | 364 ++++++++++++++++
>>   fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
>>   fs/xfs/libxfs/xfs_attr_remote.c | 126 ++++--
>>   fs/xfs/libxfs/xfs_attr_remote.h |   7 +-
>>   fs/xfs/xfs_attr_inactive.c      |   2 +-
>>   fs/xfs/xfs_trace.h              |   1 -
>>   7 files changed, 998 insertions(+), 407 deletions(-)
>>
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d
  2021-04-01 15:43   ` Brian Foster
@ 2021-04-02  9:49     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:49 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/1/21 8:43 AM, Brian Foster wrote:
> On Thu, Mar 25, 2021 at 05:32:58PM -0700, Allison Henderson wrote:
>> Originally we added this patch to help modularize the attr code in
>> preparation for delayed attributes and the state machine it requires.
>> However, later reviews found that this slightly alters the transaction
>> handling as the helper function is ambiguous as to whether the
>> transaction is diry or clean.  This may cause a dirty transaction to be
>> included in the next roll, where previously it had not.  To preserve the
>> existing code flow, we reverse apply this commit.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Thank you!
Allison

> 
>>   fs/xfs/libxfs/xfs_attr.c | 28 +++++++++-------------------
>>   1 file changed, 9 insertions(+), 19 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 472b303..b42144e 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1202,24 +1202,6 @@ int xfs_attr_node_removename_setup(
>>   	return 0;
>>   }
>>   
>> -STATIC int
>> -xfs_attr_node_remove_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.
>> -	 */
>> -	return xfs_attr_refillstate(state);
>> -}
>> -
>>   /*
>>    * Remove a name from a B-tree attribute list.
>>    *
>> @@ -1248,7 +1230,15 @@ xfs_attr_node_removename(
>>   	 * overflow the maximum size of a transaction and/or hit a deadlock.
>>   	 */
>>   	if (args->rmtblkno > 0) {
>> -		error = xfs_attr_node_remove_rmt(args, state);
>> +		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);
>>   		if (error)
>>   			goto out;
>>   	}
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-04-02  4:05   ` Chandan Babu R
@ 2021-04-02  9:50     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:50 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 4/1/21 9:05 PM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch separates the first half of xfs_attr_node_addname into a
>> helper function xfs_attr_node_addname_find_attr.  It also replaces the
>> restart goto with with an EAGAIN return code driven by a loop in the
>> calling function.  This looks odd now, but will clean up nicly once we
>> introduce the state machine.  It will also enable hoisting the last
>> state out of xfs_attr_node_addname with out having to plumb in a "done"
>> parameter to know if we need to move to the next state or not.
>>
> 
> Looks good to me.
> 
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Thanks for the reviews!
Allison

> 
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 86 ++++++++++++++++++++++++++++++------------------
>>   1 file changed, 54 insertions(+), 32 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 531ff56..16159f6 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -52,7 +52,10 @@ 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_da_args *args,
>> +				 struct xfs_da_state *state);
>> +STATIC int xfs_attr_node_addname_find_attr(struct xfs_da_args *args,
>> +				 struct xfs_da_state **state);
>>   STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
>>   STATIC int xfs_attr_node_addname_clear_incomplete(struct xfs_da_args *args);
>>   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>> @@ -267,6 +270,7 @@ xfs_attr_set_args(
>>   	struct xfs_da_args	*args)
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>> +	struct xfs_da_state     *state;
>>   	int			error;
>>   
>>   	/*
>> @@ -312,7 +316,14 @@ xfs_attr_set_args(
>>   			return error;
>>   	}
>>   
>> -	return xfs_attr_node_addname(args);
>> +	do {
>> +		error = xfs_attr_node_addname_find_attr(args, &state);
>> +		if (error)
>> +			return error;
>> +		error = xfs_attr_node_addname(args, state);
>> +	} while (error == -EAGAIN);
>> +
>> +	return error;
>>   }
>>   
>>   /*
>> @@ -885,47 +896,26 @@ xfs_attr_node_hasname(
>>    * External routines when attribute list size > geo->blksize
>>    *========================================================================*/
>>   
>> -/*
>> - * Add a name to a Btree-format attribute list.
>> - *
>> - * This will involve walking down the Btree, and may involve splitting
>> - * leaf nodes and even splitting intermediate nodes up to and including
>> - * the root node (a special case of an intermediate node).
>> - *
>> - * "Remote" attribute values confuse the issue and atomic rename operations
>> - * add a whole extra layer of confusion on top of that.
>> - */
>>   STATIC int
>> -xfs_attr_node_addname(
>> -	struct xfs_da_args	*args)
>> +xfs_attr_node_addname_find_attr(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state     **state)
>>   {
>> -	struct xfs_da_state	*state;
>> -	struct xfs_da_state_blk	*blk;
>> -	struct xfs_inode	*dp;
>> -	int			retval, error;
>> -
>> -	trace_xfs_attr_node_addname(args);
>> +	int			retval;
>>   
>>   	/*
>> -	 * Fill in bucket of arguments/results/context to carry around.
>> -	 */
>> -	dp = args->dp;
>> -restart:
>> -	/*
>>   	 * Search to see if name already exists, and get back a pointer
>>   	 * to where it should go.
>>   	 */
>> -	retval = xfs_attr_node_hasname(args, &state);
>> +	retval = xfs_attr_node_hasname(args, state);
>>   	if (retval != -ENOATTR && retval != -EEXIST)
>> -		goto out;
>> +		goto error;
>>   
>> -	blk = &state->path.blk[ state->path.active-1 ];
>> -	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>   	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>> -		goto out;
>> +		goto error;
>>   	if (retval == -EEXIST) {
>>   		if (args->attr_flags & XATTR_CREATE)
>> -			goto out;
>> +			goto error;
>>   
>>   		trace_xfs_attr_node_replace(args);
>>   
>> @@ -943,6 +933,38 @@ xfs_attr_node_addname(
>>   		args->rmtvaluelen = 0;
>>   	}
>>   
>> +	return 0;
>> +error:
>> +	if (*state)
>> +		xfs_da_state_free(*state);
>> +	return retval;
>> +}
>> +
>> +/*
>> + * Add a name to a Btree-format attribute list.
>> + *
>> + * This will involve walking down the Btree, and may involve splitting
>> + * leaf nodes and even splitting intermediate nodes up to and including
>> + * the root node (a special case of an intermediate node).
>> + *
>> + * "Remote" attribute values confuse the issue and atomic rename operations
>> + * add a whole extra layer of confusion on top of that.
>> + */
>> +STATIC int
>> +xfs_attr_node_addname(
>> +	struct xfs_da_args	*args,
>> +	struct xfs_da_state	*state)
>> +{
>> +	struct xfs_da_state_blk	*blk;
>> +	struct xfs_inode	*dp;
>> +	int			retval, error;
>> +
>> +	trace_xfs_attr_node_addname(args);
>> +
>> +	dp = args->dp;
>> +	blk = &state->path.blk[state->path.active-1];
>> +	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>> +
>>   	retval = xfs_attr3_leaf_add(blk->bp, state->args);
>>   	if (retval == -ENOSPC) {
>>   		if (state->path.active == 1) {
>> @@ -968,7 +990,7 @@ xfs_attr_node_addname(
>>   			if (error)
>>   				goto out;
>>   
>> -			goto restart;
>> +			return -EAGAIN;
>>   		}
>>   
>>   		/*
> 
> 

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

* Re: [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname
  2021-04-02  4:40   ` Chandan Babu R
@ 2021-04-02  9:50     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:50 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 4/1/21 9:40 PM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch hoists xfs_attr_leaf_addname into the calling function.  The
>> goal being to get all the code that will require state management into
>> the same scope. This isn't particuarly aesthetic right away, but it is a
>> preliminary step to merging in the state machine code.
> 
> The changes look good to me.
> 
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Thank you!
Allison

> 
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> 

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

* Re: [PATCH v16 09/11] xfs: Hoist node transaction handling
  2021-04-02  5:04   ` Chandan Babu R
@ 2021-04-02  9:51     ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-02  9:51 UTC (permalink / raw)
  To: Chandan Babu R; +Cc: linux-xfs



On 4/1/21 10:04 PM, Chandan Babu R wrote:
> On 26 Mar 2021 at 06:03, Allison Henderson wrote:
>> This patch basically hoists the node transaction handling around the
>> leaf code we just hoisted.  This will helps setup this area for the
>> state machine since the goto is easily replaced with a state since it
>> ends with a transaction roll.
>>
> 
> Looks good to me.
> 
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Thanks!  Will add the rvb.
Allison

> 
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Brian Foster <bfoster@redhat.com>
>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> 

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

* Re: [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname
  2021-04-02  9:01     ` Allison Henderson
@ 2021-04-05 13:15       ` Brian Foster
  0 siblings, 0 replies; 44+ messages in thread
From: Brian Foster @ 2021-04-05 13:15 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Fri, Apr 02, 2021 at 02:01:08AM -0700, Allison Henderson wrote:
> 
> 
> On 4/1/21 8:43 AM, Brian Foster wrote:
> > On Thu, Mar 25, 2021 at 05:33:05PM -0700, Allison Henderson wrote:
> > > This patch hoists xfs_attr_leaf_addname into the calling function.  The
> > > goal being to get all the code that will require state management into
> > > the same scope. This isn't particuarly aesthetic right away, but it is a
> > > preliminary step to merging in the state machine code.
> > > 
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > ---
> > >   fs/xfs/libxfs/xfs_attr.c | 209 ++++++++++++++++++++++-------------------------
> > >   1 file changed, 96 insertions(+), 113 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index 5b5410f..16f10ac 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > ...
> > > @@ -729,115 +821,6 @@ xfs_attr_leaf_try_add(
> > >   	return retval;
> > >   }
> > > -
> > > -/*
> > > - * 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);
> > > -
> > 
> > Did this tracepoint disappear for a reason?
> I thought the trace made sense to mark the entry of this function, but then
> when hoisted, looked sort of out of place.  It certainly wouldn't hurt it to
> put it back if people prefer.  I don't see it used anywhere else, and I
> don't think the calling function has it's own trace scheme either?  Should I
> translate trace_xfs_attr_leaf_addname to
> trace_xfs_attr_set_args and hoist it up?
> 

Ok. I have no strong preference, I just noticed it missing. FWIW, it
appears to be unused as of the end of the series so this patch should
probably just drop it from xfs_trace.h if the removal was intentional.

Brian

> Allison
> 
> > 
> > Brian
> > 
> > > -	error = xfs_attr_leaf_try_add(args, bp);
> > > -	if (error)
> > > -		return error;
> > > -
> > > -	/*
> > > -	 * 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;
> > > -
> > > -	/*
> > > -	 * If there was an out-of-line value, allocate the blocks we
> > > -	 * identified for its storage and copy the value.  This is done
> > > -	 * after we create 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_rmtval_set(args);
> > > -		if (error)
> > > -			return error;
> > > -	}
> > > -
> > > -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
> > > -		/*
> > > -		 * Added a "remote" value, just clear the incomplete flag.
> > > -		 */
> > > -		if (args->rmtblkno > 0)
> > > -			error = xfs_attr3_leaf_clearflag(args);
> > > -
> > > -		return error;
> > > -	}
> > > -
> > > -	/*
> > > -	 * If this is an atomic rename operation, we must "flip" the incomplete
> > > -	 * flags on the "new" and "old" attribute/value pairs so that one
> > > -	 * disappears and one appears atomically.  Then we must remove the "old"
> > > -	 * attribute/value pair.
> > > -	 *
> > > -	 * In a separate transaction, set the incomplete flag on the "old" attr
> > > -	 * and clear the incomplete flag on the "new" attr.
> > > -	 */
> > > -
> > > -	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 a "remote" value
> > > -	 * (if it exists).
> > > -	 */
> > > -	xfs_attr_restore_rmt_blk(args);
> > > -
> > > -	if (args->rmtblkno) {
> > > -		error = xfs_attr_rmtval_invalidate(args);
> > > -		if (error)
> > > -			return error;
> > > -
> > > -		error = xfs_attr_rmtval_remove(args);
> > > -		if (error)
> > > -			return error;
> > > -	}
> > > -
> > > -	/*
> > > -	 * Read in the block containing the "old" attr, then remove the "old"
> > > -	 * attr from that block (neat, huh!)
> > > -	 */
> > > -	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
> > > -				   &bp);
> > > -	if (error)
> > > -		return error;
> > > -
> > > -	xfs_attr3_leaf_remove(bp, args);
> > > -
> > > -	/*
> > > -	 * If the result is small enough, shrink it all into the inode.
> > > -	 */
> > > -	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 */
> > > -
> > > -	return error;
> > > -}
> > > -
> > >   /*
> > >    * Return EEXIST if attr is found, or ENOATTR if not
> > >    */
> > > -- 
> > > 2.7.4
> > > 
> > 
> 


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

* Re: [PATCH v16 10/11] xfs: Add delay ready attr remove routines
  2021-04-02  9:42     ` Allison Henderson
@ 2021-04-05 13:15       ` Brian Foster
  0 siblings, 0 replies; 44+ messages in thread
From: Brian Foster @ 2021-04-05 13:15 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Fri, Apr 02, 2021 at 02:42:28AM -0700, Allison Henderson wrote:
> 
> 
> On 4/1/21 9:55 AM, Brian Foster wrote:
> > On Thu, Mar 25, 2021 at 05:33:07PM -0700, Allison Henderson 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 is merged with
> > > xfs_attr_node_removename become a new function, xfs_attr_remove_iter.
> > > This new version uses a sort of state machine like switch to keep track
> > > of where it was when EAGAIN was returned. A new version of
> > > xfs_attr_remove_args consists of a simple loop to refresh the
> > > transaction until the operation is completed. A new XFS_DAC_DEFER_FINISH
> > > flag is used to finish the transaction where ever the existing code used
> > > to.
> > > 
> > > Calls to xfs_attr_rmtval_remove are replaced with the delay ready
> > > version __xfs_attr_rmtval_remove. We will rename
> > > __xfs_attr_rmtval_remove back to xfs_attr_rmtval_remove when we are
> > > done.
> > > 
> > > xfs_attr_rmtval_remove itself is still in use by the set routines (used
> > > during a rename).  For reasons of preserving existing function, we
> > > modify xfs_attr_rmtval_remove to call xfs_defer_finish when the flag is
> > > set.  Similar to how xfs_attr_remove_args does here.  Once we transition
> > > the set routines to be delay ready, xfs_attr_rmtval_remove is no longer
> > > used and will be removed.
> > > 
> > > 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.  See xfs_attr.h for a more
> > > detailed diagram of the states.
> > > 
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > ---
> > >   fs/xfs/libxfs/xfs_attr.c        | 206 +++++++++++++++++++++++++++-------------
> > >   fs/xfs/libxfs/xfs_attr.h        | 125 ++++++++++++++++++++++++
> > >   fs/xfs/libxfs/xfs_attr_leaf.c   |   2 +-
> > >   fs/xfs/libxfs/xfs_attr_remote.c |  48 ++++++----
> > >   fs/xfs/libxfs/xfs_attr_remote.h |   2 +-
> > >   fs/xfs/xfs_attr_inactive.c      |   2 +-
> > >   6 files changed, 297 insertions(+), 88 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index 41accd5..4a73691 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > ...
> > > @@ -221,6 +220,32 @@ xfs_attr_is_shortform(
> > >   		ip->i_afp->if_nextents == 0);
> > >   }
> > > +/*
> > > + * Checks to see if a delayed attribute transaction should be rolled.  If so,
> > > + * also checks for a defer finish.  Transaction is finished and rolled as
> > > + * needed, and returns true of false if the delayed operation should continue.
> > > + */
> > 
> > Outdated comment wrt to the return value.
> Ok, will drop last line here
> 
> > 
> > > +int
> > > +xfs_attr_trans_roll(
> > > +	struct xfs_delattr_context	*dac)
> > > +{
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	int				error;
> > > +
> > > +	if (dac->flags & XFS_DAC_DEFER_FINISH) {
> > > +		/*
> > > +		 * The caller wants us to finish all the deferred ops so that we
> > > +		 * avoid pinning the log tail with a large number of deferred
> > > +		 * ops.
> > > +		 */
> > > +		dac->flags &= ~XFS_DAC_DEFER_FINISH;
> > > +		error = xfs_defer_finish(&args->trans);
> > > +	} else
> > > +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > +
> > > +	return error;
> > > +}
> > > +
> > >   STATIC int
> > >   xfs_attr_set_fmt(
> > >   	struct xfs_da_args	*args)
> > ...
> > > @@ -1232,70 +1264,114 @@ xfs_attr_node_remove_cleanup(
> > >   }
> > >   /*
> > > - * Remove a name from a B-tree attribute list.
> > > + * Remove the attribute specified in @args.
> > >    *
> > >    * 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 in-line 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)
> > > +int
> > > +xfs_attr_remove_iter(
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	struct xfs_da_state	*state;
> > > -	int			retval, error;
> > > -	struct xfs_inode	*dp = args->dp;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_da_state		*state = dac->da_state;
> > > +	int				retval, error;
> > > +	struct xfs_inode		*dp = args->dp;
> > >   	trace_xfs_attr_node_removename(args);
> > > -	error = xfs_attr_node_removename_setup(args, &state);
> > > -	if (error)
> > > -		goto out;
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_UNINIT:
> > > +		if (!xfs_inode_hasattr(dp))
> > > +			return -ENOATTR;
> > > -	/*
> > > -	 * 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_rmtval_remove(args);
> > > -		if (error)
> > > -			goto out;
> > > +		if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
> > > +			ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
> > > +			return xfs_attr_shortform_remove(args);
> > > +		}
> > > +
> > > +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> > > +			return xfs_attr_leaf_removename(args);
> > > +
> > > +	/* fallthrough */
> > > +	case XFS_DAS_RMTBLK:
> > > +		dac->dela_state = XFS_DAS_RMTBLK;
> > > +
> > > +		if (!dac->da_state) {
> > > +			error = xfs_attr_node_removename_setup(dac);
> > > +			if (error)
> > > +				goto out;
> > 
> > Do we need the goto here if _removename_setup() frees state on error (or
> > is the latter change necessary)?
> I think we can safely return here.  Will update
> 
> > 
> > > +		}
> > > +		state = dac->da_state;
> > 
> > Also, can this fold into the above if (!da_state) branch? Or maybe the
> > whole setup branch pulled up into the UNINIT state? Not a big deal, but
> > it does look a little out of place in the RMTBLK state.
> Sure, it should be ok, there isnt any EAGAINs here, so it shouldnt make a
> difference
> 
> > 
> > >   		/*
> > > -		 * Refill the state structure with buffers, the prior calls
> > > -		 * released our buffers.
> > > +		 * 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.
> > >   		 */
> > > -		error = xfs_attr_refillstate(state);
> > > -		if (error)
> > > -			goto out;
> > > -	}
> > > -	retval = xfs_attr_node_remove_cleanup(args, state);
> > > +		if (args->rmtblkno > 0) {
> > > +			/*
> > > +			 * May return -EAGAIN. Remove blocks until
> > > +			 * args->rmtblkno == 0
> > > +			 */
> > > +			error = __xfs_attr_rmtval_remove(dac);
> > > +			if (error)
> > > +				break;
> > 
> > I feel that the difference between a break and goto out might confuse
> > some of the error handling. Right now, it looks like the exit path
> > handles either scenario, so we could presumably do something like the
> > following at the end of the function:
> > 
> > 	if (error != -EAGAIN && state)
> > 		xfs_da_state_free(state);
> > 	return error;
> > 
> > ... and just ditch the label. Alternatively we could retain the label above
> > the state check, but just use it consistently throughout the function.
> > 
> Either will work?  I think I'd prefer the gotos over the breaks though, I
> just think it reads easier.  The switch is sort of big, so I think the gotos
> make it a little more clear in that we're exiting the function without
> having to skim all the way to the bottom.
> 

Sounds reasonable to me as long as the error handling usage is
consistent. Thanks.

Brian

> > Other than those few nits, this one looks pretty good to me.
> Great, will update.  Thanks!
> 
> Allison
> 
> > 
> > Brian
> > 
> > > +
> > > +			/*
> > > +			 * Refill the state structure with buffers, the prior
> > > +			 * calls released our buffers.
> > > +			 */
> > > +			ASSERT(args->rmtblkno == 0);
> > > +			error = xfs_attr_refillstate(state);
> > > +			if (error)
> > > +				goto out;
> > > +
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			return -EAGAIN;
> > > +		}
> > > +
> > > +		retval = xfs_attr_node_remove_cleanup(args, state);
> > > -	/*
> > > -	 * Check to see if the tree needs to be collapsed.
> > > -	 */
> > > -	if (retval && (state->path.active > 1)) {
> > > -		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.
> > > +		 * Check to see if the tree needs to be collapsed. Set the flag
> > > +		 * to indicate that the calling function needs to move the
> > > +		 * shrink operation
> > >   		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > -		if (error)
> > > -			goto out;
> > > -	}
> > > +		if (retval && (state->path.active > 1)) {
> > > +			error = xfs_da3_join(state);
> > > +			if (error)
> > > +				goto out;
> > > -	/*
> > > -	 * 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);
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +			dac->dela_state = XFS_DAS_RM_SHRINK;
> > > +			return -EAGAIN;
> > > +		}
> > > +
> > > +		/* fallthrough */
> > > +	case 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);
> > > +
> > > +		break;
> > > +	default:
> > > +		ASSERT(0);
> > > +		error = -EINVAL;
> > > +		goto out;
> > > +	}
> > > +	if (error == -EAGAIN)
> > > +		return error;
> > >   out:
> > >   	if (state)
> > >   		xfs_da_state_free(state);
> > > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > > index 3e97a93..92a6a50 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.h
> > > +++ b/fs/xfs/libxfs/xfs_attr.h
> > > @@ -74,6 +74,127 @@ struct xfs_attr_list_context {
> > >   };
> > > +/*
> > > + * ========================================================================
> > > + * Structure used to pass context around among the delayed routines.
> > > + * ========================================================================
> > > + */
> > > +
> > > +/*
> > > + * 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()
> > > + *              │
> > > + *              v
> > > + *        have attr to remove? ──n──> done
> > > + *              │
> > > + *              y
> > > + *              │
> > > + *              v
> > > + *        are we short form? ──y──> xfs_attr_shortform_remove ──> done
> > > + *              │
> > > + *              n
> > > + *              │
> > > + *              V
> > > + *        are we leaf form? ──y──> xfs_attr_leaf_removename ──> done
> > > + *              │
> > > + *              n
> > > + *              │
> > > + *              V
> > > + *   ┌── need to setup state?
> > > + *   │          │
> > > + *   n          y
> > > + *   │          │
> > > + *   │          v
> > > + *   │ find attr and get state
> > > + *   │    attr has blks? ───n────???
> > > + *   │          │                v
> > > + *   │          │         find and invalidate
> > > + *   │          y         the blocks. mark
> > > + *   │          │         attr incomplete
> > > + *   │          ├────────────────┘
> > > + *   └──────────┤
> > > + *              │
> > > + *              v
> > > + *      Have blks to remove? ─────y────???
> > > + *              │       ^      remove the blks
> > > + *              │       │              │
> > > + *              │       │              v
> > > + *              │       │        refill the state
> > > + *              n       │              │
> > > + *              │       │              v
> > > + *              │       │         XFS_DAS_RMTBLK
> > > + *              │       └─────  re-enter with one
> > > + *              │               less blk to remove
> > > + *              │
> > > + *              v
> > > + *       remove leaf and
> > > + *       update hash with
> > > + *   xfs_attr_node_remove_cleanup
> > > + *              │
> > > + *              v
> > > + *           need to
> > > + *        shrink tree? ─n─???
> > > + *              │         │
> > > + *              y         │
> > > + *              │         │
> > > + *              v         │
> > > + *          join leaf     │
> > > + *              │         │
> > > + *              v         │
> > > + *      XFS_DAS_RM_SHRINK │
> > > + *              │         │
> > > + *              v         │
> > > + *       do the shrink    │
> > > + *              │         │
> > > + *              v         │
> > > + *          free state <──┘
> > > + *              │
> > > + *              v
> > > + *            done
> > > + *
> > > + */
> > > +
> > > +/*
> > > + * 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 {
> > > +	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
> > > +	XFS_DAS_RMTBLK,		      /* Removing remote blks */
> > > +	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
> > > +};
> > > +
> > > +/*
> > > + * Defines for xfs_delattr_context.flags
> > > + */
> > > +#define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
> > > +
> > > +/*
> > > + * Context used for keeping track of delayed attribute operations
> > > + */
> > > +struct xfs_delattr_context {
> > > +	struct xfs_da_args      *da_args;
> > > +
> > > +	/* Used in xfs_attr_node_removename to roll through removing blocks */
> > > +	struct xfs_da_state     *da_state;
> > > +
> > > +	/* Used to keep track of current state of delayed operation */
> > > +	unsigned int            flags;
> > > +	enum xfs_delattr_state  dela_state;
> > > +};
> > > +
> > >   /*========================================================================
> > >    * Function prototypes for the kernel.
> > >    *========================================================================*/
> > > @@ -91,6 +212,10 @@ 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);
> > > +int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
> > >   bool xfs_attr_namecheck(const void *name, size_t length);
> > > +void xfs_delattr_context_init(struct xfs_delattr_context *dac,
> > > +			      struct xfs_da_args *args);
> > >   #endif	/* __XFS_ATTR_H__ */
> > > diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> > > index d6ef69a..3780141 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> > > +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> > > @@ -19,8 +19,8 @@
> > >   #include "xfs_bmap_btree.h"
> > >   #include "xfs_bmap.h"
> > >   #include "xfs_attr_sf.h"
> > > -#include "xfs_attr_remote.h"
> > >   #include "xfs_attr.h"
> > > +#include "xfs_attr_remote.h"
> > >   #include "xfs_attr_leaf.h"
> > >   #include "xfs_error.h"
> > >   #include "xfs_trace.h"
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > > index 48d8e9c..908521e7 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > > @@ -674,10 +674,12 @@ xfs_attr_rmtval_invalidate(
> > >    */
> > >   int
> > >   xfs_attr_rmtval_remove(
> > > -	struct xfs_da_args      *args)
> > > +	struct xfs_da_args		*args)
> > >   {
> > > -	int			error;
> > > -	int			retval;
> > > +	int				error;
> > > +	struct xfs_delattr_context	dac  = {
> > > +		.da_args	= args,
> > > +	};
> > >   	trace_xfs_attr_rmtval_remove(args);
> > > @@ -685,31 +687,29 @@ xfs_attr_rmtval_remove(
> > >   	 * Keep de-allocating extents until the remote-value region is gone.
> > >   	 */
> > >   	do {
> > > -		retval = __xfs_attr_rmtval_remove(args);
> > > -		if (retval && retval != -EAGAIN)
> > > -			return retval;
> > > +		error = __xfs_attr_rmtval_remove(&dac);
> > > +		if (error != -EAGAIN)
> > > +			break;
> > > -		/*
> > > -		 * Close out trans and start the next one in the chain.
> > > -		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> > > +		error = xfs_attr_trans_roll(&dac);
> > >   		if (error)
> > >   			return error;
> > > -	} while (retval == -EAGAIN);
> > > +	} while (true);
> > > -	return 0;
> > > +	return error;
> > >   }
> > >   /*
> > >    * 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
> > > + * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the
> > >    * transaction and re-call the function
> > >    */
> > >   int
> > >   __xfs_attr_rmtval_remove(
> > > -	struct xfs_da_args	*args)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	int			error, done;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	int				error, done;
> > >   	/*
> > >   	 * Unmap value blocks for this attr.
> > > @@ -719,12 +719,20 @@ __xfs_attr_rmtval_remove(
> > >   	if (error)
> > >   		return error;
> > > -	error = xfs_defer_finish(&args->trans);
> > > -	if (error)
> > > -		return error;
> > > -
> > > -	if (!done)
> > > +	/*
> > > +	 * We don't need an explicit state here to pick up where we left off. We
> > > +	 * can figure it out using the !done return code. Calling function only
> > > +	 * needs to keep recalling this routine until we indicate to stop by
> > > +	 * returning anything other than -EAGAIN. The actual value of
> > > +	 * attr->xattri_dela_state may be some value reminiscent of the calling
> > > +	 * function, but it's value is irrelevant with in the context of this
> > > +	 * function. Once we are done here, the next state is set as needed
> > > +	 * by the parent
> > > +	 */
> > > +	if (!done) {
> > > +		dac->flags |= XFS_DAC_DEFER_FINISH;
> > >   		return -EAGAIN;
> > > +	}
> > >   	return error;
> > >   }
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> > > index 9eee615..002fd30 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.h
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> > > @@ -14,5 +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);
> > > +int __xfs_attr_rmtval_remove(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 bfad669..aaa7e66 100644
> > > --- a/fs/xfs/xfs_attr_inactive.c
> > > +++ b/fs/xfs/xfs_attr_inactive.c
> > > @@ -15,10 +15,10 @@
> > >   #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"
> > > -#include "xfs_attr.h"
> > >   #include "xfs_attr_leaf.h"
> > >   #include "xfs_quota.h"
> > >   #include "xfs_dir2.h"
> > > -- 
> > > 2.7.4
> > > 
> > 
> 


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

* Re: [PATCH v16 11/11] xfs: Add delay ready attr set routines
  2021-04-02  9:01     ` Allison Henderson
@ 2021-04-05 13:17       ` Brian Foster
  2021-04-14 18:14         ` Allison Henderson
  0 siblings, 1 reply; 44+ messages in thread
From: Brian Foster @ 2021-04-05 13:17 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Fri, Apr 02, 2021 at 02:01:20AM -0700, Allison Henderson wrote:
> 
> 
> On 4/1/21 9:57 AM, Brian Foster wrote:
> > On Thu, Mar 25, 2021 at 05:33:08PM -0700, Allison Henderson 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. See xfs_attr.h for a more detailed diagram of the states.
> > > 
> > > Two new helper functions have been added: xfs_attr_rmtval_find_space 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.
> > > 
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > ---
> > >   fs/xfs/libxfs/xfs_attr.c        | 446 ++++++++++++++++++++++++----------------
> > >   fs/xfs/libxfs/xfs_attr.h        | 241 +++++++++++++++++++++-
> > >   fs/xfs/libxfs/xfs_attr_remote.c | 100 ++++++---
> > >   fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
> > >   fs/xfs/xfs_trace.h              |   1 -
> > >   5 files changed, 582 insertions(+), 211 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index 4a73691..6a86b62 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > ...
> > > @@ -246,29 +247,55 @@ xfs_attr_trans_roll(
> > >   	return error;
> > >   }
> > > +/*
> > > + * Set the attribute specified in @args.
> > > + */
> > > +int
> > > +xfs_attr_set_args(
> > > +	struct xfs_da_args		*args)
> > > +{
> > > +	struct xfs_buf			*leaf_bp = NULL;
> > > +	int				error = 0;
> > > +	struct xfs_delattr_context	dac = {
> > > +		.da_args	= args,
> > > +	};
> > > +
> > > +	do {
> > > +		error = xfs_attr_set_iter(&dac, &leaf_bp);
> > > +		if (error != -EAGAIN)
> > > +			break;
> > > +
> > > +		error = xfs_attr_trans_roll(&dac);
> > > +		if (error)
> > > +			return error;
> > > +	} while (true);
> > > +
> > > +	return error;
> > > +}
> > > +
> > >   STATIC int
> > >   xfs_attr_set_fmt(
> > > -	struct xfs_da_args	*args)
> > > +	struct xfs_delattr_context	*dac,
> > > +	struct xfs_buf			**leaf_bp)
> > >   {
> > > -	struct xfs_buf          *leaf_bp = NULL;
> > > -	struct xfs_inode	*dp = args->dp;
> > > -	int			error2, error = 0;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_inode		*dp = args->dp;
> > > +	int				error = 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;
> > > -	}
> > > +
> > > +	/* Should only be 0, -EEXIST or -ENOSPC */
> > > +	if (error != -ENOSPC)
> > > +		return error;
> > 
> > Ok, so it looks like the commit that goes away here is replaced by one
> > up the call stack.
> > 
> > >   	/*
> > >   	 * 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);
> > > +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
> > >   	if (error)
> > >   		return error;
> > > @@ -277,95 +304,139 @@ xfs_attr_set_fmt(
> > >   	 * concurrent AIL push cannot grab the half-baked leaf buffer
> > >   	 * and run into problems with the write verifier.
> > >   	 */
> > > -	xfs_trans_bhold(args->trans, leaf_bp);
> > > -	error = xfs_defer_finish(&args->trans);
> > > -	xfs_trans_bhold_release(args->trans, leaf_bp);
> > > -	if (error) {
> > > -		xfs_trans_brelse(args->trans, leaf_bp);
> > 
> > What about the xfs_trans_brelse() in the error case that seems to
> > disappear?
> Hmm, maybe we could add some handler in xfs_attr_set_args such that if the
> roll fails, we release the leaf_bp if it is not null?

I suspect we need it somewhere. I don't recall the details of this code
off the top of my head, but I remember it being tricky so we should
probably work to preserve current logic unless there's a clear reason to
do otherwise.

> > 
> > > -		return error;
> > > -	}
> > > +	xfs_trans_bhold(args->trans, *leaf_bp);
> > > +	/*
> > > +	 * We're still in XFS_DAS_UNINIT state here.  We've converted
> > > +	 * the attr fork to leaf format and will restart with the leaf
> > > +	 * add.
> > > +	 */
> > > +	dac->flags |= XFS_DAC_DEFER_FINISH;
> > >   	return -EAGAIN;
> > >   }
> > >   /*
> > >    * Set the attribute specified in @args.
> > > + * This routine is meant to function as a delayed operation, and may return
> > > + * -EAGAIN when the transaction needs to be rolled.  Calling functions will need
> > > + * to handle this, and recall the function until a successful error code is
> > > + * returned.
> > >    */
> > >   int
> > > -xfs_attr_set_args(
> > > -	struct xfs_da_args	*args)
> > > +xfs_attr_set_iter(
> > > +	struct xfs_delattr_context	*dac,
> > > +	struct xfs_buf			**leaf_bp)
> > >   {
> > > -	struct xfs_inode	*dp = args->dp;
> > > -	struct xfs_buf		*bp = NULL;
> > > -	struct xfs_da_state     *state = NULL;
> > > -	int			forkoff, error = 0;
> > > +	struct xfs_da_args              *args = dac->da_args;
> > > +	struct xfs_inode		*dp = args->dp;
> > > +	struct xfs_buf			*bp = NULL;
> > > +	struct xfs_da_state		*state = NULL;
> > > +	int				forkoff, error = 0;
> > > -	/*
> > > -	 * 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 (xfs_attr_is_shortform(dp)) {
> > > -		error = xfs_attr_set_fmt(args);
> > > -		if (error != -EAGAIN)
> > > -			return error;
> > > -	}
> > > +	/* State machine switch */
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_UNINIT:
> > > +		if (xfs_attr_is_shortform(dp))
> > > +			return xfs_attr_set_fmt(dac, leaf_bp);
> > > -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > -		error = xfs_attr_leaf_try_add(args, bp);
> > > -		if (error == -ENOSPC) {
> > > -			/*
> > > -			 * Promote the attribute list to the Btree format.
> > > -			 */
> > > -			error = xfs_attr3_leaf_to_node(args);
> > > +		/*
> > > +		 * After a shortform to leaf conversion, we need to hold the
> > > +		 * leaf and cycle out the transaction.  When we get back,
> > > +		 * we need to release the leaf to release the hold on the leaf
> > > +		 * buffer.
> > > +		 */
> > > +		if (*leaf_bp != NULL) {
> > > +			xfs_trans_bhold_release(args->trans, *leaf_bp);
> > > +			*leaf_bp = NULL;
> > > +		}
> > > +
> > > +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> > > +			error = xfs_attr_leaf_try_add(args, *leaf_bp);
> > > +			if (error == -ENOSPC) {
> > > +				/*
> > > +				 * Promote the attribute list to the Btree
> > > +				 * format.
> > > +				 */
> > > +				error = xfs_attr3_leaf_to_node(args);
> > > +				if (error)
> > > +					return error;
> > > +
> > > +				/*
> > > +				 * Finish any deferred work items and roll the
> > > +				 * transaction once more.  The goal here is to
> > > +				 * call node_addname with the inode and
> > > +				 * transaction in the same state (inode locked
> > > +				 * and joined, transaction clean) no matter how
> > > +				 * we got to this step.
> > > +				 *
> > > +				 * At this point, we are still in
> > > +				 * XFS_DAS_UNINIT, but when we come back, we'll
> > > +				 * be a node, so we'll fall down into the node
> > > +				 * handling code below
> > > +				 */
> > > +				dac->flags |= XFS_DAC_DEFER_FINISH;
> > > +				return -EAGAIN;
> > > +			}
> > > +			else if (error)
> > 
> > Nit:			} else if (error)
> > 
> Sure, will fix
> 
> > > +				return error;
> > > +		}
> > > +		else {
> > 
> > Nit:		} else {
> will fix
> 
> > 
> > > +			error = xfs_attr_node_addname_find_attr(dac);
> > >   			if (error)
> > >   				return error;
> > > -			/*
> > > -			 * Finish any deferred work items and roll the transaction once
> > > -			 * more.  The goal here is to call node_addname with the inode
> > > -			 * and transaction in the same state (inode locked and joined,
> > > -			 * transaction clean) no matter how we got to this step.
> > > -			 */
> > > -			error = xfs_defer_finish(&args->trans);
> > > +			error = xfs_attr_node_addname(dac);
> > >   			if (error)
> > >   				return error;
> > 
> > Ok, so these couple of node calls get peeled out of the loop that
> > existed prior to this patch and xfs_attr_node_addname() returns -EAGAIN
> > to trigger reentry, if necessary.
> Right, that is the idea
> 
> > 
> > >   			/*
> > > -			 * Commit the current trans (including the inode) and
> > > -			 * start a new one.
> > > +			 * If addname was successful, and we dont need to alloc
> > > +			 * anymore blks, we're done.
> > >   			 */
> > > -			error = xfs_trans_roll_inode(&args->trans, dp);
> > > -			if (error)
> > > +			if (!args->rmtblkno && !args->rmtblkno2)
> > >   				return error;
> > 
> > Is this check new? What about clearing flags and whatnot?
> gosh, I remember putting this in, and now I'm struggling to remember the why
> that lead to it.  It's needed later in the set, but at this point its sort
> of an optimization.  In the xfs_attr_leaf_try_add, in the case of a rename,
> the blocks are saved for later processing.  If no blocks were saved, there's
> nothing to flip, so the idea is we can stop here. The extra go around isnt a
> big deal at this point, but it's a problem in delayed attrs, because you end
> up with an extra empty log entry.  in the extended set, we fail an assertion
> with out it:
> Assertion failed: !list_empty(&cil->xc_cil), file: fs/xfs/xfs_log_cil.c,
> line: 907
> 
> On my set up, we seem to get away without it at this point in the set, but
> it does have to go in eventually.  I think I must have worked through this
> bug at one time, and then placed the fix in this patch, as it seems to be a
> requirement for becoming "delay ready"
> 

Hmm, that seems like an odd failure to associate with this code. I guess
it's hard to grok because the context (i.e. delayed attrs) comes a bit
later. If possible, it might be wise to defer this hunk until where it's
necessary so the context/purpose is more clear on review (it also might
be helpful to explain the purpose in a bit more detail in the comment).

> 
> 
> > 
> > > -			goto node;
> > > -		}
> > > -		else if (error) {
> > > -			return error;
> > > +			dac->dela_state = XFS_DAS_FOUND_NBLK;
> > > +			return -EAGAIN;
> > >   		}
> > > -		/*
> > > -		 * 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;
> > > +		dac->dela_state = XFS_DAS_FOUND_LBLK;
> > > +		return -EAGAIN;
> > 
> > Is there a reason the node branch sets the state in the branch but the
> > leaf branch falls out to here? I.e., could we not be consistent and have
> > 
> > 	if (one_block()) {
> > 		...
> > 		dac->dela_state = XFS_DAS_FOUND_LBLK;
> > 	} else {
> > 		...
> > 		dac->dela_state = XFS_DAS_FOUND_NBLK;
> > 	}
> > 	
> > 	return -EAGAIN;
> It's just sort of left over from its originally linear code flow that just
> sort of fell through in the the leaf logic. I think what you are proposing
> is logically analogous tho.  Does your example mean to add an extra if/else
> at the end here?  Or to tuck the return up into the the existing if/else?
> Both work, I am fine with either.
> 

Yes, the suggestion was intended to be logically equivalent. I think it
improves readability and is slightly less fragile to make the duplicated
code (i.e. return -EAGAIN) common and the state assignment as part of
the associated branch.

Brian

> > 
> > > +        case XFS_DAS_FOUND_LBLK:
> > >   		/*
> > >   		 * If there was an out-of-line value, allocate the blocks we
> > >   		 * identified for its storage and copy the value.  This is done
> > >   		 * after we create 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_rmtval_set(args);
> > > +
> > > +		/* Open coded xfs_attr_rmtval_set without trans handling */
> > > +		if ((dac->flags & XFS_DAC_LEAF_ADDNAME_INIT) == 0) {
> > > +			dac->flags |= XFS_DAC_LEAF_ADDNAME_INIT;
> > > +			if (args->rmtblkno > 0) {
> > > +				error = xfs_attr_rmtval_find_space(dac);
> > > +				if (error)
> > > +					return error;
> > > +			}
> > > +		}
> > > +
> > > +		/*
> > > +		 * Roll through the "value", allocating blocks on disk as
> > > +		 * required.
> > > +		 */
> > > +		if (dac->blkcnt > 0) {
> > > +			error = xfs_attr_rmtval_set_blk(dac);
> > >   			if (error)
> > >   				return error;
> > > +
> > > +			return -EAGAIN;
> > >   		}
> > > +		error = xfs_attr_rmtval_set_value(args);
> > > +		if (error)
> > > +			return error;
> > > +
> > >   		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
> > >   			/*
> > >   			 * Added a "remote" value, just clear the incomplete
> > > @@ -394,22 +465,26 @@ xfs_attr_set_args(
> > >   		 * 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;
> > > +	case XFS_DAS_FLIP_LFLAG:
> > >   		/*
> > >   		 * Dismantle the "old" attribute/value pair by removing a
> > >   		 * "remote" value (if it exists).
> > >   		 */
> > >   		xfs_attr_restore_rmt_blk(args);
> > > -		if (args->rmtblkno) {
> > > -			error = xfs_attr_rmtval_invalidate(args);
> > > -			if (error)
> > > -				return error;
> > > +		error = xfs_attr_rmtval_invalidate(args);
> > > +		if (error)
> > > +			return error;
> > > +
> > > +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
> > > +		dac->dela_state = XFS_DAS_RM_LBLK;
> > > -			error = xfs_attr_rmtval_remove(args);
> > > +		/* fallthrough */
> > > +	case XFS_DAS_RM_LBLK:
> > > +		if (args->rmtblkno) {
> > > +			error = __xfs_attr_rmtval_remove(dac);
> > >   			if (error)
> > >   				return error;
> > >   		}
> > 
> > This looks like a similar scenario as before where
> > xfs_attr_rmtval_remove() would have returned with a clean transaction
> > after the final unmap, but here __xfs_attr_rmtval_remove() just returns
> > 0 if done == 1. We probably need to roll one more time out of this
> > branch and land in a subsequent state..?
> I see, ok will add extra -EAGAIN here.
> 
> 
> > 
> > > @@ -434,91 +509,114 @@ xfs_attr_set_args(
> > >   			/* bp is gone due to xfs_da_shrink_inode */
> > >   		return error;
> > > -	}
> > > -node:
> > > +	case XFS_DAS_FOUND_NBLK:
> > > +		/*
> > > +		 * If there was an out-of-line value, allocate the blocks we
> > > +		 * identified for its storage and copy the value.  This is done
> > > +		 * after we create the attribute so that we don't overflow the
> > > +		 * maximum size of a transaction and/or hit a deadlock.
> > > +		 */
> > > +		if (args->rmtblkno > 0) {
> > > +			/*
> > > +			 * Open coded xfs_attr_rmtval_set without trans
> > > +			 * handling
> > > +			 */
> > > +			error = xfs_attr_rmtval_find_space(dac);
> > > +			if (error)
> > > +				return error;
> > > -	do {
> > > -		error = xfs_attr_node_addname_find_attr(args, &state);
> > > -		if (error)
> > > -			return error;
> > > -		error = xfs_attr_node_addname(args, state);
> > > -	} while (error == -EAGAIN);
> > > -	if (error)
> > > -		return error;
> > > +			/*
> > > +			 * Roll through the "value", allocating blocks on disk
> > > +			 * as required.  Set the state in case of -EAGAIN return
> > > +			 * code
> > > +			 */
> > > +			dac->dela_state = XFS_DAS_ALLOC_NODE;
> > > +		}
> > > -	/*
> > > -	 * 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;
> > > +		/* fallthrough */
> > > +	case XFS_DAS_ALLOC_NODE:
> > > +		if (args->rmtblkno > 0) {
> > > +			if (dac->blkcnt > 0) {
> > > +				error = xfs_attr_rmtval_set_blk(dac);
> > > +				if (error)
> > > +					return error;
> > > -	/*
> > > -	 * If there was an out-of-line value, allocate the blocks we
> > > -	 * identified for its storage and copy the value.  This is done
> > > -	 * after we create 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_rmtval_set(args);
> > > -		if (error)
> > > -			return error;
> > > -	}
> > > +				return -EAGAIN;
> > > +			}
> > > +
> > > +			error = xfs_attr_rmtval_set_value(args);
> > > +			if (error)
> > > +				return error;
> > > +		}
> > > +
> > > +		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
> > > +			/*
> > > +			 * Added a "remote" value, just clear the incomplete
> > > +			 * flag.
> > > +			 */
> > > +			if (args->rmtblkno > 0)
> > > +				error = xfs_attr3_leaf_clearflag(args);
> > > +			goto out;
> > > +		}
> > > -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
> > >   		/*
> > > -		 * Added a "remote" value, just clear the incomplete flag.
> > > +		 * If this is an atomic rename operation, we must "flip" the
> > > +		 * incomplete flags on the "new" and "old" attribute/value pairs
> > > +		 * so that one disappears and one appears atomically.  Then we
> > > +		 * must remove the "old" attribute/value pair.
> > > +		 *
> > > +		 * In a separate transaction, set the incomplete flag on the
> > > +		 * "old" attr and clear the incomplete flag on the "new" attr.
> > >   		 */
> > > -		if (args->rmtblkno > 0)
> > > -			error = xfs_attr3_leaf_clearflag(args);
> > > -		goto out;
> > > -	}
> > > -
> > > -	/*
> > > -	 * If this is an atomic rename operation, we must "flip" the incomplete
> > > -	 * flags on the "new" and "old" attribute/value pairs so that one
> > > -	 * disappears and one appears atomically.  Then we must remove the "old"
> > > -	 * attribute/value pair.
> > > -	 *
> > > -	 * In a separate transaction, set the incomplete flag on the "old" attr
> > > -	 * and clear the incomplete flag on the "new" attr.
> > > -	 */
> > > -	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;
> > > +		error = xfs_attr3_leaf_flipflags(args);
> > > +		if (error)
> > > +			goto out;
> > > +		/*
> > > +		 * Commit the flag value change and start the next trans in
> > > +		 * series
> > > +		 */
> > > +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> > > +		return -EAGAIN;
> > > -	/*
> > > -	 * Dismantle the "old" attribute/value pair by removing a "remote" value
> > > -	 * (if it exists).
> > > -	 */
> > > -	xfs_attr_restore_rmt_blk(args);
> > > +	case XFS_DAS_FLIP_NFLAG:
> > > +		/*
> > > +		 * Dismantle the "old" attribute/value pair by removing a
> > > +		 * "remote" value (if it exists).
> > > +		 */
> > > +		xfs_attr_restore_rmt_blk(args);
> > > -	if (args->rmtblkno) {
> > >   		error = xfs_attr_rmtval_invalidate(args);
> > >   		if (error)
> > >   			return error;
> > > -		error = xfs_attr_rmtval_remove(args);
> > > -		if (error)
> > > -			return error;
> > > -	}
> > > +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
> > > +		dac->dela_state = XFS_DAS_RM_NBLK;
> > > +
> > > +		/* fallthrough */
> > > +	case XFS_DAS_RM_NBLK:
> > > +		if (args->rmtblkno) {
> > > +			error = __xfs_attr_rmtval_remove(dac);
> > > +			if (error)
> > > +				return error;
> > > +		}
> > 
> > Similar thing here with __xfs_attr_rmtval_remove()..?
> Sure, will add one more -EAGAIN
> 
> > 
> > > +
> > > +		error = xfs_attr_node_addname_clear_incomplete(dac);
> > > -	error = xfs_attr_node_addname_clear_incomplete(args);
> > >   out:
> > > -	if (state)
> > > -		xfs_da_state_free(state);
> > > -	return error;
> > > +		if (state)
> > > +			xfs_da_state_free(state);
> > > +		return error;
> > 
> > Can we avoid this out label landing inside the switch statement? That
> > looks like a landmine. Even if we just duplicated an 'done_out' path
> > after the last return in the function, I think that would be preferable.
> Sure, can do, that seems like a simple thing to tack on
> 
> > 
> > All previous feedback aside, I think this patch now looks much more
> > digestable in general. Most of the state code is isolated to the _iter()
> > function and so it's much easier to follow along and compare against the
> > current code flow. I did still have some thoughts with regard to further
> > cleanups, possibly clearing up some the logic and/or tweaking the states
> > and whatnot, but I think this is at a point where it might be reasonable
> > to make such changes on top of this patch instead of continuing to make
> > significant changes to it. If I get a chance perhaps I'll take a closer
> > look at that once the remaining kinks are worked out..
> > 
> > Brian
> Ok, I will get these last bits updated here.  I still need to check into the
> issues Darrick is seeing on his set up, but it sounds like we've found an
> arrangement people like.  And yes, I think switching to cleanups on top is a
> good next step.  Thanks for the reviews!
> 
> Allison
> 
> > 
> > > +
> > > +	default:
> > > +		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
> > > +		break;
> > > +	}
> > > +	return error;
> > >   }
> > > +
> > >   /*
> > >    * Return EEXIST if attr is found, or ENOATTR if not
> > >    */
> > > @@ -984,18 +1082,18 @@ xfs_attr_node_hasname(
> > >   STATIC int
> > >   xfs_attr_node_addname_find_attr(
> > > -	struct xfs_da_args	*args,
> > > -	struct xfs_da_state     **state)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	int			retval;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	int				retval;
> > >   	/*
> > >   	 * Search to see if name already exists, and get back a pointer
> > >   	 * to where it should go.
> > >   	 */
> > > -	retval = xfs_attr_node_hasname(args, state);
> > > +	retval = xfs_attr_node_hasname(args, &dac->da_state);
> > >   	if (retval != -ENOATTR && retval != -EEXIST)
> > > -		goto error;
> > > +		return retval;
> > >   	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
> > >   		goto error;
> > > @@ -1021,8 +1119,8 @@ xfs_attr_node_addname_find_attr(
> > >   	return 0;
> > >   error:
> > > -	if (*state)
> > > -		xfs_da_state_free(*state);
> > > +	if (dac->da_state)
> > > +		xfs_da_state_free(dac->da_state);
> > >   	return retval;
> > >   }
> > > @@ -1035,20 +1133,24 @@ xfs_attr_node_addname_find_attr(
> > >    *
> > >    * "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_da_state	*state)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	struct xfs_da_state_blk	*blk;
> > > -	struct xfs_inode	*dp;
> > > -	int			error;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_da_state		*state = dac->da_state;
> > > +	struct xfs_da_state_blk		*blk;
> > > +	int				error;
> > >   	trace_xfs_attr_node_addname(args);
> > > -	dp = args->dp;
> > > -	blk = &state->path.blk[state->path.active-1];
> > > +	blk = &state->path.blk[ state->path.active-1 ];
> > >   	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
> > >   	error = xfs_attr3_leaf_add(blk->bp, state->args);
> > > @@ -1064,18 +1166,15 @@ 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.
> > > +			 * Now that we have converted the leaf to a node, we can
> > > +			 * roll the transaction, and try xfs_attr3_leaf_add
> > > +			 * again on re-entry.  No need to set dela_state to do
> > > +			 * this. dela_state is still unset by this function at
> > > +			 * this point.
> > >   			 */
> > > -			error = xfs_trans_roll_inode(&args->trans, dp);
> > > -			if (error)
> > > -				goto out;
> > > -
> > > +			dac->flags |= XFS_DAC_DEFER_FINISH;
> > >   			return -EAGAIN;
> > >   		}
> > > @@ -1088,9 +1187,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.
> > > @@ -1105,8 +1202,9 @@ xfs_attr_node_addname(
> > >   STATIC
> > >   int xfs_attr_node_addname_clear_incomplete(
> > > -	struct xfs_da_args		*args)
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > +	struct xfs_da_args		*args = dac->da_args;
> > >   	struct xfs_da_state		*state = NULL;
> > >   	struct xfs_da_state_blk		*blk;
> > >   	int				retval = 0;
> > > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > > index 92a6a50..4e4233d 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.h
> > > +++ b/fs/xfs/libxfs/xfs_attr.h
> > > @@ -159,6 +159,233 @@ struct xfs_attr_list_context {
> > >    *              v
> > >    *            done
> > >    *
> > > + *
> > > + * Below is a state machine diagram for attr set operations.
> > > + *
> > > + * It seems the challenge with understanding this system comes from trying to
> > > + * absorb the state machine all at once, when really one should only be looking
> > > + * at it with in the context of a single function. Once a state sensitive
> > > + * function is called, the idea is that it "takes ownership" of the
> > > + * state machine. It isn't concerned with the states that may have belonged to
> > > + * it's calling parent. Only the states relevant to itself or any other
> > > + * subroutines there in. Once a calling function hands off the state machine to
> > > + * a subroutine, it needs to respect the simple rule that it doesn't "own" the
> > > + * state machine anymore, and it's the responsibility of that calling function
> > > + * to propagate the -EAGAIN back up the call stack. Upon reentry, it is
> > > + * committed to re-calling that subroutine until it returns something other than
> > > + * -EAGAIN. Once that subroutine signals completion (by returning anything other
> > > + * than -EAGAIN), the calling function can resume using the state machine.
> > > + *
> > > + *  xfs_attr_set_iter()
> > > + *              │
> > > + *              v
> > > + *   ┌─y─ has an attr fork?
> > > + *   │          |
> > > + *   │          n
> > > + *   │          |
> > > + *   │          V
> > > + *   │       add a fork
> > > + *   │          │
> > > + *   └──────────┤
> > > + *              │
> > > + *              V
> > > + *   ┌─y─ is shortform?
> > > + *   │          │
> > > + *   │          V
> > > + *   │   xfs_attr_set_fmt
> > > + *   │          |
> > > + *   │          V
> > > + *   │ xfs_attr_try_sf_addname
> > > + *   │          │
> > > + *   │          V
> > > + *   │      had enough ──y──> done
> > > + *   │        space?
> > > + *   n          │
> > > + *   │          n
> > > + *   │          │
> > > + *   │          V
> > > + *   │   transform to leaf
> > > + *   │          │
> > > + *   │          V
> > > + *   │   hold the leaf buffer
> > > + *   │          │
> > > + *   │          V
> > > + *   │     return -EAGAIN
> > > + *   │      Re-enter in
> > > + *   │       leaf form
> > > + *   │
> > > + *   └─> release leaf buffer
> > > + *          if needed
> > > + *              │
> > > + *              V
> > > + *   ┌───n── fork has
> > > + *   │      only 1 blk?
> > > + *   │          │
> > > + *   │          y
> > > + *   │          │
> > > + *   │          v
> > > + *   │ xfs_attr_leaf_try_add()
> > > + *   │          │
> > > + *   │          v
> > > + *   │      had enough ──────────────y───────────────???
> > > + *   │        space?                                 │
> > > + *   │          │                                    │
> > > + *   │          n                                    │
> > > + *   │          │                                    │
> > > + *   │          v                                    │
> > > + *   │    return -EAGAIN                             │
> > > + *   │      re-enter in                              │
> > > + *   │        node form                              │
> > > + *   │          │                                    │
> > > + *   └──────────┤                                    │
> > > + *              │                                    │
> > > + *              V                                    │
> > > + * xfs_attr_node_addname_find_attr                   │
> > > + *        determines if this                         │
> > > + *       is create or rename                         │
> > > + *     find space to store attr                      │
> > > + *              │                                    │
> > > + *              v                                    │
> > > + *     xfs_attr_node_addname                         │
> > > + *              │                                    │
> > > + *              v                                    │
> > > + *   fits in a node leaf? ────n─────???                │
> > > + *              │     ^             v                │
> > > + *              │     │        single leaf node?     │
> > > + *              │     │          │            │      │
> > > + *              y     │          y            n      │
> > > + *              │     │          │            │      │
> > > + *              v     │          v            v      │
> > > + *            update  │     grow the leaf  split if  │
> > > + *           hashvals └─── return -EAGAIN   needed   │
> > > + *              │          retry leaf add     │      │
> > > + *              │            on reentry       │      │
> > > + *              ├─────────────────────────────┘      │
> > > + *              │                                    │
> > > + *              v                                    │
> > > + *         need to alloc                             │
> > > + *   ┌─y── or flip flag?                             │
> > > + *   │          │                                    │
> > > + *   │          n                                    │
> > > + *   │          │                                    │
> > > + *   │          v                                    │
> > > + *   │         done                                  │
> > > + *   │                                               │
> > > + *   │                                               │
> > > + *   │         XFS_DAS_FOUND_LBLK <──────────────────┘
> > > + *   │                  │
> > > + *   │                  V
> > > + *   │        xfs_attr_leaf_addname()
> > > + *   │                  │
> > > + *   │                  v
> > > + *   │      ┌──first time through?
> > > + *   │      │          │
> > > + *   │      │          y
> > > + *   │      │          │
> > > + *   │      n          v
> > > + *   │      │    if we have rmt blks
> > > + *   │      │    find space for them
> > > + *   │      │          │
> > > + *   │      └──────────┤
> > > + *   │                 │
> > > + *   │                 v
> > > + *   │            still have
> > > + *   │      ┌─n─ blks to alloc? <──???
> > > + *   │      │          │           │
> > > + *   │      │          y           │
> > > + *   │      │          │           │
> > > + *   │      │          v           │
> > > + *   │      │     alloc one blk    │
> > > + *   │      │     return -EAGAIN ──┘
> > > + *   │      │    re-enter with one
> > > + *   │      │    less blk to alloc
> > > + *   │      │
> > > + *   │      │
> > > + *   │      └───> set the rmt
> > > + *   │               value
> > > + *   │                 │
> > > + *   │                 v
> > > + *   │               was this
> > > + *   │              a rename? ──n─???
> > > + *   │                 │          │
> > > + *   │                 y          │
> > > + *   │                 │          │
> > > + *   │                 v          │
> > > + *   │           flip incomplete  │
> > > + *   │               flag         │
> > > + *   │                 │          │
> > > + *   │                 v          │
> > > + *   │         XFS_DAS_FLIP_LFLAG │
> > > + *   │                 │          │
> > > + *   │                 v          │
> > > + *   │               remove       │
> > > + *   │        ┌───> old name      │
> > > + *   │        │        │          │
> > > + *   │ XFS_DAS_RM_LBLK │          │
> > > + *   │        ^        │          │
> > > + *   │        │        v          │
> > > + *   │        └──y── more to      │
> > > + *   │               remove       │
> > > + *   │                 │          │
> > > + *   │                 n          │
> > > + *   │                 │          │
> > > + *   │                 v          │
> > > + *   │                done <──────┘
> > > + *   │
> > > + *   └──────> XFS_DAS_FOUND_NBLK
> > > + *                     │
> > > + *                     v
> > > + *       ┌─────n──  need to
> > > + *       │        alloc blks?
> > > + *       │             │
> > > + *       │             y
> > > + *       │             │
> > > + *       │             v
> > > + *       │        find space
> > > + *       │             │
> > > + *       │             v
> > > + *       │  ┌─>XFS_DAS_ALLOC_NODE
> > > + *       │  │          │
> > > + *       │  │          v
> > > + *       │  │      alloc blk
> > > + *       │  │          │
> > > + *       │  │          v
> > > + *       │  └──y── need to alloc
> > > + *       │         more blocks?
> > > + *       │             │
> > > + *       │             n
> > > + *       │             │
> > > + *       │             v
> > > + *       │      set the rmt value
> > > + *       │             │
> > > + *       │             v
> > > + *       │          was this
> > > + *       └────────> a rename? ──n─???
> > > + *                     │          │
> > > + *                     y          │
> > > + *                     │          │
> > > + *                     v          │
> > > + *               flip incomplete  │
> > > + *                   flag         │
> > > + *                     │          │
> > > + *                     v          │
> > > + *             XFS_DAS_FLIP_NFLAG │
> > > + *                     │          │
> > > + *                     v          │
> > > + *                   remove       │
> > > + *        ┌────────> old name     │
> > > + *        │            │          │
> > > + *  XFS_DAS_RM_NBLK    │          │
> > > + *        ^            │          │
> > > + *        │            v          │
> > > + *        └──────y── more to      │
> > > + *                   remove       │
> > > + *                     │          │
> > > + *                     n          │
> > > + *                     │          │
> > > + *                     v          │
> > > + *                    done <──────┘
> > > + *
> > >    */
> > >   /*
> > > @@ -174,12 +401,20 @@ enum xfs_delattr_state {
> > >   	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
> > >   	XFS_DAS_RMTBLK,		      /* Removing remote blks */
> > >   	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
> > > +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
> > > +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
> > > +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
> > > +	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 */
> > >   };
> > >   /*
> > >    * Defines for xfs_delattr_context.flags
> > >    */
> > >   #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
> > > +#define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
> > >   /*
> > >    * Context used for keeping track of delayed attribute operations
> > > @@ -187,6 +422,11 @@ enum xfs_delattr_state {
> > >   struct xfs_delattr_context {
> > >   	struct xfs_da_args      *da_args;
> > > +	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
> > > +	struct xfs_bmbt_irec	map;
> > > +	xfs_dablk_t		lblkno;
> > > +	int			blkcnt;
> > > +
> > >   	/* Used in xfs_attr_node_removename to roll through removing blocks */
> > >   	struct xfs_da_state     *da_state;
> > > @@ -213,7 +453,6 @@ 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);
> > > -int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
> > >   bool xfs_attr_namecheck(const void *name, size_t length);
> > >   void xfs_delattr_context_init(struct xfs_delattr_context *dac,
> > >   			      struct xfs_da_args *args);
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > > index 908521e7..fc71f10 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > > @@ -439,9 +439,9 @@ xfs_attr_rmtval_get(
> > >   /*
> > >    * Find a "hole" in the attribute address space large enough for us to drop the
> > > - * new attribute's value into
> > > + * new attributes value into
> > >    */
> > > -STATIC int
> > > +int
> > >   xfs_attr_rmt_find_hole(
> > >   	struct xfs_da_args	*args)
> > >   {
> > > @@ -468,7 +468,7 @@ xfs_attr_rmt_find_hole(
> > >   	return 0;
> > >   }
> > > -STATIC int
> > > +int
> > >   xfs_attr_rmtval_set_value(
> > >   	struct xfs_da_args	*args)
> > >   {
> > > @@ -628,6 +628,69 @@ 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_find_space(
> > > +	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->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 0;
> > > +}
> > > +
> > > +/*
> > > + * 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.
> > >    */
> > > @@ -669,37 +732,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)
> > > -{
> > > -	int				error;
> > > -	struct xfs_delattr_context	dac  = {
> > > -		.da_args	= args,
> > > -	};
> > > -
> > > -	trace_xfs_attr_rmtval_remove(args);
> > > -
> > > -	/*
> > > -	 * Keep de-allocating extents until the remote-value region is gone.
> > > -	 */
> > > -	do {
> > > -		error = __xfs_attr_rmtval_remove(&dac);
> > > -		if (error != -EAGAIN)
> > > -			break;
> > > -
> > > -		error = xfs_attr_trans_roll(&dac);
> > > -		if (error)
> > > -			return error;
> > > -	} while (true);
> > > -
> > > -	return error;
> > > -}
> > > -
> > > -/*
> > >    * 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 re-call the function
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
> > > index 002fd30..8ad68d5 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.h
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.h
> > > @@ -10,9 +10,12 @@ int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
> > >   int xfs_attr_rmtval_get(struct xfs_da_args *args);
> > >   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);
> > >   int __xfs_attr_rmtval_remove(struct xfs_delattr_context *dac);
> > > +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_find_space(struct xfs_delattr_context *dac);
> > >   #endif /* __XFS_ATTR_REMOTE_H__ */
> > > diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> > > index e74bbb6..0c16d46 100644
> > > --- a/fs/xfs/xfs_trace.h
> > > +++ b/fs/xfs/xfs_trace.h
> > > @@ -1944,7 +1944,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] 44+ messages in thread

* Re: [PATCH v16 11/11] xfs: Add delay ready attr set routines
  2021-04-05 13:17       ` Brian Foster
@ 2021-04-14 18:14         ` Allison Henderson
  0 siblings, 0 replies; 44+ messages in thread
From: Allison Henderson @ 2021-04-14 18:14 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 4/5/21 6:17 AM, Brian Foster wrote:
> On Fri, Apr 02, 2021 at 02:01:20AM -0700, Allison Henderson wrote:
>>
>>
>> On 4/1/21 9:57 AM, Brian Foster wrote:
>>> On Thu, Mar 25, 2021 at 05:33:08PM -0700, Allison Henderson 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. See xfs_attr.h for a more detailed diagram of the states.
>>>>
>>>> Two new helper functions have been added: xfs_attr_rmtval_find_space 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.
>>>>
>>>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>>>> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
>>>> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
>>>> ---
>>>>    fs/xfs/libxfs/xfs_attr.c        | 446 ++++++++++++++++++++++++----------------
>>>>    fs/xfs/libxfs/xfs_attr.h        | 241 +++++++++++++++++++++-
>>>>    fs/xfs/libxfs/xfs_attr_remote.c | 100 ++++++---
>>>>    fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
>>>>    fs/xfs/xfs_trace.h              |   1 -
>>>>    5 files changed, 582 insertions(+), 211 deletions(-)
>>>>
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>>> index 4a73691..6a86b62 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>> ...
>>>> @@ -246,29 +247,55 @@ xfs_attr_trans_roll(
>>>>    	return error;
>>>>    }
>>>> +/*
>>>> + * Set the attribute specified in @args.
>>>> + */
>>>> +int
>>>> +xfs_attr_set_args(
>>>> +	struct xfs_da_args		*args)
>>>> +{
>>>> +	struct xfs_buf			*leaf_bp = NULL;
>>>> +	int				error = 0;
>>>> +	struct xfs_delattr_context	dac = {
>>>> +		.da_args	= args,
>>>> +	};
>>>> +
>>>> +	do {
>>>> +		error = xfs_attr_set_iter(&dac, &leaf_bp);
>>>> +		if (error != -EAGAIN)
>>>> +			break;
>>>> +
>>>> +		error = xfs_attr_trans_roll(&dac);
>>>> +		if (error)
>>>> +			return error;
>>>> +	} while (true);
>>>> +
>>>> +	return error;
>>>> +}
>>>> +
>>>>    STATIC int
>>>>    xfs_attr_set_fmt(
>>>> -	struct xfs_da_args	*args)
>>>> +	struct xfs_delattr_context	*dac,
>>>> +	struct xfs_buf			**leaf_bp)
>>>>    {
>>>> -	struct xfs_buf          *leaf_bp = NULL;
>>>> -	struct xfs_inode	*dp = args->dp;
>>>> -	int			error2, error = 0;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_inode		*dp = args->dp;
>>>> +	int				error = 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;
>>>> -	}
>>>> +
>>>> +	/* Should only be 0, -EEXIST or -ENOSPC */
>>>> +	if (error != -ENOSPC)
>>>> +		return error;
>>>
>>> Ok, so it looks like the commit that goes away here is replaced by one
>>> up the call stack.
>>>
>>>>    	/*
>>>>    	 * 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);
>>>> +	error = xfs_attr_shortform_to_leaf(args, leaf_bp);
>>>>    	if (error)
>>>>    		return error;
>>>> @@ -277,95 +304,139 @@ xfs_attr_set_fmt(
>>>>    	 * concurrent AIL push cannot grab the half-baked leaf buffer
>>>>    	 * and run into problems with the write verifier.
>>>>    	 */
>>>> -	xfs_trans_bhold(args->trans, leaf_bp);
>>>> -	error = xfs_defer_finish(&args->trans);
>>>> -	xfs_trans_bhold_release(args->trans, leaf_bp);
>>>> -	if (error) {
>>>> -		xfs_trans_brelse(args->trans, leaf_bp);
>>>
>>> What about the xfs_trans_brelse() in the error case that seems to
>>> disappear?
>> Hmm, maybe we could add some handler in xfs_attr_set_args such that if the
>> roll fails, we release the leaf_bp if it is not null?
> 
> I suspect we need it somewhere. I don't recall the details of this code
> off the top of my head, but I remember it being tricky so we should
> probably work to preserve current logic unless there's a clear reason to
> do otherwise.
> 
>>>
>>>> -		return error;
>>>> -	}
>>>> +	xfs_trans_bhold(args->trans, *leaf_bp);
>>>> +	/*
>>>> +	 * We're still in XFS_DAS_UNINIT state here.  We've converted
>>>> +	 * the attr fork to leaf format and will restart with the leaf
>>>> +	 * add.
>>>> +	 */
>>>> +	dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>    	return -EAGAIN;
>>>>    }
>>>>    /*
>>>>     * Set the attribute specified in @args.
>>>> + * This routine is meant to function as a delayed operation, and may return
>>>> + * -EAGAIN when the transaction needs to be rolled.  Calling functions will need
>>>> + * to handle this, and recall the function until a successful error code is
>>>> + * returned.
>>>>     */
>>>>    int
>>>> -xfs_attr_set_args(
>>>> -	struct xfs_da_args	*args)
>>>> +xfs_attr_set_iter(
>>>> +	struct xfs_delattr_context	*dac,
>>>> +	struct xfs_buf			**leaf_bp)
>>>>    {
>>>> -	struct xfs_inode	*dp = args->dp;
>>>> -	struct xfs_buf		*bp = NULL;
>>>> -	struct xfs_da_state     *state = NULL;
>>>> -	int			forkoff, error = 0;
>>>> +	struct xfs_da_args              *args = dac->da_args;
>>>> +	struct xfs_inode		*dp = args->dp;
>>>> +	struct xfs_buf			*bp = NULL;
>>>> +	struct xfs_da_state		*state = NULL;
>>>> +	int				forkoff, error = 0;
>>>> -	/*
>>>> -	 * 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 (xfs_attr_is_shortform(dp)) {
>>>> -		error = xfs_attr_set_fmt(args);
>>>> -		if (error != -EAGAIN)
>>>> -			return error;
>>>> -	}
>>>> +	/* State machine switch */
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_UNINIT:
>>>> +		if (xfs_attr_is_shortform(dp))
>>>> +			return xfs_attr_set_fmt(dac, leaf_bp);
>>>> -	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>> -		error = xfs_attr_leaf_try_add(args, bp);
>>>> -		if (error == -ENOSPC) {
>>>> -			/*
>>>> -			 * Promote the attribute list to the Btree format.
>>>> -			 */
>>>> -			error = xfs_attr3_leaf_to_node(args);
>>>> +		/*
>>>> +		 * After a shortform to leaf conversion, we need to hold the
>>>> +		 * leaf and cycle out the transaction.  When we get back,
>>>> +		 * we need to release the leaf to release the hold on the leaf
>>>> +		 * buffer.
>>>> +		 */
>>>> +		if (*leaf_bp != NULL) {
>>>> +			xfs_trans_bhold_release(args->trans, *leaf_bp);
>>>> +			*leaf_bp = NULL;
>>>> +		}
>>>> +
>>>> +		if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>>>> +			error = xfs_attr_leaf_try_add(args, *leaf_bp);
>>>> +			if (error == -ENOSPC) {
>>>> +				/*
>>>> +				 * Promote the attribute list to the Btree
>>>> +				 * format.
>>>> +				 */
>>>> +				error = xfs_attr3_leaf_to_node(args);
>>>> +				if (error)
>>>> +					return error;
>>>> +
>>>> +				/*
>>>> +				 * Finish any deferred work items and roll the
>>>> +				 * transaction once more.  The goal here is to
>>>> +				 * call node_addname with the inode and
>>>> +				 * transaction in the same state (inode locked
>>>> +				 * and joined, transaction clean) no matter how
>>>> +				 * we got to this step.
>>>> +				 *
>>>> +				 * At this point, we are still in
>>>> +				 * XFS_DAS_UNINIT, but when we come back, we'll
>>>> +				 * be a node, so we'll fall down into the node
>>>> +				 * handling code below
>>>> +				 */
>>>> +				dac->flags |= XFS_DAC_DEFER_FINISH;
>>>> +				return -EAGAIN;
>>>> +			}
>>>> +			else if (error)
>>>
>>> Nit:			} else if (error)
>>>
>> Sure, will fix
>>
>>>> +				return error;
>>>> +		}
>>>> +		else {
>>>
>>> Nit:		} else {
>> will fix
>>
>>>
>>>> +			error = xfs_attr_node_addname_find_attr(dac);
>>>>    			if (error)
>>>>    				return error;
>>>> -			/*
>>>> -			 * Finish any deferred work items and roll the transaction once
>>>> -			 * more.  The goal here is to call node_addname with the inode
>>>> -			 * and transaction in the same state (inode locked and joined,
>>>> -			 * transaction clean) no matter how we got to this step.
>>>> -			 */
>>>> -			error = xfs_defer_finish(&args->trans);
>>>> +			error = xfs_attr_node_addname(dac);
>>>>    			if (error)
>>>>    				return error;
>>>
>>> Ok, so these couple of node calls get peeled out of the loop that
>>> existed prior to this patch and xfs_attr_node_addname() returns -EAGAIN
>>> to trigger reentry, if necessary.
>> Right, that is the idea
>>
>>>
>>>>    			/*
>>>> -			 * Commit the current trans (including the inode) and
>>>> -			 * start a new one.
>>>> +			 * If addname was successful, and we dont need to alloc
>>>> +			 * anymore blks, we're done.
>>>>    			 */
>>>> -			error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -			if (error)
>>>> +			if (!args->rmtblkno && !args->rmtblkno2)
>>>>    				return error;
>>>
>>> Is this check new? What about clearing flags and whatnot?
>> gosh, I remember putting this in, and now I'm struggling to remember the why
>> that lead to it.  It's needed later in the set, but at this point its sort
>> of an optimization.  In the xfs_attr_leaf_try_add, in the case of a rename,
>> the blocks are saved for later processing.  If no blocks were saved, there's
>> nothing to flip, so the idea is we can stop here. The extra go around isnt a
>> big deal at this point, but it's a problem in delayed attrs, because you end
>> up with an extra empty log entry.  in the extended set, we fail an assertion
>> with out it:
>> Assertion failed: !list_empty(&cil->xc_cil), file: fs/xfs/xfs_log_cil.c,
>> line: 907
>>
>> On my set up, we seem to get away without it at this point in the set, but
>> it does have to go in eventually.  I think I must have worked through this
>> bug at one time, and then placed the fix in this patch, as it seems to be a
>> requirement for becoming "delay ready"
>>
> 
> Hmm, that seems like an odd failure to associate with this code. I guess
> it's hard to grok because the context (i.e. delayed attrs) comes a bit
> later. If possible, it might be wise to defer this hunk until where it's
> necessary so the context/purpose is more clear on review (it also might
> be helpful to explain the purpose in a bit more detail in the comment).
> 
>>
>>
>>>
>>>> -			goto node;
>>>> -		}
>>>> -		else if (error) {
>>>> -			return error;
>>>> +			dac->dela_state = XFS_DAS_FOUND_NBLK;
>>>> +			return -EAGAIN;
>>>>    		}
>>>> -		/*
>>>> -		 * 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;
>>>> +		dac->dela_state = XFS_DAS_FOUND_LBLK;
>>>> +		return -EAGAIN;
>>>
>>> Is there a reason the node branch sets the state in the branch but the
>>> leaf branch falls out to here? I.e., could we not be consistent and have
>>>
>>> 	if (one_block()) {
>>> 		...
>>> 		dac->dela_state = XFS_DAS_FOUND_LBLK;
>>> 	} else {
>>> 		...
>>> 		dac->dela_state = XFS_DAS_FOUND_NBLK;
>>> 	}
>>> 	
>>> 	return -EAGAIN;
>> It's just sort of left over from its originally linear code flow that just
>> sort of fell through in the the leaf logic. I think what you are proposing
>> is logically analogous tho.  Does your example mean to add an extra if/else
>> at the end here?  Or to tuck the return up into the the existing if/else?
>> Both work, I am fine with either.
>>
> 
> Yes, the suggestion was intended to be logically equivalent. I think it
> improves readability and is slightly less fragile to make the duplicated
> code (i.e. return -EAGAIN) common and the state assignment as part of
> the associated branch.
> 
> Brian
> 
>>>
>>>> +        case XFS_DAS_FOUND_LBLK:
>>>>    		/*
>>>>    		 * If there was an out-of-line value, allocate the blocks we
>>>>    		 * identified for its storage and copy the value.  This is done
>>>>    		 * after we create 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_rmtval_set(args);
>>>> +
>>>> +		/* Open coded xfs_attr_rmtval_set without trans handling */
>>>> +		if ((dac->flags & XFS_DAC_LEAF_ADDNAME_INIT) == 0) {
>>>> +			dac->flags |= XFS_DAC_LEAF_ADDNAME_INIT;
>>>> +			if (args->rmtblkno > 0) {
>>>> +				error = xfs_attr_rmtval_find_space(dac);
>>>> +				if (error)
>>>> +					return error;
>>>> +			}
>>>> +		}
>>>> +
>>>> +		/*
>>>> +		 * Roll through the "value", allocating blocks on disk as
>>>> +		 * required.
>>>> +		 */
>>>> +		if (dac->blkcnt > 0) {
>>>> +			error = xfs_attr_rmtval_set_blk(dac);
>>>>    			if (error)
>>>>    				return error;
>>>> +
>>>> +			return -EAGAIN;
>>>>    		}
>>>> +		error = xfs_attr_rmtval_set_value(args);
>>>> +		if (error)
>>>> +			return error;
>>>> +
>>>>    		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>>>>    			/*
>>>>    			 * Added a "remote" value, just clear the incomplete
>>>> @@ -394,22 +465,26 @@ xfs_attr_set_args(
>>>>    		 * 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;
>>>> +	case XFS_DAS_FLIP_LFLAG:
>>>>    		/*
>>>>    		 * Dismantle the "old" attribute/value pair by removing a
>>>>    		 * "remote" value (if it exists).
>>>>    		 */
>>>>    		xfs_attr_restore_rmt_blk(args);
>>>> -		if (args->rmtblkno) {
>>>> -			error = xfs_attr_rmtval_invalidate(args);
>>>> -			if (error)
>>>> -				return error;
>>>> +		error = xfs_attr_rmtval_invalidate(args);
>>>> +		if (error)
>>>> +			return error;
>>>> +
>>>> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
>>>> +		dac->dela_state = XFS_DAS_RM_LBLK;
>>>> -			error = xfs_attr_rmtval_remove(args);
>>>> +		/* fallthrough */
>>>> +	case XFS_DAS_RM_LBLK:
>>>> +		if (args->rmtblkno) {
>>>> +			error = __xfs_attr_rmtval_remove(dac);
>>>>    			if (error)
>>>>    				return error;
>>>>    		}
>>>
>>> This looks like a similar scenario as before where
>>> xfs_attr_rmtval_remove() would have returned with a clean transaction
>>> after the final unmap, but here __xfs_attr_rmtval_remove() just returns
>>> 0 if done == 1. We probably need to roll one more time out of this
>>> branch and land in a subsequent state..?
>> I see, ok will add extra -EAGAIN here.
>>
>>
>>>
>>>> @@ -434,91 +509,114 @@ xfs_attr_set_args(
>>>>    			/* bp is gone due to xfs_da_shrink_inode */
>>>>    		return error;
>>>> -	}
>>>> -node:
>>>> +	case XFS_DAS_FOUND_NBLK:
>>>> +		/*
>>>> +		 * If there was an out-of-line value, allocate the blocks we
>>>> +		 * identified for its storage and copy the value.  This is done
>>>> +		 * after we create the attribute so that we don't overflow the
>>>> +		 * maximum size of a transaction and/or hit a deadlock.
>>>> +		 */
>>>> +		if (args->rmtblkno > 0) {
>>>> +			/*
>>>> +			 * Open coded xfs_attr_rmtval_set without trans
>>>> +			 * handling
>>>> +			 */
>>>> +			error = xfs_attr_rmtval_find_space(dac);
>>>> +			if (error)
>>>> +				return error;
>>>> -	do {
>>>> -		error = xfs_attr_node_addname_find_attr(args, &state);
>>>> -		if (error)
>>>> -			return error;
>>>> -		error = xfs_attr_node_addname(args, state);
>>>> -	} while (error == -EAGAIN);
>>>> -	if (error)
>>>> -		return error;
>>>> +			/*
>>>> +			 * Roll through the "value", allocating blocks on disk
>>>> +			 * as required.  Set the state in case of -EAGAIN return
>>>> +			 * code
>>>> +			 */
>>>> +			dac->dela_state = XFS_DAS_ALLOC_NODE;
>>>> +		}
>>>> -	/*
>>>> -	 * 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;
>>>> +		/* fallthrough */
>>>> +	case XFS_DAS_ALLOC_NODE:
>>>> +		if (args->rmtblkno > 0) {
>>>> +			if (dac->blkcnt > 0) {
>>>> +				error = xfs_attr_rmtval_set_blk(dac);
>>>> +				if (error)
>>>> +					return error;
>>>> -	/*
>>>> -	 * If there was an out-of-line value, allocate the blocks we
>>>> -	 * identified for its storage and copy the value.  This is done
>>>> -	 * after we create 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_rmtval_set(args);
>>>> -		if (error)
>>>> -			return error;
>>>> -	}
>>>> +				return -EAGAIN;
>>>> +			}
>>>> +
>>>> +			error = xfs_attr_rmtval_set_value(args);
>>>> +			if (error)
>>>> +				return error;
>>>> +		}
>>>> +
>>>> +		if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>>>> +			/*
>>>> +			 * Added a "remote" value, just clear the incomplete
>>>> +			 * flag.
>>>> +			 */
>>>> +			if (args->rmtblkno > 0)
>>>> +				error = xfs_attr3_leaf_clearflag(args);
>>>> +			goto out;
>>>> +		}
>>>> -	if (!(args->op_flags & XFS_DA_OP_RENAME)) {
>>>>    		/*
>>>> -		 * Added a "remote" value, just clear the incomplete flag.
>>>> +		 * If this is an atomic rename operation, we must "flip" the
>>>> +		 * incomplete flags on the "new" and "old" attribute/value pairs
>>>> +		 * so that one disappears and one appears atomically.  Then we
>>>> +		 * must remove the "old" attribute/value pair.
>>>> +		 *
>>>> +		 * In a separate transaction, set the incomplete flag on the
>>>> +		 * "old" attr and clear the incomplete flag on the "new" attr.
>>>>    		 */
>>>> -		if (args->rmtblkno > 0)
>>>> -			error = xfs_attr3_leaf_clearflag(args);
>>>> -		goto out;
>>>> -	}
>>>> -
>>>> -	/*
>>>> -	 * If this is an atomic rename operation, we must "flip" the incomplete
>>>> -	 * flags on the "new" and "old" attribute/value pairs so that one
>>>> -	 * disappears and one appears atomically.  Then we must remove the "old"
>>>> -	 * attribute/value pair.
>>>> -	 *
>>>> -	 * In a separate transaction, set the incomplete flag on the "old" attr
>>>> -	 * and clear the incomplete flag on the "new" attr.
>>>> -	 */
>>>> -	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;
>>>> +		error = xfs_attr3_leaf_flipflags(args);
>>>> +		if (error)
>>>> +			goto out;
>>>> +		/*
>>>> +		 * Commit the flag value change and start the next trans in
>>>> +		 * series
>>>> +		 */
>>>> +		dac->dela_state = XFS_DAS_FLIP_NFLAG;
>>>> +		return -EAGAIN;
>>>> -	/*
>>>> -	 * Dismantle the "old" attribute/value pair by removing a "remote" value
>>>> -	 * (if it exists).
>>>> -	 */
>>>> -	xfs_attr_restore_rmt_blk(args);
>>>> +	case XFS_DAS_FLIP_NFLAG:
>>>> +		/*
>>>> +		 * Dismantle the "old" attribute/value pair by removing a
>>>> +		 * "remote" value (if it exists).
>>>> +		 */
>>>> +		xfs_attr_restore_rmt_blk(args);
>>>> -	if (args->rmtblkno) {
>>>>    		error = xfs_attr_rmtval_invalidate(args);
>>>>    		if (error)
>>>>    			return error;
>>>> -		error = xfs_attr_rmtval_remove(args);
>>>> -		if (error)
>>>> -			return error;
>>>> -	}
>>>> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
>>>> +		dac->dela_state = XFS_DAS_RM_NBLK;
>>>> +
>>>> +		/* fallthrough */
>>>> +	case XFS_DAS_RM_NBLK:
>>>> +		if (args->rmtblkno) {
>>>> +			error = __xfs_attr_rmtval_remove(dac);
>>>> +			if (error)
>>>> +				return error;
>>>> +		}
>>>
>>> Similar thing here with __xfs_attr_rmtval_remove()..?
>> Sure, will add one more -EAGAIN
Mkay, so generic/026 picked up a hang with this.  It's because the 
condidtion that breaks this loop isnt args->rmtblkno > 0, it just error 
!= -EAGAIN, and then falling through.  I think had a comment somewhere 
in the last patch that confused people, I will update that.

In anycase, if we want another -EAGAIN, it means we have to stick in 
another state.  And ditto for the other two calls to this function.  Not 
a big deal, just an fyi so that it's not a suprise in the next revision.

Allison

>>
>>>
>>>> +
>>>> +		error = xfs_attr_node_addname_clear_incomplete(dac);
>>>> -	error = xfs_attr_node_addname_clear_incomplete(args);
>>>>    out:
>>>> -	if (state)
>>>> -		xfs_da_state_free(state);
>>>> -	return error;
>>>> +		if (state)
>>>> +			xfs_da_state_free(state);
>>>> +		return error;
>>>
>>> Can we avoid this out label landing inside the switch statement? That
>>> looks like a landmine. Even if we just duplicated an 'done_out' path
>>> after the last return in the function, I think that would be preferable.
>> Sure, can do, that seems like a simple thing to tack on
>>
>>>
>>> All previous feedback aside, I think this patch now looks much more
>>> digestable in general. Most of the state code is isolated to the _iter()
>>> function and so it's much easier to follow along and compare against the
>>> current code flow. I did still have some thoughts with regard to further
>>> cleanups, possibly clearing up some the logic and/or tweaking the states
>>> and whatnot, but I think this is at a point where it might be reasonable
>>> to make such changes on top of this patch instead of continuing to make
>>> significant changes to it. If I get a chance perhaps I'll take a closer
>>> look at that once the remaining kinks are worked out..
>>>
>>> Brian
>> Ok, I will get these last bits updated here.  I still need to check into the
>> issues Darrick is seeing on his set up, but it sounds like we've found an
>> arrangement people like.  And yes, I think switching to cleanups on top is a
>> good next step.  Thanks for the reviews!
>>
>> Allison
>>
>>>
>>>> +
>>>> +	default:
>>>> +		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
>>>> +		break;
>>>> +	}
>>>> +	return error;
>>>>    }
>>>> +
>>>>    /*
>>>>     * Return EEXIST if attr is found, or ENOATTR if not
>>>>     */
>>>> @@ -984,18 +1082,18 @@ xfs_attr_node_hasname(
>>>>    STATIC int
>>>>    xfs_attr_node_addname_find_attr(
>>>> -	struct xfs_da_args	*args,
>>>> -	struct xfs_da_state     **state)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> -	int			retval;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	int				retval;
>>>>    	/*
>>>>    	 * Search to see if name already exists, and get back a pointer
>>>>    	 * to where it should go.
>>>>    	 */
>>>> -	retval = xfs_attr_node_hasname(args, state);
>>>> +	retval = xfs_attr_node_hasname(args, &dac->da_state);
>>>>    	if (retval != -ENOATTR && retval != -EEXIST)
>>>> -		goto error;
>>>> +		return retval;
>>>>    	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>>>>    		goto error;
>>>> @@ -1021,8 +1119,8 @@ xfs_attr_node_addname_find_attr(
>>>>    	return 0;
>>>>    error:
>>>> -	if (*state)
>>>> -		xfs_da_state_free(*state);
>>>> +	if (dac->da_state)
>>>> +		xfs_da_state_free(dac->da_state);
>>>>    	return retval;
>>>>    }
>>>> @@ -1035,20 +1133,24 @@ xfs_attr_node_addname_find_attr(
>>>>     *
>>>>     * "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_da_state	*state)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> -	struct xfs_da_state_blk	*blk;
>>>> -	struct xfs_inode	*dp;
>>>> -	int			error;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_da_state		*state = dac->da_state;
>>>> +	struct xfs_da_state_blk		*blk;
>>>> +	int				error;
>>>>    	trace_xfs_attr_node_addname(args);
>>>> -	dp = args->dp;
>>>> -	blk = &state->path.blk[state->path.active-1];
>>>> +	blk = &state->path.blk[ state->path.active-1 ];
>>>>    	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
>>>>    	error = xfs_attr3_leaf_add(blk->bp, state->args);
>>>> @@ -1064,18 +1166,15 @@ 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.
>>>> +			 * Now that we have converted the leaf to a node, we can
>>>> +			 * roll the transaction, and try xfs_attr3_leaf_add
>>>> +			 * again on re-entry.  No need to set dela_state to do
>>>> +			 * this. dela_state is still unset by this function at
>>>> +			 * this point.
>>>>    			 */
>>>> -			error = xfs_trans_roll_inode(&args->trans, dp);
>>>> -			if (error)
>>>> -				goto out;
>>>> -
>>>> +			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>    			return -EAGAIN;
>>>>    		}
>>>> @@ -1088,9 +1187,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.
>>>> @@ -1105,8 +1202,9 @@ xfs_attr_node_addname(
>>>>    STATIC
>>>>    int xfs_attr_node_addname_clear_incomplete(
>>>> -	struct xfs_da_args		*args)
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>>    	struct xfs_da_state		*state = NULL;
>>>>    	struct xfs_da_state_blk		*blk;
>>>>    	int				retval = 0;
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>>>> index 92a6a50..4e4233d 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.h
>>>> +++ b/fs/xfs/libxfs/xfs_attr.h
>>>> @@ -159,6 +159,233 @@ struct xfs_attr_list_context {
>>>>     *              v
>>>>     *            done
>>>>     *
>>>> + *
>>>> + * Below is a state machine diagram for attr set operations.
>>>> + *
>>>> + * It seems the challenge with understanding this system comes from trying to
>>>> + * absorb the state machine all at once, when really one should only be looking
>>>> + * at it with in the context of a single function. Once a state sensitive
>>>> + * function is called, the idea is that it "takes ownership" of the
>>>> + * state machine. It isn't concerned with the states that may have belonged to
>>>> + * it's calling parent. Only the states relevant to itself or any other
>>>> + * subroutines there in. Once a calling function hands off the state machine to
>>>> + * a subroutine, it needs to respect the simple rule that it doesn't "own" the
>>>> + * state machine anymore, and it's the responsibility of that calling function
>>>> + * to propagate the -EAGAIN back up the call stack. Upon reentry, it is
>>>> + * committed to re-calling that subroutine until it returns something other than
>>>> + * -EAGAIN. Once that subroutine signals completion (by returning anything other
>>>> + * than -EAGAIN), the calling function can resume using the state machine.
>>>> + *
>>>> + *  xfs_attr_set_iter()
>>>> + *              │
>>>> + *              v
>>>> + *   ┌─y─ has an attr fork?
>>>> + *   │          |
>>>> + *   │          n
>>>> + *   │          |
>>>> + *   │          V
>>>> + *   │       add a fork
>>>> + *   │          │
>>>> + *   └──────────┤
>>>> + *              │
>>>> + *              V
>>>> + *   ┌─y─ is shortform?
>>>> + *   │          │
>>>> + *   │          V
>>>> + *   │   xfs_attr_set_fmt
>>>> + *   │          |
>>>> + *   │          V
>>>> + *   │ xfs_attr_try_sf_addname
>>>> + *   │          │
>>>> + *   │          V
>>>> + *   │      had enough ──y──> done
>>>> + *   │        space?
>>>> + *   n          │
>>>> + *   │          n
>>>> + *   │          │
>>>> + *   │          V
>>>> + *   │   transform to leaf
>>>> + *   │          │
>>>> + *   │          V
>>>> + *   │   hold the leaf buffer
>>>> + *   │          │
>>>> + *   │          V
>>>> + *   │     return -EAGAIN
>>>> + *   │      Re-enter in
>>>> + *   │       leaf form
>>>> + *   │
>>>> + *   └─> release leaf buffer
>>>> + *          if needed
>>>> + *              │
>>>> + *              V
>>>> + *   ┌───n── fork has
>>>> + *   │      only 1 blk?
>>>> + *   │          │
>>>> + *   │          y
>>>> + *   │          │
>>>> + *   │          v
>>>> + *   │ xfs_attr_leaf_try_add()
>>>> + *   │          │
>>>> + *   │          v
>>>> + *   │      had enough ──────────────y───────────────???
>>>> + *   │        space?                                 │
>>>> + *   │          │                                    │
>>>> + *   │          n                                    │
>>>> + *   │          │                                    │
>>>> + *   │          v                                    │
>>>> + *   │    return -EAGAIN                             │
>>>> + *   │      re-enter in                              │
>>>> + *   │        node form                              │
>>>> + *   │          │                                    │
>>>> + *   └──────────┤                                    │
>>>> + *              │                                    │
>>>> + *              V                                    │
>>>> + * xfs_attr_node_addname_find_attr                   │
>>>> + *        determines if this                         │
>>>> + *       is create or rename                         │
>>>> + *     find space to store attr                      │
>>>> + *              │                                    │
>>>> + *              v                                    │
>>>> + *     xfs_attr_node_addname                         │
>>>> + *              │                                    │
>>>> + *              v                                    │
>>>> + *   fits in a node leaf? ────n─────???                │
>>>> + *              │     ^             v                │
>>>> + *              │     │        single leaf node?     │
>>>> + *              │     │          │            │      │
>>>> + *              y     │          y            n      │
>>>> + *              │     │          │            │      │
>>>> + *              v     │          v            v      │
>>>> + *            update  │     grow the leaf  split if  │
>>>> + *           hashvals └─── return -EAGAIN   needed   │
>>>> + *              │          retry leaf add     │      │
>>>> + *              │            on reentry       │      │
>>>> + *              ├─────────────────────────────┘      │
>>>> + *              │                                    │
>>>> + *              v                                    │
>>>> + *         need to alloc                             │
>>>> + *   ┌─y── or flip flag?                             │
>>>> + *   │          │                                    │
>>>> + *   │          n                                    │
>>>> + *   │          │                                    │
>>>> + *   │          v                                    │
>>>> + *   │         done                                  │
>>>> + *   │                                               │
>>>> + *   │                                               │
>>>> + *   │         XFS_DAS_FOUND_LBLK <──────────────────┘
>>>> + *   │                  │
>>>> + *   │                  V
>>>> + *   │        xfs_attr_leaf_addname()
>>>> + *   │                  │
>>>> + *   │                  v
>>>> + *   │      ┌──first time through?
>>>> + *   │      │          │
>>>> + *   │      │          y
>>>> + *   │      │          │
>>>> + *   │      n          v
>>>> + *   │      │    if we have rmt blks
>>>> + *   │      │    find space for them
>>>> + *   │      │          │
>>>> + *   │      └──────────┤
>>>> + *   │                 │
>>>> + *   │                 v
>>>> + *   │            still have
>>>> + *   │      ┌─n─ blks to alloc? <──???
>>>> + *   │      │          │           │
>>>> + *   │      │          y           │
>>>> + *   │      │          │           │
>>>> + *   │      │          v           │
>>>> + *   │      │     alloc one blk    │
>>>> + *   │      │     return -EAGAIN ──┘
>>>> + *   │      │    re-enter with one
>>>> + *   │      │    less blk to alloc
>>>> + *   │      │
>>>> + *   │      │
>>>> + *   │      └───> set the rmt
>>>> + *   │               value
>>>> + *   │                 │
>>>> + *   │                 v
>>>> + *   │               was this
>>>> + *   │              a rename? ──n─???
>>>> + *   │                 │          │
>>>> + *   │                 y          │
>>>> + *   │                 │          │
>>>> + *   │                 v          │
>>>> + *   │           flip incomplete  │
>>>> + *   │               flag         │
>>>> + *   │                 │          │
>>>> + *   │                 v          │
>>>> + *   │         XFS_DAS_FLIP_LFLAG │
>>>> + *   │                 │          │
>>>> + *   │                 v          │
>>>> + *   │               remove       │
>>>> + *   │        ┌───> old name      │
>>>> + *   │        │        │          │
>>>> + *   │ XFS_DAS_RM_LBLK │          │
>>>> + *   │        ^        │          │
>>>> + *   │        │        v          │
>>>> + *   │        └──y── more to      │
>>>> + *   │               remove       │
>>>> + *   │                 │          │
>>>> + *   │                 n          │
>>>> + *   │                 │          │
>>>> + *   │                 v          │
>>>> + *   │                done <──────┘
>>>> + *   │
>>>> + *   └──────> XFS_DAS_FOUND_NBLK
>>>> + *                     │
>>>> + *                     v
>>>> + *       ┌─────n──  need to
>>>> + *       │        alloc blks?
>>>> + *       │             │
>>>> + *       │             y
>>>> + *       │             │
>>>> + *       │             v
>>>> + *       │        find space
>>>> + *       │             │
>>>> + *       │             v
>>>> + *       │  ┌─>XFS_DAS_ALLOC_NODE
>>>> + *       │  │          │
>>>> + *       │  │          v
>>>> + *       │  │      alloc blk
>>>> + *       │  │          │
>>>> + *       │  │          v
>>>> + *       │  └──y── need to alloc
>>>> + *       │         more blocks?
>>>> + *       │             │
>>>> + *       │             n
>>>> + *       │             │
>>>> + *       │             v
>>>> + *       │      set the rmt value
>>>> + *       │             │
>>>> + *       │             v
>>>> + *       │          was this
>>>> + *       └────────> a rename? ──n─???
>>>> + *                     │          │
>>>> + *                     y          │
>>>> + *                     │          │
>>>> + *                     v          │
>>>> + *               flip incomplete  │
>>>> + *                   flag         │
>>>> + *                     │          │
>>>> + *                     v          │
>>>> + *             XFS_DAS_FLIP_NFLAG │
>>>> + *                     │          │
>>>> + *                     v          │
>>>> + *                   remove       │
>>>> + *        ┌────────> old name     │
>>>> + *        │            │          │
>>>> + *  XFS_DAS_RM_NBLK    │          │
>>>> + *        ^            │          │
>>>> + *        │            v          │
>>>> + *        └──────y── more to      │
>>>> + *                   remove       │
>>>> + *                     │          │
>>>> + *                     n          │
>>>> + *                     │          │
>>>> + *                     v          │
>>>> + *                    done <──────┘
>>>> + *
>>>>     */
>>>>    /*
>>>> @@ -174,12 +401,20 @@ enum xfs_delattr_state {
>>>>    	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
>>>>    	XFS_DAS_RMTBLK,		      /* Removing remote blks */
>>>>    	XFS_DAS_RM_SHRINK,	      /* We are shrinking the tree */
>>>> +	XFS_DAS_FOUND_LBLK,	      /* We found leaf blk for attr */
>>>> +	XFS_DAS_FOUND_NBLK,	      /* We found node blk for attr */
>>>> +	XFS_DAS_FLIP_LFLAG,	      /* Flipped leaf INCOMPLETE attr flag */
>>>> +	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 */
>>>>    };
>>>>    /*
>>>>     * Defines for xfs_delattr_context.flags
>>>>     */
>>>>    #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
>>>> +#define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
>>>>    /*
>>>>     * Context used for keeping track of delayed attribute operations
>>>> @@ -187,6 +422,11 @@ enum xfs_delattr_state {
>>>>    struct xfs_delattr_context {
>>>>    	struct xfs_da_args      *da_args;
>>>> +	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
>>>> +	struct xfs_bmbt_irec	map;
>>>> +	xfs_dablk_t		lblkno;
>>>> +	int			blkcnt;
>>>> +
>>>>    	/* Used in xfs_attr_node_removename to roll through removing blocks */
>>>>    	struct xfs_da_state     *da_state;
>>>> @@ -213,7 +453,6 @@ 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);
>>>> -int xfs_attr_trans_roll(struct xfs_delattr_context *dac);
>>>>    bool xfs_attr_namecheck(const void *name, size_t length);
>>>>    void xfs_delattr_context_init(struct xfs_delattr_context *dac,
>>>>    			      struct xfs_da_args *args);
>>>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>>>> index 908521e7..fc71f10 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>>>> @@ -439,9 +439,9 @@ xfs_attr_rmtval_get(
>>>>    /*
>>>>     * Find a "hole" in the attribute address space large enough for us to drop the
>>>> - * new attribute's value into
>>>> + * new attributes value into
>>>>     */
>>>> -STATIC int
>>>> +int
>>>>    xfs_attr_rmt_find_hole(
>>>>    	struct xfs_da_args	*args)
>>>>    {
>>>> @@ -468,7 +468,7 @@ xfs_attr_rmt_find_hole(
>>>>    	return 0;
>>>>    }
>>>> -STATIC int
>>>> +int
>>>>    xfs_attr_rmtval_set_value(
>>>>    	struct xfs_da_args	*args)
>>>>    {
>>>> @@ -628,6 +628,69 @@ 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_find_space(
>>>> +	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->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 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * 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.
>>>>     */
>>>> @@ -669,37 +732,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)
>>>> -{
>>>> -	int				error;
>>>> -	struct xfs_delattr_context	dac  = {
>>>> -		.da_args	= args,
>>>> -	};
>>>> -
>>>> -	trace_xfs_attr_rmtval_remove(args);
>>>> -
>>>> -	/*
>>>> -	 * Keep de-allocating extents until the remote-value region is gone.
>>>> -	 */
>>>> -	do {
>>>> -		error = __xfs_attr_rmtval_remove(&dac);
>>>> -		if (error != -EAGAIN)
>>>> -			break;
>>>> -
>>>> -		error = xfs_attr_trans_roll(&dac);
>>>> -		if (error)
>>>> -			return error;
>>>> -	} while (true);
>>>> -
>>>> -	return error;
>>>> -}
>>>> -
>>>> -/*
>>>>     * 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 re-call the function
>>>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
>>>> index 002fd30..8ad68d5 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr_remote.h
>>>> +++ b/fs/xfs/libxfs/xfs_attr_remote.h
>>>> @@ -10,9 +10,12 @@ int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
>>>>    int xfs_attr_rmtval_get(struct xfs_da_args *args);
>>>>    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);
>>>>    int __xfs_attr_rmtval_remove(struct xfs_delattr_context *dac);
>>>> +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_find_space(struct xfs_delattr_context *dac);
>>>>    #endif /* __XFS_ATTR_REMOTE_H__ */
>>>> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
>>>> index e74bbb6..0c16d46 100644
>>>> --- a/fs/xfs/xfs_trace.h
>>>> +++ b/fs/xfs/xfs_trace.h
>>>> @@ -1944,7 +1944,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] 44+ messages in thread

end of thread, other threads:[~2021-04-14 18:16 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-26  0:32 [PATCH v16 00/11] xfs: Delay Ready Attributes Allison Henderson
2021-03-26  0:32 ` [PATCH v16 01/11] xfs: Reverse apply 72b97ea40d Allison Henderson
2021-03-29  8:44   ` Chandan Babu R
2021-04-02  9:47     ` Allison Henderson
2021-04-01 15:43   ` Brian Foster
2021-04-02  9:49     ` Allison Henderson
2021-03-26  0:32 ` [PATCH v16 02/11] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
2021-03-26  0:33 ` [PATCH v16 03/11] xfs: Hoist xfs_attr_set_shortform Allison Henderson
2021-03-29  9:21   ` Chandan Babu R
2021-04-02  9:47     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 04/11] xfs: Add helper xfs_attr_set_fmt Allison Henderson
2021-03-29  9:37   ` Chandan Babu R
2021-04-02  9:47     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 05/11] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_clear_incomplete Allison Henderson
2021-03-29 14:42   ` Chandan Babu R
2021-04-02  9:00     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 06/11] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
2021-04-02  4:05   ` Chandan Babu R
2021-04-02  9:50     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 07/11] xfs: Hoist xfs_attr_node_addname Allison Henderson
2021-04-02  4:26   ` Chandan Babu R
2021-04-02  9:01     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 08/11] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
2021-04-01 15:43   ` Brian Foster
2021-04-02  9:01     ` Allison Henderson
2021-04-05 13:15       ` Brian Foster
2021-04-02  4:40   ` Chandan Babu R
2021-04-02  9:50     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 09/11] xfs: Hoist node transaction handling Allison Henderson
2021-04-02  5:04   ` Chandan Babu R
2021-04-02  9:51     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 10/11] xfs: Add delay ready attr remove routines Allison Henderson
2021-04-01 16:55   ` Brian Foster
2021-04-02  9:42     ` Allison Henderson
2021-04-05 13:15       ` Brian Foster
2021-04-02  7:59   ` Chandan Babu R
2021-04-02  9:45     ` Allison Henderson
2021-03-26  0:33 ` [PATCH v16 11/11] xfs: Add delay ready attr set routines Allison Henderson
2021-04-01 16:57   ` Brian Foster
2021-04-02  9:01     ` Allison Henderson
2021-04-05 13:17       ` Brian Foster
2021-04-14 18:14         ` Allison Henderson
2021-03-29 21:59 ` [PATCH v16 00/11] xfs: Delay Ready Attributes Darrick J. Wong
2021-04-02  9:49   ` Allison Henderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).