All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v15 00/22] xfs: Delayed Attributes
@ 2021-02-18 16:53 Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step Allison Henderson
                   ` (21 more replies)
  0 siblings, 22 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

Hi all,

This set is a subset of a 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.

At the moment, I would like people to focus their review efforts on just this
"delayed attribute" sub series, as I think that is a more conservative use of peoples
review time.  I also think the set is a bit much to manage all at once, and we
need to get the infrastructure ironed out before we focus too much anything
that depends on it. But I do have the extended series for folks that want to
see the bigger picture of where this is going.

To help organize the set, I've arranged the patches to make sort of mini sets.
I thought it would help reviewers break down the reviewing some. For reviewing
purposes, the set could be broken up into 2 phases:

Delay Ready Attributes: (patches 1-15)
Some of these are the remaining patches belonging to the "Delay Ready" series that
we've been working with.  In these patches, transaction handling is removed
from the attr routines, and replaced with a state machine that allows a high
level function to roll the transaction and repeatedly recall the attr routines
until they are finished.  Patches 4-12 correspond to a refactoring RFC effort that
Brian and I had worked on earlier.  The lower level versions of the RFC patch
deviates slightly to correct some minor logic bugs not seen in the RFC.  I further
continued the refactoring to hoist the last state up into the xfs_attr_set_iter
routine.  The final product does create a bit of a monster function, but the state
management code is much more linear than in previous versions.  It should be noted
that while some of these new patches look a little odd, their purpose is neither to
optimize aesthetics, or even the hoist.  The goal is to prepare an arrangement of the
code such that the code changes in patch 12 are minimal and limited to state machine
mechanics.  As in previous revisions, the final product of this sub series is that the
attr routines are now compatible as a .finish_item call back.
   xfs: Add helper xfs_attr_node_remove_step
   xfs: Add xfs_attr_node_remove_cleanup
   xfs: Hoist transaction handling in xfs_attr_node_remove_step
   xfs: Hoist xfs_attr_set_shortform
   xfs: Add helper xfs_attr_set_fmt
   xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
   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
   xfs: Add state machine tracepoints
   xfs: Rename __xfs_attr_rmtval_remove
   xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans

Delayed Attributes: (patches 15 - 22)
These patches go on to fully implement delayed attributes.  New attr intent and
done items are introduced for use in the existing logging infrastructure.  A
mount option is added to toggle the feature on and off, and an error tag is added
to test the log replay
  xfs: Set up infastructure for deferred attribute operations
  xfs: Skip flip flags for delayed attrs
  xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
  xfs: Remove unused xfs_attr_*_args
  xfs: Add delayed attributes error tag
  xfs: Add delattr mount option
  xfs: Merge xfs_delattr_context into xfs_attr_item

Updates since v14: Mostly re-layering the changes discussed in the rfc along
with other minor nits from the last revision

xfs: Add helper xfs_attr_node_remove_step
   Typo nits in commit message

xfs: Hoist transaction handling in xfs_attr_node_remove_step
   Fixed typo in commit message
   removed unused dp variable from xfs_attr_node_remove_step
   Changed "return error" to "goto out" in xfs_attr_node_removename

xfs: Add delay ready attr remove routines
   Added extra xfs_freestate in xfs_attr_node_removename_setup
   rebase adjustments

xfs: Hoist xfs_attr_set_shortform
   NEW

xfs: Add helper xfs_attr_set_fmt
   NEW

xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
   NEW

xfs: Add helper xfs_attr_node_addname_find_attr
   NEW

xfs: Hoist xfs_attr_node_addname
   NEW

xfs: Hoist xfs_attr_leaf_addname
   NEW

xfs: Hoist node transaction handling
   NEW

xfs: Add delay ready attr set routines
  Fixed typos in comments and commit message
  Rebased onto refactoring additions
  All state management appears in xfs_attr_set_iter
  State machine gotos replaced with switch
  Flow chart updated

xfs: Add state machine tracepoints
  Rebase adjustments
  Added inode to trace data
  Added separate traces types for each function

xfs: Rename __xfs_attr_rmtval_remove
  Rebase adjustments

xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
  Expanded commit message about alloc warnings

xfs: Skip flip flags for delayed attrs
  Rebase adjustments

xfs: Remove unused xfs_attr_*_args
  Rebase adjustments

xfs: Merge xfs_delattr_context into xfs_attr_item
  Rebase adjustments


xfsprogs: Introduce error injection to allocate only minlen size extents for files
  Ported as a rebase dependancy
  Amended io/inject.c with error tag name to avoid compiler errors

xfsprogs: Introduce error injection to reduce maximum inode fork extent count
  Ported as a rebase dependancy
  Amended io/inject.c with error tag name to avoid compiler errors


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

As well as the extended delayed attribute and parent pointer series:
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_v15_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_v15
https://github.com/allisonhenderson/xfs_work/tree/delay_ready_attrs_xfsprogs_v15_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 Collins (1):
  xfs: Add helper xfs_attr_node_remove_step
  xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred

Allison Henderson (20):
  xfs: Add xfs_attr_node_remove_cleanup
  xfs: Hoist transaction handling in xfs_attr_node_remove_step
  xfs: Hoist xfs_attr_set_shortform
  xfs: Add helper xfs_attr_set_fmt
  xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  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
  xfs: Add state machine tracepoints
  xfs: Rename __xfs_attr_rmtval_remove
  xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
  xfs: Set up infastructure for deferred attribute operations
  xfs: Skip flip flags for delayed attrs
  xfs: Remove unused xfs_attr_*_args
  xfs: Add delayed attributes error tag
  xfs: Add delattr mount option
  xfs: Merge xfs_delattr_context into xfs_attr_item

 fs/xfs/Makefile                 |   1 +
 fs/xfs/libxfs/xfs_attr.c        | 955 +++++++++++++++++++++++++---------------
 fs/xfs/libxfs/xfs_attr.h        | 366 ++++++++++++++-
 fs/xfs/libxfs/xfs_attr_leaf.c   |   5 +-
 fs/xfs/libxfs/xfs_attr_remote.c | 127 ++++--
 fs/xfs/libxfs/xfs_attr_remote.h |   7 +-
 fs/xfs/libxfs/xfs_defer.c       |   1 +
 fs/xfs/libxfs/xfs_defer.h       |   3 +
 fs/xfs/libxfs/xfs_errortag.h    |   4 +-
 fs/xfs/libxfs/xfs_log_format.h  |  44 +-
 fs/xfs/libxfs/xfs_log_recover.h |   2 +
 fs/xfs/scrub/common.c           |   2 +
 fs/xfs/xfs_acl.c                |   2 +
 fs/xfs/xfs_attr_inactive.c      |   2 +-
 fs/xfs/xfs_attr_item.c          | 830 ++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_attr_item.h          |  52 +++
 fs/xfs/xfs_attr_list.c          |   1 +
 fs/xfs/xfs_error.c              |   3 +
 fs/xfs/xfs_ioctl.c              |   2 +
 fs/xfs/xfs_ioctl32.c            |   2 +
 fs/xfs/xfs_iops.c               |   2 +
 fs/xfs/xfs_log.c                |   4 +
 fs/xfs/xfs_log_recover.c        |   7 +-
 fs/xfs/xfs_mount.h              |   1 +
 fs/xfs/xfs_ondisk.h             |   2 +
 fs/xfs/xfs_super.c              |   6 +-
 fs/xfs/xfs_trace.h              |  26 +-
 fs/xfs/xfs_xattr.c              |   3 +
 28 files changed, 2056 insertions(+), 406 deletions(-)
 create mode 100644 fs/xfs/xfs_attr_item.c
 create mode 100644 fs/xfs/xfs_attr_item.h

-- 
2.7.4


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

* [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:03   ` Brian Foster
  2021-02-18 16:53 ` [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
                   ` (20 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

From: Allison Collins <allison.henderson@oracle.com>

This patch adds a new helper function xfs_attr_node_remove_step.  This
will help simplify and modularize the calling function
xfs_attr_node_removename.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
---
 fs/xfs/libxfs/xfs_attr.c | 46 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 472b303..28ff93d 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1228,19 +1228,14 @@ xfs_attr_node_remove_rmt(
  * the root node (a special case of an intermediate node).
  */
 STATIC int
-xfs_attr_node_removename(
-	struct xfs_da_args	*args)
+xfs_attr_node_remove_step(
+	struct xfs_da_args	*args,
+	struct xfs_da_state	*state)
 {
-	struct xfs_da_state	*state;
 	struct xfs_da_state_blk	*blk;
 	int			retval, error;
 	struct xfs_inode	*dp = args->dp;
 
-	trace_xfs_attr_node_removename(args);
-
-	error = xfs_attr_node_removename_setup(args, &state);
-	if (error)
-		goto out;
 
 	/*
 	 * If there is an out-of-line value, de-allocate the blocks.
@@ -1250,7 +1245,7 @@ xfs_attr_node_removename(
 	if (args->rmtblkno > 0) {
 		error = xfs_attr_node_remove_rmt(args, state);
 		if (error)
-			goto out;
+			return error;
 	}
 
 	/*
@@ -1267,18 +1262,45 @@ xfs_attr_node_removename(
 	if (retval && (state->path.active > 1)) {
 		error = xfs_da3_join(state);
 		if (error)
-			goto out;
+			return error;
 		error = xfs_defer_finish(&args->trans);
 		if (error)
-			goto out;
+			return error;
 		/*
 		 * Commit the Btree join operation and start a new trans.
 		 */
 		error = xfs_trans_roll_inode(&args->trans, dp);
 		if (error)
-			goto out;
+			return error;
 	}
 
+	return error;
+}
+
+/*
+ * Remove a name from a B-tree attribute list.
+ *
+ * This routine will find the blocks of the name to remove, remove them and
+ * shrink the tree if needed.
+ */
+STATIC int
+xfs_attr_node_removename(
+	struct xfs_da_args	*args)
+{
+	struct xfs_da_state	*state = NULL;
+	int			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;
+
+	error = xfs_attr_node_remove_step(args, state);
+	if (error)
+		goto out;
+
 	/*
 	 * If the result is small enough, push it all into the inode.
 	 */
-- 
2.7.4


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

* [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:03   ` Brian Foster
  2021-02-26  3:00   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step Allison Henderson
                   ` (19 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 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 28ff93d..4e6c89d 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1220,6 +1220,25 @@ xfs_attr_node_remove_rmt(
 	return xfs_attr_refillstate(state);
 }
 
+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.
  *
@@ -1232,7 +1251,6 @@ xfs_attr_node_remove_step(
 	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;
 
@@ -1247,14 +1265,7 @@ xfs_attr_node_remove_step(
 		if (error)
 			return error;
 	}
-
-	/*
-	 * 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	[flat|nested] 87+ messages in thread

* [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:04   ` Brian Foster
  2021-02-26  3:02   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform Allison Henderson
                   ` (18 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

This patch hoists transaction handling in xfs_attr_node_removename to
xfs_attr_node_remove_step.  This will help keep transaction handling in
higher level functions instead of buried in subfunctions when we
introduce delay attributes

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

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 4e6c89d..3cf76e2 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1251,9 +1251,7 @@ xfs_attr_node_remove_step(
 	struct xfs_da_args	*args,
 	struct xfs_da_state	*state)
 {
-	int			retval, error;
-	struct xfs_inode	*dp = args->dp;
-
+	int			error = 0;
 
 	/*
 	 * If there is an out-of-line value, de-allocate the blocks.
@@ -1265,25 +1263,6 @@ xfs_attr_node_remove_step(
 		if (error)
 			return error;
 	}
-	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)
-			return error;
-		error = xfs_defer_finish(&args->trans);
-		if (error)
-			return error;
-		/*
-		 * Commit the Btree join operation and start a new trans.
-		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
-		if (error)
-			return error;
-	}
 
 	return error;
 }
@@ -1299,7 +1278,7 @@ xfs_attr_node_removename(
 	struct xfs_da_args	*args)
 {
 	struct xfs_da_state	*state = NULL;
-	int			error;
+	int			retval, error;
 	struct xfs_inode	*dp = args->dp;
 
 	trace_xfs_attr_node_removename(args);
@@ -1312,6 +1291,26 @@ xfs_attr_node_removename(
 	if (error)
 		goto out;
 
+	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.
+		 */
+		error = xfs_trans_roll_inode(&args->trans, dp);
+		if (error)
+			goto out;
+	}
+
 	/*
 	 * If the result is small enough, push it all into the inode.
 	 */
-- 
2.7.4


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

* [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (2 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:04   ` Brian Foster
  2021-02-26  3:03   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt Allison Henderson
                   ` (17 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 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 3cf76e2..a064c5b 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	[flat|nested] 87+ messages in thread

* [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (3 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:04   ` Brian Foster
  2021-02-26  3:07   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work Allison Henderson
                   ` (16 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 fs/xfs/libxfs/xfs_attr.c | 77 +++++++++++++++++++++++++++---------------------
 1 file changed, 44 insertions(+), 33 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index a064c5b..205ad26 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -216,6 +216,46 @@ 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 -EAGAIN;
+}
+
 /*
  * Set the attribute specified in @args.
  */
@@ -224,8 +264,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 +273,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)
+		error = xfs_attr_set_fmt(args);
+		if (error != -EAGAIN)
 			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;
-		}
 	}
 
 	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
@@ -297,8 +309,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	[flat|nested] 87+ messages in thread

* [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (4 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:04   ` Brian Foster
  2021-02-26  4:02   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
                   ` (15 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 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 205ad26..bee8d3fb 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_work(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);
@@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
 			return error;
 	}
 
+	error = xfs_attr_node_addname_work(args);
+out:
+	if (state)
+		xfs_da_state_free(state);
+	if (error)
+		return error;
+	return retval;
+}
+
+
+STATIC
+int xfs_attr_node_addname_work(
+	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	[flat|nested] 87+ messages in thread

* [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (5 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 15:04   ` Brian Foster
  2021-02-26  4:06   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname Allison Henderson
                   ` (14 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 fs/xfs/libxfs/xfs_attr.c | 80 ++++++++++++++++++++++++++++++------------------
 1 file changed, 51 insertions(+), 29 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index bee8d3fb..4333b61 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_work(struct xfs_da_args *args);
 STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
@@ -265,6 +268,7 @@ xfs_attr_set_args(
 	struct xfs_da_args	*args)
 {
 	struct xfs_inode	*dp = args->dp;
+	struct xfs_da_state     *state;
 	int			error;
 
 	/*
@@ -310,7 +314,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;
 }
 
 /*
@@ -883,42 +894,21 @@ 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;
 
-	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;
 	if (retval == -EEXIST) {
@@ -941,6 +931,38 @@ xfs_attr_node_addname(
 		args->rmtvaluelen = 0;
 	}
 
+	return 0;
+out:
+	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) {
@@ -966,7 +988,7 @@ xfs_attr_node_addname(
 			if (error)
 				goto out;
 
-			goto restart;
+			return -EAGAIN;
 		}
 
 		/*
-- 
2.7.4


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

* [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (6 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 18:42   ` Brian Foster
  2021-03-01 18:05   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
                   ` (13 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 fs/xfs/libxfs/xfs_attr.c | 165 ++++++++++++++++++++++++-----------------------
 1 file changed, 83 insertions(+), 82 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 4333b61..19a532a 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,
@@ -268,8 +269,9 @@ 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;
+	int			retval = 0;
 
 	/*
 	 * If the attribute list is already in leaf format, jump straight to
@@ -320,8 +322,82 @@ 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);
+		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_work(args);
+out:
+	if (state)
+		xfs_da_state_free(state);
+	if (error)
+		return error;
+	return retval;
 
-	return error;
 }
 
 /*
@@ -955,7 +1031,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);
 
@@ -963,8 +1039,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
@@ -1010,85 +1086,10 @@ 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_work(args);
 out:
 	if (state)
 		xfs_da_state_free(state);
-	if (error)
-		return error;
-	return retval;
+	return error;
 }
 
 
-- 
2.7.4


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

* [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (7 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 18:42   ` Brian Foster
  2021-03-01 18:19   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 10/22] xfs: Hoist node transaction handling Allison Henderson
                   ` (12 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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 asetheic right away, but it is a
preliminary step to to manageing the state machine code.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 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 19a532a..bfd4466 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.
@@ -269,8 +269,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;
 	int			retval = 0;
 
 	/*
@@ -286,10 +287,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.
 		 */
@@ -731,115 +823,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	[flat|nested] 87+ messages in thread

* [PATCH v15 10/22] xfs: Hoist node transaction handling
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (8 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 18:43   ` Brian Foster
  2021-03-01 18:20   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 11/22] xfs: Add delay ready attr remove routines Allison Henderson
                   ` (11 subsequent siblings)
  21 siblings, 2 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 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>
---
 fs/xfs/libxfs/xfs_attr.c | 53 +++++++++++++++++++++++++-----------------------
 1 file changed, 28 insertions(+), 25 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index bfd4466..56d4b56 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -288,8 +288,34 @@ 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)
 			return error;
 
@@ -381,32 +407,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	[flat|nested] 87+ messages in thread

* [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (9 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 10/22] xfs: Hoist node transaction handling Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-24 18:45   ` Brian Foster
  2021-02-18 16:53 ` [PATCH v15 12/22] xfs: Add delay ready attr set routines Allison Henderson
                   ` (10 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 19253 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 has become xfs_attr_remove_iter, which
uses a sort of state machine like switch to keep track of where it was
when EAGAIN was returned. xfs_attr_node_removename has also been
modified to use the switch, and a new version of xfs_attr_remove_args
consists of a simple loop to refresh the transaction until the operation
is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
 fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
 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, 294 insertions(+), 83 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 56d4b56..d46b92a 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -57,8 +57,8 @@ 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_work(struct xfs_da_args *args);
+STATIC int xfs_attr_node_removename_iter(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);
@@ -221,6 +221,34 @@ 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);
+		if (error)
+			return error;
+	} else
+		error = xfs_trans_roll_inode(&args->trans, args->dp);
+
+	return error;
+}
+
 STATIC int
 xfs_attr_set_fmt(
 	struct xfs_da_args	*args)
@@ -531,23 +559,58 @@ 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,
+	};
+
+	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;
+}
+
+/*
+ * Remove the attribute specified in @args.
+ *
+ * This function may return -EAGAIN to signal that the transaction needs to be
+ * rolled.  Callers should continue calling this function until they receive a
+ * return value other than -EAGAIN.
+ */
+int
+xfs_attr_remove_iter(
+	struct xfs_delattr_context	*dac)
+{
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_inode		*dp = args->dp;
 
-	if (!xfs_inode_hasattr(dp)) {
-		error = -ENOATTR;
-	} else if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
+	/* If we are shrinking a node, resume shrink */
+	if (dac->dela_state == XFS_DAS_RM_SHRINK)
+		goto node;
+
+	if (!xfs_inode_hasattr(dp))
+		return -ENOATTR;
+
+	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);
+		return xfs_attr_shortform_remove(args);
 	}
 
-	return error;
+	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
+		return xfs_attr_leaf_removename(args);
+node:
+	/* If we are not short form or leaf, then proceed to remove node */
+	return  xfs_attr_node_removename_iter(dac);
 }
 
 /*
@@ -1191,14 +1254,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 ==
@@ -1207,22 +1272,28 @@ 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;
 }
 
 STATIC int
-xfs_attr_node_remove_rmt(
-	struct xfs_da_args	*args,
-	struct xfs_da_state	*state)
+xfs_attr_node_remove_rmt (
+	struct xfs_delattr_context	*dac,
+	struct xfs_da_state		*state)
 {
-	int			error = 0;
+	int				error = 0;
 
-	error = xfs_attr_rmtval_remove(args);
+	/*
+	 * May return -EAGAIN to request that the caller recall this function
+	 */
+	error = __xfs_attr_rmtval_remove(dac);
 	if (error)
 		return error;
 
@@ -1253,18 +1324,24 @@ xfs_attr_node_remove_cleanup(
 }
 
 /*
- * Remove a name from a B-tree attribute list.
+ * Step through removeing a name from a B-tree attribute list.
  *
  * This will involve walking down the Btree, and may involve joining
  * leaf nodes and even joining intermediate nodes up to and including
  * the root node (a special case of an intermediate node).
+ *
+ * This routine is meant to function as either an inline or delayed operation,
+ * and may return -EAGAIN when the transaction needs to be rolled.  Calling
+ * functions will need to handle this, and recall the function until a
+ * successful error code is returned.
  */
 STATIC int
 xfs_attr_node_remove_step(
-	struct xfs_da_args	*args,
-	struct xfs_da_state	*state)
+	struct xfs_delattr_context	*dac)
 {
-	int			error = 0;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_state		*state = dac->da_state;
+	int				error = 0;
 
 	/*
 	 * If there is an out-of-line value, de-allocate the blocks.
@@ -1272,7 +1349,10 @@ xfs_attr_node_remove_step(
 	 * overflow the maximum size of a transaction and/or hit a deadlock.
 	 */
 	if (args->rmtblkno > 0) {
-		error = xfs_attr_node_remove_rmt(args, state);
+		/*
+		 * May return -EAGAIN. Remove blocks until args->rmtblkno == 0
+		 */
+		error = xfs_attr_node_remove_rmt(dac, state);
 		if (error)
 			return error;
 	}
@@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
  *
  * This routine will find the blocks of the name to remove, remove them and
  * shrink the tree if needed.
+ *
+ * This routine is meant to function as either an inline or delayed operation,
+ * and may return -EAGAIN when the transaction needs to be rolled.  Calling
+ * functions will need to handle this, and recall the function until a
+ * successful error code is returned.
  */
 STATIC int
-xfs_attr_node_removename(
-	struct xfs_da_args	*args)
+xfs_attr_node_removename_iter(
+	struct xfs_delattr_context	*dac)
 {
-	struct xfs_da_state	*state = NULL;
-	int			retval, error;
-	struct xfs_inode	*dp = args->dp;
+	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_state		*state = NULL;
+	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;
-
-	error = xfs_attr_node_remove_step(args, state);
-	if (error)
-		goto out;
-
-	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 (!dac->da_state) {
+		error = xfs_attr_node_removename_setup(dac);
 		if (error)
 			goto out;
+	}
+	state = dac->da_state;
+
+	switch (dac->dela_state) {
+	case XFS_DAS_UNINIT:
 		/*
-		 * Commit the Btree join operation and start a new trans.
+		 * repeatedly remove remote blocks, remove the entry and join.
+		 * returns -EAGAIN or 0 for completion of the step.
 		 */
-		error = xfs_trans_roll_inode(&args->trans, dp);
+		error = xfs_attr_node_remove_step(dac);
 		if (error)
-			goto out;
-	}
+			break;
 
-	/*
-	 * 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);
+		retval = xfs_attr_node_remove_cleanup(args, state);
 
+		/*
+		 * 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
+		 */
+		if (retval && (state->path.active > 1)) {
+			error = xfs_da3_join(state);
+			if (error)
+				goto out;
+
+			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..3154ef4 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -74,6 +74,102 @@ 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
+ *        found attr blks? ───n──┐
+ *              │                v
+ *              │         find and invalidate
+ *              y         the blocks. mark
+ *              │         attr incomplete
+ *              ├────────────────┘
+ *              │
+ *              v
+ *      remove a block with
+ *    xfs_attr_node_remove_step <────┐
+ *              │                    │
+ *              v                    │
+ *      still have blks ──y──> return -EAGAIN.
+ *        to remove?          re-enter with one
+ *              │            less blk to remove
+ *              n
+ *              │
+ *              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_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 +187,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..f09820c 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 dont 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 reminicent 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] 87+ messages in thread

* [PATCH v15 12/22] xfs: Add delay ready attr set routines
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (10 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 11/22] xfs: Add delay ready attr remove routines Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-03-02  1:39   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 13/22] xfs: Add state machine tracepoints Allison Henderson
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 34998 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>
---
 fs/xfs/libxfs/xfs_attr.c        | 448 ++++++++++++++++++++++++----------------
 fs/xfs/libxfs/xfs_attr.h        | 241 ++++++++++++++++++++-
 fs/xfs/libxfs/xfs_attr_remote.c |  98 ++++++---
 fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
 fs/xfs/xfs_trace.h              |   1 -
 5 files changed, 583 insertions(+), 210 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index d46b92a..c7b86d5 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -53,16 +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_work(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_work(struct xfs_delattr_context *dac);
 STATIC int xfs_attr_node_removename_iter(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(
@@ -226,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)
 {
@@ -249,29 +249,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;
 
@@ -280,93 +306,140 @@ 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);
+	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;
-	int			retval = 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;
+	int				retval = 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 sucesfull, 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;
+			dac->dela_state = XFS_DAS_FOUND_NBLK;
+			return -EAGAIN;
 		}
-		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;
+		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
@@ -395,22 +468,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;
 
-			error = xfs_attr_rmtval_remove(args);
+		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
+		dac->dela_state = XFS_DAS_RM_LBLK;
+
+		/* fallthrough */
+	case XFS_DAS_RM_LBLK:
+		if (args->rmtblkno) {
+			error = __xfs_attr_rmtval_remove(dac);
 			if (error)
 				return error;
 		}
@@ -435,94 +512,117 @@ 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);
+			retval = error;
+			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);
-		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;
+		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);
+		/* 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_work(dac);
+
+out:
+		if (state)
+			xfs_da_state_free(state);
 		if (error)
 			return error;
-	}
+		return retval;
 
-	error = xfs_attr_node_addname_work(args);
-out:
-	if (state)
-		xfs_da_state_free(state);
-	if (error)
-		return error;
-	return retval;
+	default:
+		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
+		break;
+	}
 
+	return error;
 }
 
+
 /*
  * Return EEXIST if attr is found, or ENOATTR if not
  */
@@ -1021,18 +1121,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 out;
+		return retval;
 
 	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
 		goto out;
@@ -1058,8 +1158,8 @@ xfs_attr_node_addname_find_attr(
 
 	return 0;
 out:
-	if (*state)
-		xfs_da_state_free(*state);
+	if (dac->da_state)
+		xfs_da_state_free(dac->da_state);
 	return retval;
 }
 
@@ -1072,20 +1172,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);
@@ -1101,18 +1205,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;
 		}
 
@@ -1125,9 +1226,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.
@@ -1144,8 +1243,9 @@ xfs_attr_node_addname(
 
 STATIC
 int xfs_attr_node_addname_work(
-	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 3154ef4..603887e 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -135,6 +135,233 @@ struct xfs_attr_list_context {
  *              v
  *            done
  *
+ *
+ * Below is a state machine diagram for attr set operations.
+ *
+ * It seems the challenge with undertanding 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
+ * statemachine. 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 statemachine to
+ * a subroutine, it needs to respect the simple rule that it doesn't "own" the
+ * statemachine 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 statemachine.
+ *
+ *  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 <──────┘
+ *
  */
 
 /*
@@ -149,12 +376,20 @@ struct xfs_attr_list_context {
 enum xfs_delattr_state {
 	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
 	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
@@ -162,6 +397,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;
 
@@ -188,7 +428,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 f09820c..6af86bf 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -441,7 +441,7 @@ xfs_attr_rmtval_get(
  * Find a "hole" in the attribute address space large enough for us to drop the
  * new attribute's value into
  */
-STATIC int
+int
 xfs_attr_rmt_find_hole(
 	struct xfs_da_args	*args)
 {
@@ -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 63ecbc6..363e1bf 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1942,7 +1942,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] 87+ messages in thread

* [PATCH v15 13/22] xfs: Add state machine tracepoints
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (11 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 12/22] xfs: Add delay ready attr set routines Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  5:06   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 14/22] xfs: Rename __xfs_attr_rmtval_remove Allison Henderson
                   ` (8 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

This is a quick patch to add a new tracepoint: xfs_das_state_return.  We
use this to track when ever a new state is set or -EAGAIN is returned

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c        | 31 ++++++++++++++++++++++++++++++-
 fs/xfs/libxfs/xfs_attr_remote.c |  1 +
 fs/xfs/xfs_trace.h              | 25 +++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index c7b86d5..ba21475 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -313,6 +313,7 @@ xfs_attr_set_fmt(
 	 * the attr fork to leaf format and will restart with the leaf
 	 * add.
 	 */
+	trace_xfs_attr_set_fmt_return(XFS_DAS_UNINIT, args->dp);
 	dac->flags |= XFS_DAC_DEFER_FINISH;
 	return -EAGAIN;
 }
@@ -378,6 +379,8 @@ xfs_attr_set_iter(
 				 * handling code below
 				 */
 				dac->flags |= XFS_DAC_DEFER_FINISH;
+				trace_xfs_attr_set_iter_return(
+					dac->dela_state, args->dp);
 				return -EAGAIN;
 			}
 			else if (error)
@@ -400,10 +403,13 @@ xfs_attr_set_iter(
 				return error;
 
 			dac->dela_state = XFS_DAS_FOUND_NBLK;
+			trace_xfs_attr_set_iter_return(dac->dela_state,
+						       args->dp);
 			return -EAGAIN;
 		}
 
 		dac->dela_state = XFS_DAS_FOUND_LBLK;
+		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
 		return -EAGAIN;
 
         case XFS_DAS_FOUND_LBLK:
@@ -433,6 +439,8 @@ xfs_attr_set_iter(
 			if (error)
 				return error;
 
+			trace_xfs_attr_set_iter_return(dac->dela_state,
+						       args->dp);
 			return -EAGAIN;
 		}
 
@@ -469,6 +477,7 @@ xfs_attr_set_iter(
 		 * series.
 		 */
 		dac->dela_state = XFS_DAS_FLIP_LFLAG;
+		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
 		return -EAGAIN;
 	case XFS_DAS_FLIP_LFLAG:
 		/*
@@ -488,6 +497,9 @@ xfs_attr_set_iter(
 	case XFS_DAS_RM_LBLK:
 		if (args->rmtblkno) {
 			error = __xfs_attr_rmtval_remove(dac);
+			if (error == -EAGAIN)
+				trace_xfs_attr_set_iter_return(
+					dac->dela_state, args->dp);
 			if (error)
 				return error;
 		}
@@ -545,6 +557,8 @@ xfs_attr_set_iter(
 				if (error)
 					return error;
 
+				trace_xfs_attr_set_iter_return(
+					dac->dela_state, args->dp);
 				return -EAGAIN;
 			}
 
@@ -581,6 +595,7 @@ xfs_attr_set_iter(
 		 * series
 		 */
 		dac->dela_state = XFS_DAS_FLIP_NFLAG;
+		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
 		return -EAGAIN;
 
 	case XFS_DAS_FLIP_NFLAG:
@@ -601,6 +616,10 @@ xfs_attr_set_iter(
 	case XFS_DAS_RM_NBLK:
 		if (args->rmtblkno) {
 			error = __xfs_attr_rmtval_remove(dac);
+			if (error == -EAGAIN)
+				trace_xfs_attr_set_iter_return(
+					dac->dela_state, args->dp);
+
 			if (error)
 				return error;
 		}
@@ -1214,6 +1233,8 @@ xfs_attr_node_addname(
 			 * this point.
 			 */
 			dac->flags |= XFS_DAC_DEFER_FINISH;
+			trace_xfs_attr_node_addname_return(
+					dac->dela_state, args->dp);
 			return -EAGAIN;
 		}
 
@@ -1394,6 +1415,9 @@ xfs_attr_node_remove_rmt (
 	 * May return -EAGAIN to request that the caller recall this function
 	 */
 	error = __xfs_attr_rmtval_remove(dac);
+	if (error == -EAGAIN)
+		trace_xfs_attr_node_remove_rmt_return(dac->dela_state,
+						      dac->da_args->dp);
 	if (error)
 		return error;
 
@@ -1513,6 +1537,8 @@ xfs_attr_node_removename_iter(
 
 			dac->flags |= XFS_DAC_DEFER_FINISH;
 			dac->dela_state = XFS_DAS_RM_SHRINK;
+			trace_xfs_attr_node_removename_iter_return(
+					dac->dela_state, args->dp);
 			return -EAGAIN;
 		}
 
@@ -1531,8 +1557,11 @@ xfs_attr_node_removename_iter(
 		goto out;
 	}
 
-	if (error == -EAGAIN)
+	if (error == -EAGAIN) {
+		trace_xfs_attr_node_removename_iter_return(
+					dac->dela_state, args->dp);
 		return error;
+	}
 out:
 	if (state)
 		xfs_da_state_free(state);
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 6af86bf..b242e1a 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -763,6 +763,7 @@ __xfs_attr_rmtval_remove(
 	 */
 	if (!done) {
 		dac->flags |= XFS_DAC_DEFER_FINISH;
+		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
 		return -EAGAIN;
 	}
 
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 363e1bf..7993f55 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3927,6 +3927,31 @@ DEFINE_EVENT(xfs_eofblocks_class, name,	\
 DEFINE_EOFBLOCKS_EVENT(xfs_ioc_free_eofblocks);
 DEFINE_EOFBLOCKS_EVENT(xfs_blockgc_free_space);
 
+DECLARE_EVENT_CLASS(xfs_das_state_class,
+	TP_PROTO(int das, struct xfs_inode *ip),
+	TP_ARGS(das, ip),
+	TP_STRUCT__entry(
+		__field(int, das)
+		__field(xfs_ino_t, ino)
+	),
+	TP_fast_assign(
+		__entry->das = das;
+		__entry->ino = ip->i_ino;
+	),
+	TP_printk("state change %d ino 0x%llx",
+		  __entry->das, __entry->ino)
+)
+
+#define DEFINE_DAS_STATE_EVENT(name) \
+DEFINE_EVENT(xfs_das_state_class, name, \
+	TP_PROTO(int das, struct xfs_inode *ip), \
+	TP_ARGS(das, ip))
+DEFINE_DAS_STATE_EVENT(xfs_attr_set_fmt_return);
+DEFINE_DAS_STATE_EVENT(xfs_attr_set_iter_return);
+DEFINE_DAS_STATE_EVENT(xfs_attr_node_addname_return);
+DEFINE_DAS_STATE_EVENT(xfs_attr_node_removename_iter_return);
+DEFINE_DAS_STATE_EVENT(xfs_attr_node_remove_rmt_return);
+DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_remove_return);
 #endif /* _TRACE_XFS_H */
 
 #undef TRACE_INCLUDE_PATH
-- 
2.7.4


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

* [PATCH v15 14/22] xfs: Rename __xfs_attr_rmtval_remove
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (12 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 13/22] xfs: Add state machine tracepoints Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans Allison Henderson
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

Now that xfs_attr_rmtval_remove is gone, rename __xfs_attr_rmtval_remove
to xfs_attr_rmtval_remove

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

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index ba21475..2b8e481 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -496,7 +496,7 @@ xfs_attr_set_iter(
 		/* fallthrough */
 	case XFS_DAS_RM_LBLK:
 		if (args->rmtblkno) {
-			error = __xfs_attr_rmtval_remove(dac);
+			error = xfs_attr_rmtval_remove(dac);
 			if (error == -EAGAIN)
 				trace_xfs_attr_set_iter_return(
 					dac->dela_state, args->dp);
@@ -615,7 +615,7 @@ xfs_attr_set_iter(
 		/* fallthrough */
 	case XFS_DAS_RM_NBLK:
 		if (args->rmtblkno) {
-			error = __xfs_attr_rmtval_remove(dac);
+			error = xfs_attr_rmtval_remove(dac);
 			if (error == -EAGAIN)
 				trace_xfs_attr_set_iter_return(
 					dac->dela_state, args->dp);
@@ -1414,7 +1414,7 @@ xfs_attr_node_remove_rmt (
 	/*
 	 * May return -EAGAIN to request that the caller recall this function
 	 */
-	error = __xfs_attr_rmtval_remove(dac);
+	error = xfs_attr_rmtval_remove(dac);
 	if (error == -EAGAIN)
 		trace_xfs_attr_node_remove_rmt_return(dac->dela_state,
 						      dac->da_args->dp);
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index b242e1a..b6554a3 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -737,7 +737,7 @@ xfs_attr_rmtval_invalidate(
  * transaction and re-call the function
  */
 int
-__xfs_attr_rmtval_remove(
+xfs_attr_rmtval_remove(
 	struct xfs_delattr_context	*dac)
 {
 	struct xfs_da_args		*args = dac->da_args;
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index 8ad68d5..6ae91af 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -13,7 +13,7 @@ int xfs_attr_rmtval_set(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_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);
-- 
2.7.4


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

* [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (13 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 14/22] xfs: Rename __xfs_attr_rmtval_remove Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  5:06   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations Allison Henderson
                   ` (6 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

Because xattrs can be over a page in size, we need to handle possible
krealloc errors to avoid warnings

The warning:
   WARNING: CPU: 1 PID: 20255 at mm/page_alloc.c:3446
                 get_page_from_freelist+0x100b/0x1690

is caused when sizes larger that a page are allocated with the
__GFP_NOFAIL flag option.  We encounter this error now because attr
values can be up to 64k in size.  So we cannot use __GFP_NOFAIL, and
we need to handle the error code if the allocation fails.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/xfs_log_recover.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 97f3130..295a5c6 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -2061,7 +2061,10 @@ xlog_recover_add_to_cont_trans(
 	old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
 	old_len = item->ri_buf[item->ri_cnt-1].i_len;
 
-	ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL | __GFP_NOFAIL);
+	ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL);
+	if (ptr == NULL)
+		return -ENOMEM;
+
 	memcpy(&ptr[old_len], dp, len);
 	item->ri_buf[item->ri_cnt-1].i_len += len;
 	item->ri_buf[item->ri_cnt-1].i_addr = ptr;
-- 
2.7.4


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

* [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (14 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  4:58   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs Allison Henderson
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

Currently attributes are modified directly across one or more
transactions. But they are not logged or replayed in the event of an
error. The goal of delayed attributes is to enable logging and replaying
of attribute operations using the existing delayed operations
infrastructure.  This will later enable the attributes to become part of
larger multi part operations that also must first be recorded to the
log.  This is mostly of interest in the scheme of parent pointers which
would need to maintain an attribute containing parent inode information
any time an inode is moved, created, or removed.  Parent pointers would
then be of interest to any feature that would need to quickly derive an
inode path from the mount point. Online scrub, nfs lookups and fs grow
or shrink operations are all features that could take advantage of this.

This patch adds two new log item types for setting or removing
attributes as deferred operations.  The xfs_attri_log_item logs an
intent to set or remove an attribute.  The corresponding
xfs_attrd_log_item holds a reference to the xfs_attri_log_item and is
freed once the transaction is done.  Both log items use a generic
xfs_attr_log_format structure that contains the attribute name, value,
flags, inode, and an op_flag that indicates if the operations is a set
or remove.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/Makefile                 |   1 +
 fs/xfs/libxfs/xfs_attr.c        |   7 +-
 fs/xfs/libxfs/xfs_attr.h        |  31 ++
 fs/xfs/libxfs/xfs_defer.c       |   1 +
 fs/xfs/libxfs/xfs_defer.h       |   3 +
 fs/xfs/libxfs/xfs_log_format.h  |  44 ++-
 fs/xfs/libxfs/xfs_log_recover.h |   2 +
 fs/xfs/scrub/common.c           |   2 +
 fs/xfs/xfs_acl.c                |   2 +
 fs/xfs/xfs_attr_item.c          | 828 ++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_attr_item.h          |  52 +++
 fs/xfs/xfs_attr_list.c          |   1 +
 fs/xfs/xfs_ioctl.c              |   2 +
 fs/xfs/xfs_ioctl32.c            |   2 +
 fs/xfs/xfs_iops.c               |   2 +
 fs/xfs/xfs_log.c                |   4 +
 fs/xfs/xfs_log_recover.c        |   2 +
 fs/xfs/xfs_ondisk.h             |   2 +
 fs/xfs/xfs_xattr.c              |   1 +
 19 files changed, 984 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 04611a1..b056cfc 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -102,6 +102,7 @@ xfs-y				+= xfs_log.o \
 				   xfs_buf_item_recover.o \
 				   xfs_dquot_item_recover.o \
 				   xfs_extfree_item.o \
+				   xfs_attr_item.o \
 				   xfs_icreate_item.o \
 				   xfs_inode_item.o \
 				   xfs_inode_item_recover.o \
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 2b8e481..e4c1b4b 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -24,6 +24,7 @@
 #include "xfs_quota.h"
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
+#include "xfs_attr_item.h"
 
 /*
  * xfs_attr.c
@@ -61,8 +62,8 @@ 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_attr_set_iter(struct xfs_delattr_context *dac,
+		      struct xfs_buf **leaf_bp);
 
 int
 xfs_inode_hasattr(
@@ -144,7 +145,7 @@ xfs_attr_get(
 /*
  * Calculate how many blocks we need for the new attribute,
  */
-STATIC int
+int
 xfs_attr_calc_size(
 	struct xfs_da_args	*args,
 	int			*local)
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 603887e..ee79763 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -28,6 +28,11 @@ struct xfs_attr_list_context;
  */
 #define	ATTR_MAX_VALUELEN	(64*1024)	/* max length of a value */
 
+static inline bool xfs_hasdelattr(struct xfs_mount *mp)
+{
+	return false;
+}
+
 /*
  * Kernel-internal version of the attrlist cursor.
  */
@@ -390,6 +395,7 @@ enum xfs_delattr_state {
  */
 #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
 #define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
+#define XFS_DAC_DELAYED_OP_INIT		0x04 /* delayed operations init*/
 
 /*
  * Context used for keeping track of delayed attribute operations
@@ -397,6 +403,11 @@ enum xfs_delattr_state {
 struct xfs_delattr_context {
 	struct xfs_da_args      *da_args;
 
+	/*
+	 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
+	 */
+	struct xfs_buf		*leaf_bp;
+
 	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
 	struct xfs_bmbt_irec	map;
 	xfs_dablk_t		lblkno;
@@ -410,6 +421,23 @@ struct xfs_delattr_context {
 	enum xfs_delattr_state  dela_state;
 };
 
+/*
+ * List of attrs to commit later.
+ */
+struct xfs_attr_item {
+	struct xfs_delattr_context	xattri_dac;
+
+	/*
+	 * Indicates if the attr operation is a set or a remove
+	 * XFS_ATTR_OP_FLAGS_{SET,REMOVE}
+	 */
+	uint32_t			xattri_op_flags;
+
+	/* used to log this item to an intent */
+	struct list_head		xattri_list;
+};
+
+
 /*========================================================================
  * Function prototypes for the kernel.
  *========================================================================*/
@@ -425,11 +453,14 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
 int xfs_attr_get(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
 int xfs_attr_set_args(struct xfs_da_args *args);
+int xfs_attr_set_iter(struct xfs_delattr_context *dac,
+		      struct xfs_buf **leaf_bp);
 int xfs_has_attr(struct xfs_da_args *args);
 int xfs_attr_remove_args(struct xfs_da_args *args);
 int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
 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);
+int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
 
 #endif	/* __XFS_ATTR_H__ */
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index eff4a12..e9caff7 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -178,6 +178,7 @@ static const struct xfs_defer_op_type *defer_op_types[] = {
 	[XFS_DEFER_OPS_TYPE_RMAP]	= &xfs_rmap_update_defer_type,
 	[XFS_DEFER_OPS_TYPE_FREE]	= &xfs_extent_free_defer_type,
 	[XFS_DEFER_OPS_TYPE_AGFL_FREE]	= &xfs_agfl_free_defer_type,
+	[XFS_DEFER_OPS_TYPE_ATTR]	= &xfs_attr_defer_type,
 };
 
 static void
diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
index 05472f7..72a5789 100644
--- a/fs/xfs/libxfs/xfs_defer.h
+++ b/fs/xfs/libxfs/xfs_defer.h
@@ -19,6 +19,7 @@ enum xfs_defer_ops_type {
 	XFS_DEFER_OPS_TYPE_RMAP,
 	XFS_DEFER_OPS_TYPE_FREE,
 	XFS_DEFER_OPS_TYPE_AGFL_FREE,
+	XFS_DEFER_OPS_TYPE_ATTR,
 	XFS_DEFER_OPS_TYPE_MAX,
 };
 
@@ -63,6 +64,8 @@ extern const struct xfs_defer_op_type xfs_refcount_update_defer_type;
 extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
 extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
 extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
+extern const struct xfs_defer_op_type xfs_attr_defer_type;
+
 
 /*
  * This structure enables a dfops user to detach the chain of deferred
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index 8bd00da..19963b6 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -117,7 +117,12 @@ struct xfs_unmount_log_format {
 #define XLOG_REG_TYPE_CUD_FORMAT	24
 #define XLOG_REG_TYPE_BUI_FORMAT	25
 #define XLOG_REG_TYPE_BUD_FORMAT	26
-#define XLOG_REG_TYPE_MAX		26
+#define XLOG_REG_TYPE_ATTRI_FORMAT	27
+#define XLOG_REG_TYPE_ATTRD_FORMAT	28
+#define XLOG_REG_TYPE_ATTR_NAME	29
+#define XLOG_REG_TYPE_ATTR_VALUE	30
+#define XLOG_REG_TYPE_MAX		30
+
 
 /*
  * Flags to log operation header
@@ -240,6 +245,8 @@ typedef struct xfs_trans_header {
 #define	XFS_LI_CUD		0x1243
 #define	XFS_LI_BUI		0x1244	/* bmbt update intent */
 #define	XFS_LI_BUD		0x1245
+#define	XFS_LI_ATTRI		0x1246  /* attr set/remove intent*/
+#define	XFS_LI_ATTRD		0x1247  /* attr set/remove done */
 
 #define XFS_LI_TYPE_DESC \
 	{ XFS_LI_EFI,		"XFS_LI_EFI" }, \
@@ -255,7 +262,9 @@ typedef struct xfs_trans_header {
 	{ XFS_LI_CUI,		"XFS_LI_CUI" }, \
 	{ XFS_LI_CUD,		"XFS_LI_CUD" }, \
 	{ XFS_LI_BUI,		"XFS_LI_BUI" }, \
-	{ XFS_LI_BUD,		"XFS_LI_BUD" }
+	{ XFS_LI_BUD,		"XFS_LI_BUD" }, \
+	{ XFS_LI_ATTRI,		"XFS_LI_ATTRI" }, \
+	{ XFS_LI_ATTRD,		"XFS_LI_ATTRD" }
 
 /*
  * Inode Log Item Format definitions.
@@ -863,4 +872,35 @@ struct xfs_icreate_log {
 	__be32		icl_gen;	/* inode generation number to use */
 };
 
+/*
+ * Flags for deferred attribute operations.
+ * Upper bits are flags, lower byte is type code
+ */
+#define XFS_ATTR_OP_FLAGS_SET		1	/* Set the attribute */
+#define XFS_ATTR_OP_FLAGS_REMOVE	2	/* Remove the attribute */
+#define XFS_ATTR_OP_FLAGS_TYPE_MASK	0x0FF	/* Flags type mask */
+
+/*
+ * This is the structure used to lay out an attr log item in the
+ * log.
+ */
+struct xfs_attri_log_format {
+	uint16_t	alfi_type;	/* attri log item type */
+	uint16_t	alfi_size;	/* size of this item */
+	uint32_t	__pad;		/* pad to 64 bit aligned */
+	uint64_t	alfi_id;	/* attri identifier */
+	uint64_t	alfi_ino;	/* the inode for this attr operation */
+	uint32_t	alfi_op_flags;	/* marks the op as a set or remove */
+	uint32_t	alfi_name_len;	/* attr name length */
+	uint32_t	alfi_value_len;	/* attr value length */
+	uint32_t	alfi_attr_flags;/* attr flags */
+};
+
+struct xfs_attrd_log_format {
+	uint16_t	alfd_type;	/* attrd log item type */
+	uint16_t	alfd_size;	/* size of this item */
+	uint32_t	__pad;		/* pad to 64 bit aligned */
+	uint64_t	alfd_alf_id;	/* id of corresponding attri */
+};
+
 #endif /* __XFS_LOG_FORMAT_H__ */
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 3cca2bf..b6e5514 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -72,6 +72,8 @@ extern const struct xlog_recover_item_ops xlog_rui_item_ops;
 extern const struct xlog_recover_item_ops xlog_rud_item_ops;
 extern const struct xlog_recover_item_ops xlog_cui_item_ops;
 extern const struct xlog_recover_item_ops xlog_cud_item_ops;
+extern const struct xlog_recover_item_ops xlog_attri_item_ops;
+extern const struct xlog_recover_item_ops xlog_attrd_item_ops;
 
 /*
  * Macros, structures, prototypes for internal log manager use.
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 53456f3..ac35121 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -24,6 +24,8 @@
 #include "xfs_rmap_btree.h"
 #include "xfs_log.h"
 #include "xfs_trans_priv.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_reflink.h"
 #include "scrub/scrub.h"
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index 779cb73..79f7bd2 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -10,6 +10,8 @@
 #include "xfs_trans_resv.h"
 #include "xfs_mount.h"
 #include "xfs_inode.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_trace.h"
 #include "xfs_error.h"
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
new file mode 100644
index 0000000..8c8f72d
--- /dev/null
+++ b/fs/xfs/xfs_attr_item.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2021 Oracle.  All Rights Reserved.
+ * Author: Allison Collins <allison.henderson@oracle.com>
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_shared.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_attr_item.h"
+#include "xfs_log.h"
+#include "xfs_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_shared.h"
+#include "xfs_attr_item.h"
+#include "xfs_alloc.h"
+#include "xfs_bmap.h"
+#include "xfs_trace.h"
+#include "libxfs/xfs_da_format.h"
+#include "xfs_inode.h"
+#include "xfs_quota.h"
+#include "xfs_trans_space.h"
+#include "xfs_log_priv.h"
+#include "xfs_log_recover.h"
+
+static const struct xfs_item_ops xfs_attri_item_ops;
+static const struct xfs_item_ops xfs_attrd_item_ops;
+
+/* iovec length must be 32-bit aligned */
+static inline size_t ATTR_NVEC_SIZE(size_t size)
+{
+	return size == sizeof(int32_t) ? size :
+	       sizeof(int32_t) + round_up(size, sizeof(int32_t));
+}
+
+static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip)
+{
+	return container_of(lip, struct xfs_attri_log_item, attri_item);
+}
+
+STATIC void
+xfs_attri_item_free(
+	struct xfs_attri_log_item	*attrip)
+{
+	kmem_free(attrip->attri_item.li_lv_shadow);
+	kmem_free(attrip);
+}
+
+/*
+ * Freeing the attrip requires that we remove it from the AIL if it has already
+ * been placed there. However, the ATTRI may not yet have been placed in the
+ * AIL when called by xfs_attri_release() from ATTRD processing due to the
+ * ordering of committed vs unpin operations in bulk insert operations. Hence
+ * the reference count to ensure only the last caller frees the ATTRI.
+ */
+STATIC void
+xfs_attri_release(
+	struct xfs_attri_log_item	*attrip)
+{
+	ASSERT(atomic_read(&attrip->attri_refcount) > 0);
+	if (atomic_dec_and_test(&attrip->attri_refcount)) {
+		xfs_trans_ail_delete(&attrip->attri_item,
+				     SHUTDOWN_LOG_IO_ERROR);
+		xfs_attri_item_free(attrip);
+	}
+}
+
+STATIC void
+xfs_attri_item_size(
+	struct xfs_log_item	*lip,
+	int			*nvecs,
+	int			*nbytes)
+{
+	struct xfs_attri_log_item       *attrip = ATTRI_ITEM(lip);
+
+	*nvecs += 1;
+	*nbytes += sizeof(struct xfs_attri_log_format);
+
+	/* Attr set and remove operations require a name */
+	ASSERT(attrip->attri_name_len > 0);
+
+	*nvecs += 1;
+	*nbytes += ATTR_NVEC_SIZE(attrip->attri_name_len);
+
+	/*
+	 * Set ops can accept a value of 0 len to clear an attr value.  Remove
+	 * ops do not need a value at all.  So only account for the value
+	 * when it is needed.
+	 */
+	if (attrip->attri_value_len > 0) {
+		*nvecs += 1;
+		*nbytes += ATTR_NVEC_SIZE(attrip->attri_value_len);
+	}
+}
+
+/*
+ * This is called to fill in the log iovecs for the given attri log
+ * item. We use  1 iovec for the attri_format_item, 1 for the name, and
+ * another for the value if it is present
+ */
+STATIC void
+xfs_attri_item_format(
+	struct xfs_log_item	*lip,
+	struct xfs_log_vec	*lv)
+{
+	struct xfs_attri_log_item	*attrip = ATTRI_ITEM(lip);
+	struct xfs_log_iovec		*vecp = NULL;
+
+	attrip->attri_format.alfi_type = XFS_LI_ATTRI;
+	attrip->attri_format.alfi_size = 1;
+
+	/*
+	 * This size accounting must be done before copying the attrip into the
+	 * iovec.  If we do it after, the wrong size will be recorded to the log
+	 * and we trip across assertion checks for bad region sizes later during
+	 * the log recovery.
+	 */
+
+	ASSERT(attrip->attri_name_len > 0);
+	attrip->attri_format.alfi_size++;
+
+	if (attrip->attri_value_len > 0)
+		attrip->attri_format.alfi_size++;
+
+	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT,
+			&attrip->attri_format,
+			sizeof(struct xfs_attri_log_format));
+	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME,
+			attrip->attri_name,
+			ATTR_NVEC_SIZE(attrip->attri_name_len));
+	if (attrip->attri_value_len > 0)
+		xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE,
+				attrip->attri_value,
+				ATTR_NVEC_SIZE(attrip->attri_value_len));
+}
+
+/*
+ * The unpin operation is the last place an ATTRI is manipulated in the log. It
+ * is either inserted in the AIL or aborted in the event of a log I/O error. In
+ * either case, the ATTRI transaction has been successfully committed to make
+ * it this far. Therefore, we expect whoever committed the ATTRI to either
+ * construct and commit the ATTRD or drop the ATTRD's reference in the event of
+ * error. Simply drop the log's ATTRI reference now that the log is done with
+ * it.
+ */
+STATIC void
+xfs_attri_item_unpin(
+	struct xfs_log_item	*lip,
+	int			remove)
+{
+	xfs_attri_release(ATTRI_ITEM(lip));
+}
+
+
+STATIC void
+xfs_attri_item_release(
+	struct xfs_log_item	*lip)
+{
+	xfs_attri_release(ATTRI_ITEM(lip));
+}
+
+/*
+ * Allocate and initialize an attri item.  Caller may allocate an additional
+ * trailing buffer of the specified size
+ */
+STATIC struct xfs_attri_log_item *
+xfs_attri_init(
+	struct xfs_mount		*mp,
+	int				buffer_size)
+
+{
+	struct xfs_attri_log_item	*attrip;
+	uint				size;
+
+	size = sizeof(struct xfs_attri_log_item) + buffer_size;
+	attrip = kmem_alloc_large(size, KM_ZERO);
+	if (attrip == NULL)
+		return NULL;
+
+	xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI,
+			  &xfs_attri_item_ops);
+	attrip->attri_format.alfi_id = (uintptr_t)(void *)attrip;
+	atomic_set(&attrip->attri_refcount, 2);
+
+	return attrip;
+}
+
+/*
+ * Copy an attr format buffer from the given buf, and into the destination attr
+ * format structure.
+ */
+STATIC int
+xfs_attri_copy_format(
+	struct xfs_log_iovec		*buf,
+	struct xfs_attri_log_format	*dst_attr_fmt)
+{
+	struct xfs_attri_log_format	*src_attr_fmt = buf->i_addr;
+	uint				len;
+
+	len = sizeof(struct xfs_attri_log_format);
+	if (buf->i_len != len)
+		return -EFSCORRUPTED;
+
+	memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len);
+	return 0;
+}
+
+static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip)
+{
+	return container_of(lip, struct xfs_attrd_log_item, attrd_item);
+}
+
+STATIC void
+xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp)
+{
+	kmem_free(attrdp->attrd_item.li_lv_shadow);
+	kmem_free(attrdp);
+}
+
+STATIC void
+xfs_attrd_item_size(
+	struct xfs_log_item		*lip,
+	int				*nvecs,
+	int				*nbytes)
+{
+	*nvecs += 1;
+	*nbytes += sizeof(struct xfs_attrd_log_format);
+}
+
+/*
+ * This is called to fill in the log iovecs for the given attrd log item. We use
+ * only 1 iovec for the attrd_format, and we point that at the attr_log_format
+ * structure embedded in the attrd item.
+ */
+STATIC void
+xfs_attrd_item_format(
+	struct xfs_log_item	*lip,
+	struct xfs_log_vec	*lv)
+{
+	struct xfs_attrd_log_item	*attrdp = ATTRD_ITEM(lip);
+	struct xfs_log_iovec		*vecp = NULL;
+
+	attrdp->attrd_format.alfd_type = XFS_LI_ATTRD;
+	attrdp->attrd_format.alfd_size = 1;
+
+	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRD_FORMAT,
+			&attrdp->attrd_format,
+			sizeof(struct xfs_attrd_log_format));
+}
+
+/*
+ * The ATTRD is either committed or aborted if the transaction is cancelled. If
+ * the transaction is cancelled, drop our reference to the ATTRI and free the
+ * ATTRD.
+ */
+STATIC void
+xfs_attrd_item_release(
+	struct xfs_log_item		*lip)
+{
+	struct xfs_attrd_log_item	*attrdp = ATTRD_ITEM(lip);
+
+	xfs_attri_release(attrdp->attrd_attrip);
+	xfs_attrd_item_free(attrdp);
+}
+
+/*
+ * Performs one step of an attribute update intent and marks the attrd item
+ * dirty..  An attr operation may be a set or a remove.  Note that the
+ * transaction is marked dirty regardless of whether the operation succeeds or
+ * fails to support the ATTRI/ATTRD lifecycle rules.
+ */
+int
+xfs_trans_attr(
+	struct xfs_delattr_context	*dac,
+	struct xfs_attrd_log_item	*attrdp,
+	struct xfs_buf			**leaf_bp,
+	uint32_t			op_flags)
+{
+	struct xfs_da_args		*args = dac->da_args;
+	int				error;
+
+	error = xfs_qm_dqattach_locked(args->dp, 0);
+	if (error)
+		return error;
+
+	switch (op_flags) {
+	case XFS_ATTR_OP_FLAGS_SET:
+		args->op_flags |= XFS_DA_OP_ADDNAME;
+		error = xfs_attr_set_iter(dac, leaf_bp);
+		break;
+	case XFS_ATTR_OP_FLAGS_REMOVE:
+		ASSERT(XFS_IFORK_Q(args->dp));
+		error = xfs_attr_remove_iter(dac);
+		break;
+	default:
+		error = -EFSCORRUPTED;
+		break;
+	}
+
+	/*
+	 * Mark the transaction dirty, even on error. This ensures the
+	 * transaction is aborted, which:
+	 *
+	 * 1.) releases the ATTRI and frees the ATTRD
+	 * 2.) shuts down the filesystem
+	 */
+	args->trans->t_flags |= XFS_TRANS_DIRTY;
+
+	/*
+	 * attr intent/done items are null when delayed attributes are disabled
+	 */
+	if (attrdp)
+		set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
+
+	return error;
+}
+
+/* Log an attr to the intent item. */
+STATIC void
+xfs_attr_log_item(
+	struct xfs_trans		*tp,
+	struct xfs_attri_log_item	*attrip,
+	struct xfs_attr_item		*attr)
+{
+	struct xfs_attri_log_format	*attrp;
+
+	tp->t_flags |= XFS_TRANS_DIRTY;
+	set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags);
+
+	/*
+	 * At this point the xfs_attr_item has been constructed, and we've
+	 * created the log intent. Fill in the attri log item and log format
+	 * structure with fields from this xfs_attr_item
+	 */
+	attrp = &attrip->attri_format;
+	attrp->alfi_ino = attr->xattri_dac.da_args->dp->i_ino;
+	attrp->alfi_op_flags = attr->xattri_op_flags;
+	attrp->alfi_value_len = attr->xattri_dac.da_args->valuelen;
+	attrp->alfi_name_len = attr->xattri_dac.da_args->namelen;
+	attrp->alfi_attr_flags = attr->xattri_dac.da_args->attr_filter;
+
+	attrip->attri_name = (void *)attr->xattri_dac.da_args->name;
+	attrip->attri_value = attr->xattri_dac.da_args->value;
+	attrip->attri_name_len = attr->xattri_dac.da_args->namelen;
+	attrip->attri_value_len = attr->xattri_dac.da_args->valuelen;
+}
+
+/* Get an ATTRI. */
+static struct xfs_log_item *
+xfs_attr_create_intent(
+	struct xfs_trans		*tp,
+	struct list_head		*items,
+	unsigned int			count,
+	bool				sort)
+{
+	struct xfs_mount		*mp = tp->t_mountp;
+	struct xfs_attri_log_item	*attrip;
+	struct xfs_attr_item		*attr;
+
+	ASSERT(count == 1);
+
+	if (!xfs_hasdelattr(mp))
+		return NULL;
+
+	attrip = xfs_attri_init(mp, 0);
+	if (attrip == NULL)
+		return NULL;
+
+	xfs_trans_add_item(tp, &attrip->attri_item);
+	list_for_each_entry(attr, items, xattri_list)
+		xfs_attr_log_item(tp, attrip, attr);
+	return &attrip->attri_item;
+}
+
+/* Process an attr. */
+STATIC int
+xfs_attr_finish_item(
+	struct xfs_trans		*tp,
+	struct xfs_log_item		*done,
+	struct list_head		*item,
+	struct xfs_btree_cur		**state)
+{
+	struct xfs_attr_item		*attr;
+	struct xfs_attrd_log_item	*done_item = NULL;
+	int				error;
+	struct xfs_delattr_context	*dac;
+
+	attr = container_of(item, struct xfs_attr_item, xattri_list);
+	dac = &attr->xattri_dac;
+	if (done)
+		done_item = ATTRD_ITEM(done);
+
+	/*
+	 * Corner case that can happen during a recovery.  Because the first
+	 * iteration of a multi part delay op happens in xfs_attri_item_recover
+	 * to maintain the order of the log replay items.  But the new
+	 * transactions do not automatically rejoin during a recovery as they do
+	 * in a standard delay op, so we need to catch this here and rejoin the
+	 * leaf to the new transaction
+	 */
+	if (attr->xattri_dac.leaf_bp &&
+	    attr->xattri_dac.leaf_bp->b_transp != tp) {
+		xfs_trans_bjoin(tp, attr->xattri_dac.leaf_bp);
+		xfs_trans_bhold(tp, attr->xattri_dac.leaf_bp);
+	}
+
+	/*
+	 * Always reset trans after EAGAIN cycle
+	 * since the transaction is new
+	 */
+	dac->da_args->trans = tp;
+
+	error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
+			       attr->xattri_op_flags);
+	if (error != -EAGAIN)
+		kmem_free(attr);
+
+	return error;
+}
+
+/* Abort all pending ATTRs. */
+STATIC void
+xfs_attr_abort_intent(
+	struct xfs_log_item		*intent)
+{
+	xfs_attri_release(ATTRI_ITEM(intent));
+}
+
+/* Cancel an attr */
+STATIC void
+xfs_attr_cancel_item(
+	struct list_head		*item)
+{
+	struct xfs_attr_item		*attr;
+
+	attr = container_of(item, struct xfs_attr_item, xattri_list);
+	kmem_free(attr);
+}
+
+STATIC xfs_lsn_t
+xfs_attri_item_committed(
+	struct xfs_log_item		*lip,
+	xfs_lsn_t			lsn)
+{
+	struct xfs_attri_log_item	*attrip;
+	/*
+	 * The attrip refers to xfs_attr_item memory to log the name and value
+	 * with the intent item. This already occurred when the intent was
+	 * committed so these fields are no longer accessed. Clear them out of
+	 * caution since we're about to free the xfs_attr_item.
+	 */
+	attrip = ATTRI_ITEM(lip);
+	attrip->attri_name = NULL;
+	attrip->attri_value = NULL;
+
+	/*
+	 * The ATTRI is logged only once and cannot be moved in the log, so
+	 * simply return the lsn at which it's been logged.
+	 */
+	return lsn;
+}
+
+STATIC bool
+xfs_attri_item_match(
+	struct xfs_log_item	*lip,
+	uint64_t		intent_id)
+{
+	return ATTRI_ITEM(lip)->attri_format.alfi_id == intent_id;
+}
+
+/*
+ * This routine is called to allocate an "attr free done" log item.
+ */
+struct xfs_attrd_log_item *
+xfs_trans_get_attrd(struct xfs_trans		*tp,
+		  struct xfs_attri_log_item	*attrip)
+{
+	struct xfs_attrd_log_item		*attrdp;
+	uint					size;
+
+	ASSERT(tp != NULL);
+
+	size = sizeof(struct xfs_attrd_log_item);
+	attrdp = kmem_zalloc(size, 0);
+
+	xfs_log_item_init(tp->t_mountp, &attrdp->attrd_item, XFS_LI_ATTRD,
+			  &xfs_attrd_item_ops);
+	attrdp->attrd_attrip = attrip;
+	attrdp->attrd_format.alfd_alf_id = attrip->attri_format.alfi_id;
+
+	xfs_trans_add_item(tp, &attrdp->attrd_item);
+	return attrdp;
+}
+
+static const struct xfs_item_ops xfs_attrd_item_ops = {
+	.flags		= XFS_ITEM_RELEASE_WHEN_COMMITTED,
+	.iop_size	= xfs_attrd_item_size,
+	.iop_format	= xfs_attrd_item_format,
+	.iop_release    = xfs_attrd_item_release,
+};
+
+
+/* Get an ATTRD so we can process all the attrs. */
+static struct xfs_log_item *
+xfs_attr_create_done(
+	struct xfs_trans		*tp,
+	struct xfs_log_item		*intent,
+	unsigned int			count)
+{
+	if (!intent)
+		return NULL;
+
+	return &xfs_trans_get_attrd(tp, ATTRI_ITEM(intent))->attrd_item;
+}
+
+const struct xfs_defer_op_type xfs_attr_defer_type = {
+	.max_items	= 1,
+	.create_intent	= xfs_attr_create_intent,
+	.abort_intent	= xfs_attr_abort_intent,
+	.create_done	= xfs_attr_create_done,
+	.finish_item	= xfs_attr_finish_item,
+	.cancel_item	= xfs_attr_cancel_item,
+};
+
+/*
+ * Process an attr intent item that was recovered from the log.  We need to
+ * delete the attr that it describes.
+ */
+STATIC int
+xfs_attri_item_recover(
+	struct xfs_log_item		*lip,
+	struct list_head		*capture_list)
+{
+	struct xfs_attri_log_item	*attrip = ATTRI_ITEM(lip);
+	struct xfs_attr_item		*new_attr;
+	struct xfs_mount		*mp = lip->li_mountp;
+	struct xfs_inode		*ip;
+	struct xfs_da_args		args;
+	struct xfs_da_args		*new_args;
+	struct xfs_trans_res		tres;
+	bool				rsvd;
+	struct xfs_attri_log_format	*attrp;
+	int				error;
+	int				total;
+	int				local;
+	struct xfs_attrd_log_item	*done_item = NULL;
+	struct xfs_attr_item		attr = {
+		.xattri_op_flags	= attrip->attri_format.alfi_op_flags,
+		.xattri_dac.da_args	= &args,
+	};
+
+	/*
+	 * First check the validity of the attr described by the ATTRI.  If any
+	 * are bad, then assume that all are bad and just toss the ATTRI.
+	 */
+	attrp = &attrip->attri_format;
+	if (!(attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET ||
+	      attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE) ||
+	    (attrp->alfi_value_len > XATTR_SIZE_MAX) ||
+	    (attrp->alfi_name_len > XATTR_NAME_MAX) ||
+	    (attrp->alfi_name_len == 0) ||
+	    xfs_verify_ino(mp, attrp->alfi_ino) == false ||
+	    !xfs_hasdelattr(mp)) {
+		return -EFSCORRUPTED;
+	}
+
+	error = xfs_iget(mp, 0, attrp->alfi_ino, 0, 0, &ip);
+	if (error)
+		return error;
+
+	if (VFS_I(ip)->i_nlink == 0)
+		xfs_iflags_set(ip, XFS_IRECOVERY);
+
+	memset(&args, 0, sizeof(struct xfs_da_args));
+	args.dp = ip;
+	args.geo = mp->m_attr_geo;
+	args.op_flags = attrp->alfi_op_flags;
+	args.whichfork = XFS_ATTR_FORK;
+	args.name = attrip->attri_name;
+	args.namelen = attrp->alfi_name_len;
+	args.hashval = xfs_da_hashname(args.name, args.namelen);
+	args.attr_filter = attrp->alfi_attr_flags;
+
+	if (attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET) {
+		args.value = attrip->attri_value;
+		args.valuelen = attrp->alfi_value_len;
+		args.total = xfs_attr_calc_size(&args, &local);
+
+		tres.tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
+				 M_RES(mp)->tr_attrsetrt.tr_logres *
+					args.total;
+		tres.tr_logcount = XFS_ATTRSET_LOG_COUNT;
+		tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
+		total = args.total;
+	} else {
+		tres = M_RES(mp)->tr_attrrm;
+		total = XFS_ATTRRM_SPACE_RES(mp);
+	}
+	error = xfs_trans_alloc(mp, &tres, total, 0,
+				rsvd ? XFS_TRANS_RESERVE : 0, &args.trans);
+	if (error)
+		return error;
+
+	done_item = xfs_trans_get_attrd(args.trans, attrip);
+
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(args.trans, ip, 0);
+
+	error = xfs_trans_attr(&attr.xattri_dac, done_item,
+			       &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
+	if (error == -EAGAIN) {
+		/*
+		 * There's more work to do, so make a new xfs_attr_item and add
+		 * it to this transaction.  We dont use xfs_attr_item_init here
+		 * because we need the info stored in the current attr to
+		 * continue with this multi-part operation.  So, alloc space
+		 * for it and the args and copy everything there.
+		 */
+		new_attr = kmem_zalloc(sizeof(struct xfs_attr_item) +
+				       sizeof(struct xfs_da_args), KM_NOFS);
+		new_args = (struct xfs_da_args *)((char *)new_attr +
+			   sizeof(struct xfs_attr_item));
+
+		memcpy(new_args, &args, sizeof(struct xfs_da_args));
+		memcpy(new_attr, &attr, sizeof(struct xfs_attr_item));
+
+		new_attr->xattri_dac.da_args = new_args;
+		memset(&new_attr->xattri_list, 0, sizeof(struct list_head));
+
+		xfs_defer_add(args.trans, XFS_DEFER_OPS_TYPE_ATTR,
+			      &new_attr->xattri_list);
+
+		/* Do not send -EAGAIN back to caller */
+		error = 0;
+	} else if (error) {
+		xfs_trans_cancel(args.trans);
+		goto out;
+	}
+
+	xfs_defer_ops_capture_and_commit(args.trans, ip, capture_list);
+
+out:
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	xfs_irele(ip);
+	return error;
+}
+
+/* Relog an intent item to push the log tail forward. */
+static struct xfs_log_item *
+xfs_attri_item_relog(
+	struct xfs_log_item		*intent,
+	struct xfs_trans		*tp)
+{
+	struct xfs_attrd_log_item	*attrdp;
+	struct xfs_attri_log_item	*old_attrip;
+	struct xfs_attri_log_item	*new_attrip;
+	struct xfs_attri_log_format	*new_attrp;
+	struct xfs_attri_log_format	*old_attrp;
+	int				buffer_size;
+
+	old_attrip = ATTRI_ITEM(intent);
+	old_attrp = &old_attrip->attri_format;
+	buffer_size = old_attrp->alfi_value_len + old_attrp->alfi_name_len;
+
+	tp->t_flags |= XFS_TRANS_DIRTY;
+	attrdp = xfs_trans_get_attrd(tp, old_attrip);
+	set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
+
+	new_attrip = xfs_attri_init(tp->t_mountp, buffer_size);
+	new_attrp = &new_attrip->attri_format;
+
+	new_attrp->alfi_ino = old_attrp->alfi_ino;
+	new_attrp->alfi_op_flags = old_attrp->alfi_op_flags;
+	new_attrp->alfi_value_len = old_attrp->alfi_value_len;
+	new_attrp->alfi_name_len = old_attrp->alfi_name_len;
+	new_attrp->alfi_attr_flags = old_attrp->alfi_attr_flags;
+
+	new_attrip->attri_name_len = old_attrip->attri_name_len;
+	new_attrip->attri_name = ((char *)new_attrip) +
+				 sizeof(struct xfs_attri_log_item);
+	memcpy(new_attrip->attri_name, old_attrip->attri_name,
+		new_attrip->attri_name_len);
+
+	new_attrip->attri_value_len = old_attrip->attri_value_len;
+	if (new_attrip->attri_value_len > 0) {
+		new_attrip->attri_value = new_attrip->attri_name +
+					  new_attrip->attri_name_len;
+
+		memcpy(new_attrip->attri_value, old_attrip->attri_value,
+		       new_attrip->attri_value_len);
+	}
+
+	xfs_trans_add_item(tp, &new_attrip->attri_item);
+	set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags);
+
+	return &new_attrip->attri_item;
+}
+
+static const struct xfs_item_ops xfs_attri_item_ops = {
+	.iop_size	= xfs_attri_item_size,
+	.iop_format	= xfs_attri_item_format,
+	.iop_unpin	= xfs_attri_item_unpin,
+	.iop_committed	= xfs_attri_item_committed,
+	.iop_release    = xfs_attri_item_release,
+	.iop_recover	= xfs_attri_item_recover,
+	.iop_match	= xfs_attri_item_match,
+	.iop_relog	= xfs_attri_item_relog,
+};
+
+
+
+STATIC int
+xlog_recover_attri_commit_pass2(
+	struct xlog                     *log,
+	struct list_head		*buffer_list,
+	struct xlog_recover_item        *item,
+	xfs_lsn_t                       lsn)
+{
+	int                             error;
+	struct xfs_mount                *mp = log->l_mp;
+	struct xfs_attri_log_item       *attrip;
+	struct xfs_attri_log_format     *attri_formatp;
+	char				*name = NULL;
+	char				*value = NULL;
+	int				region = 0;
+	int				buffer_size;
+
+	attri_formatp = item->ri_buf[region].i_addr;
+
+	/* Validate xfs_attri_log_format */
+	if (attri_formatp->__pad != 0 || attri_formatp->alfi_name_len == 0 ||
+	    (attri_formatp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE &&
+	    attri_formatp->alfi_value_len != 0))
+		return -EFSCORRUPTED;
+
+	buffer_size = attri_formatp->alfi_name_len +
+		      attri_formatp->alfi_value_len;
+
+	attrip = xfs_attri_init(mp, buffer_size);
+	if (attrip == NULL)
+		return -ENOMEM;
+
+	error = xfs_attri_copy_format(&item->ri_buf[region],
+				      &attrip->attri_format);
+	if (error) {
+		xfs_attri_item_free(attrip);
+		return error;
+	}
+
+	attrip->attri_name_len = attri_formatp->alfi_name_len;
+	attrip->attri_value_len = attri_formatp->alfi_value_len;
+	region++;
+	name = ((char *)attrip) + sizeof(struct xfs_attri_log_item);
+	memcpy(name, item->ri_buf[region].i_addr, attrip->attri_name_len);
+	attrip->attri_name = name;
+
+	if (attrip->attri_value_len > 0) {
+		region++;
+		value = ((char *)attrip) + sizeof(struct xfs_attri_log_item) +
+			attrip->attri_name_len;
+		memcpy(value, item->ri_buf[region].i_addr,
+			attrip->attri_value_len);
+		attrip->attri_value = value;
+	}
+
+	/*
+	 * The ATTRI has two references. One for the ATTRD and one for ATTRI to
+	 * ensure it makes it into the AIL. Insert the ATTRI into the AIL
+	 * directly and drop the ATTRI reference. Note that
+	 * xfs_trans_ail_update() drops the AIL lock.
+	 */
+	xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn);
+	xfs_attri_release(attrip);
+	return 0;
+}
+
+const struct xlog_recover_item_ops xlog_attri_item_ops = {
+	.item_type	= XFS_LI_ATTRI,
+	.commit_pass2	= xlog_recover_attri_commit_pass2,
+};
+
+/*
+ * This routine is called when an ATTRD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding ATTRI if
+ * it was still in the log. To do this it searches the AIL for the ATTRI with
+ * an id equal to that in the ATTRD format structure. If we find it we drop
+ * the ATTRD reference, which removes the ATTRI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_attrd_commit_pass2(
+	struct xlog			*log,
+	struct list_head		*buffer_list,
+	struct xlog_recover_item	*item,
+	xfs_lsn_t			lsn)
+{
+	struct xfs_attrd_log_format	*attrd_formatp;
+
+	attrd_formatp = item->ri_buf[0].i_addr;
+	ASSERT((item->ri_buf[0].i_len ==
+				(sizeof(struct xfs_attrd_log_format))));
+
+	xlog_recover_release_intent(log, XFS_LI_ATTRI,
+				    attrd_formatp->alfd_alf_id);
+	return 0;
+}
+
+const struct xlog_recover_item_ops xlog_attrd_item_ops = {
+	.item_type	= XFS_LI_ATTRD,
+	.commit_pass2	= xlog_recover_attrd_commit_pass2,
+};
diff --git a/fs/xfs/xfs_attr_item.h b/fs/xfs/xfs_attr_item.h
new file mode 100644
index 0000000..27c6bae
--- /dev/null
+++ b/fs/xfs/xfs_attr_item.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2019 Oracle.  All Rights Reserved.
+ * Author: Allison Collins <allison.henderson@oracle.com>
+ */
+#ifndef	__XFS_ATTR_ITEM_H__
+#define	__XFS_ATTR_ITEM_H__
+
+/* kernel only ATTRI/ATTRD definitions */
+
+struct xfs_mount;
+struct kmem_zone;
+
+/*
+ * Define ATTR flag bits. Manipulated by set/clear/test_bit operators.
+ */
+#define	XFS_ATTRI_RECOVERED	1
+
+
+/*
+ * This is the "attr intention" log item.  It is used to log the fact that some
+ * attribute operations need to be processed.  An operation is currently either
+ * a set or remove.  Set or remove operations are described by the xfs_attr_item
+ * which may be logged to this intent.
+ *
+ * During a normal attr operation, name and value point to the name and value
+ * feilds of the calling functions xfs_da_args.  During a recovery, the name
+ * and value buffers are copied from the log, and stored in a trailing buffer
+ * attached to the xfs_attr_item until they are committed.  They are freed when
+ * the xfs_attr_item itself is freed when the work is done.
+ */
+struct xfs_attri_log_item {
+	struct xfs_log_item		attri_item;
+	atomic_t			attri_refcount;
+	int				attri_name_len;
+	int				attri_value_len;
+	void				*attri_name;
+	void				*attri_value;
+	struct xfs_attri_log_format	attri_format;
+};
+
+/*
+ * This is the "attr done" log item.  It is used to log the fact that some attrs
+ * earlier mentioned in an attri item have been freed.
+ */
+struct xfs_attrd_log_item {
+	struct xfs_attri_log_item	*attrd_attrip;
+	struct xfs_log_item		attrd_item;
+	struct xfs_attrd_log_format	attrd_format;
+};
+
+#endif	/* __XFS_ATTR_ITEM_H__ */
diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
index 8f8837f..d7787a5 100644
--- a/fs/xfs/xfs_attr_list.c
+++ b/fs/xfs/xfs_attr_list.c
@@ -15,6 +15,7 @@
 #include "xfs_inode.h"
 #include "xfs_trans.h"
 #include "xfs_bmap.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_attr_sf.h"
 #include "xfs_attr_leaf.h"
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 248083e..6682936 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -15,6 +15,8 @@
 #include "xfs_iwalk.h"
 #include "xfs_itable.h"
 #include "xfs_error.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
index c1771e7..62e1534 100644
--- a/fs/xfs/xfs_ioctl32.c
+++ b/fs/xfs/xfs_ioctl32.c
@@ -17,6 +17,8 @@
 #include "xfs_itable.h"
 #include "xfs_fsops.h"
 #include "xfs_rtalloc.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_ioctl.h"
 #include "xfs_ioctl32.h"
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 00369502f..ce04721 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -13,6 +13,8 @@
 #include "xfs_inode.h"
 #include "xfs_acl.h"
 #include "xfs_quota.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_trans.h"
 #include "xfs_trace.h"
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 0604183..290e57b 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -2070,6 +2070,10 @@ xlog_print_tic_res(
 	    REG_TYPE_STR(CUD_FORMAT, "cud_format"),
 	    REG_TYPE_STR(BUI_FORMAT, "bui_format"),
 	    REG_TYPE_STR(BUD_FORMAT, "bud_format"),
+	    REG_TYPE_STR(ATTRI_FORMAT, "attri_format"),
+	    REG_TYPE_STR(ATTRD_FORMAT, "attrd_format"),
+	    REG_TYPE_STR(ATTR_NAME, "attr_name"),
+	    REG_TYPE_STR(ATTR_VALUE, "attr_value"),
 	};
 	BUILD_BUG_ON(ARRAY_SIZE(res_type_str) != XLOG_REG_TYPE_MAX + 1);
 #undef REG_TYPE_STR
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 295a5c6..c0821b6 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1775,6 +1775,8 @@ static const struct xlog_recover_item_ops *xlog_recover_item_ops[] = {
 	&xlog_cud_item_ops,
 	&xlog_bui_item_ops,
 	&xlog_bud_item_ops,
+	&xlog_attri_item_ops,
+	&xlog_attrd_item_ops,
 };
 
 static const struct xlog_recover_item_ops *
diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
index 0aa87c21..bc9c25e 100644
--- a/fs/xfs/xfs_ondisk.h
+++ b/fs/xfs/xfs_ondisk.h
@@ -132,6 +132,8 @@ xfs_check_ondisk_structs(void)
 	XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format,	56);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat,	20);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header,		16);
+	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
+	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
 
 	/*
 	 * The v5 superblock format extended several v4 header structures with
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index bca48b3..9b0c790 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -10,6 +10,7 @@
 #include "xfs_log_format.h"
 #include "xfs_da_format.h"
 #include "xfs_inode.h"
+#include "xfs_da_btree.h"
 #include "xfs_attr.h"
 #include "xfs_acl.h"
 #include "xfs_da_btree.h"
-- 
2.7.4


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

* [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (15 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  5:02   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred Allison Henderson
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

This is a clean up patch that skips the flip flag logic for delayed attr
renames.  Since the log replay keeps the inode locked, we do not need to
worry about race windows with attr lookups.  So we can skip over
flipping the flag and the extra transaction roll for it

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c      | 51 +++++++++++++++++++++++++------------------
 fs/xfs/libxfs/xfs_attr_leaf.c |  3 ++-
 2 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index e4c1b4b..666cc69 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -337,6 +337,7 @@ xfs_attr_set_iter(
 	struct xfs_da_state		*state = NULL;
 	int				forkoff, error = 0;
 	int				retval = 0;
+	struct xfs_mount		*mp = args->dp->i_mount;
 
 	/* State machine switch */
 	switch (dac->dela_state) {
@@ -470,16 +471,21 @@ xfs_attr_set_iter(
 		 * "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.
-		 */
-		dac->dela_state = XFS_DAS_FLIP_LFLAG;
-		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
-		return -EAGAIN;
+		if (!xfs_hasdelattr(mp)) {
+			error = xfs_attr3_leaf_flipflags(args);
+			if (error)
+				return error;
+			/*
+			 * Commit the flag value change and start the next trans
+			 * in series.
+			 */
+			dac->dela_state = XFS_DAS_FLIP_LFLAG;
+			trace_xfs_attr_set_iter_return(dac->dela_state,
+						       args->dp);
+			return -EAGAIN;
+		}
+
+		/* fallthrough */
 	case XFS_DAS_FLIP_LFLAG:
 		/*
 		 * Dismantle the "old" attribute/value pair by removing a
@@ -588,17 +594,21 @@ xfs_attr_set_iter(
 		 * 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
-		 */
-		dac->dela_state = XFS_DAS_FLIP_NFLAG;
-		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
-		return -EAGAIN;
+		if (!xfs_hasdelattr(mp)) {
+			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;
+			trace_xfs_attr_set_iter_return(dac->dela_state,
+						       args->dp);
+			return -EAGAIN;
+		}
 
+		/* fallthrough */
 	case XFS_DAS_FLIP_NFLAG:
 		/*
 		 * Dismantle the "old" attribute/value pair by removing a
@@ -1277,7 +1287,6 @@ int xfs_attr_node_addname_work(
 	 * 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.
 	 */
-	args->attr_filter |= XFS_ATTR_INCOMPLETE;
 	state = xfs_da_state_alloc(args);
 	state->inleaf = 0;
 	error = xfs_da3_node_lookup_int(state, &retval);
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 3780141..ec707bd 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -1486,7 +1486,8 @@ xfs_attr3_leaf_add_work(
 	if (tmp)
 		entry->flags |= XFS_ATTR_LOCAL;
 	if (args->op_flags & XFS_DA_OP_RENAME) {
-		entry->flags |= XFS_ATTR_INCOMPLETE;
+		if (!xfs_hasdelattr(mp))
+			entry->flags |= XFS_ATTR_INCOMPLETE;
 		if ((args->blkno2 == args->blkno) &&
 		    (args->index2 <= args->index)) {
 			args->index2++;
-- 
2.7.4


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

* [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (16 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  5:00   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args Allison Henderson
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

From: Allison Collins <allison.henderson@oracle.com>

These routines to set up and start a new deferred attribute operations.
These functions are meant to be called by any routine needing to
initiate a deferred attribute operation as opposed to the existing
inline operations. New helper function xfs_attr_item_init also added.

Finally enable delayed attributes in xfs_attr_set and xfs_attr_remove.

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

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 666cc69..cec861e 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -25,6 +25,7 @@
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
 #include "xfs_attr_item.h"
+#include "xfs_attr.h"
 
 /*
  * xfs_attr.c
@@ -838,9 +839,10 @@ xfs_attr_set(
 		if (error != -ENOATTR && error != -EEXIST)
 			goto out_trans_cancel;
 
-		error = xfs_attr_set_args(args);
+		error = xfs_attr_set_deferred(args);
 		if (error)
 			goto out_trans_cancel;
+
 		/* shortform attribute has already been committed */
 		if (!args->trans)
 			goto out_unlock;
@@ -849,7 +851,7 @@ xfs_attr_set(
 		if (error != -EEXIST)
 			goto out_trans_cancel;
 
-		error = xfs_attr_remove_args(args);
+		error = xfs_attr_remove_deferred(args);
 		if (error)
 			goto out_trans_cancel;
 	}
@@ -879,6 +881,58 @@ xfs_attr_set(
 	goto out_unlock;
 }
 
+STATIC int
+xfs_attr_item_init(
+	struct xfs_da_args	*args,
+	unsigned int		op_flags,	/* op flag (set or remove) */
+	struct xfs_attr_item	**attr)		/* new xfs_attr_item */
+{
+
+	struct xfs_attr_item	*new;
+
+	new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS);
+	new->xattri_op_flags = op_flags;
+	new->xattri_dac.da_args = args;
+
+	*attr = new;
+	return 0;
+}
+
+/* Sets an attribute for an inode as a deferred operation */
+int
+xfs_attr_set_deferred(
+	struct xfs_da_args	*args)
+{
+	struct xfs_attr_item	*new;
+	int			error = 0;
+
+	error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new);
+	if (error)
+		return error;
+
+	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
+
+	return 0;
+}
+
+/* Removes an attribute for an inode as a deferred operation */
+int
+xfs_attr_remove_deferred(
+	struct xfs_da_args	*args)
+{
+
+	struct xfs_attr_item	*new;
+	int			error;
+
+	error  = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new);
+	if (error)
+		return error;
+
+	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
+
+	return 0;
+}
+
 /*========================================================================
  * External routines when attribute list is inside the inode
  *========================================================================*/
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index ee79763..4abf02c 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -462,5 +462,7 @@ 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);
 int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
+int xfs_attr_set_deferred(struct xfs_da_args *args);
+int xfs_attr_remove_deferred(struct xfs_da_args *args);
 
 #endif	/* __XFS_ATTR_H__ */
-- 
2.7.4


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

* [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (17 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  4:58   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 20/22] xfs: Add delayed attributes error tag Allison Henderson
                   ` (2 subsequent siblings)
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

Remove xfs_attr_set_args, xfs_attr_remove_args, and xfs_attr_trans_roll.
These high level loops are now driven by the delayed operations code,
and can be removed.

Additionally collapse in the leaf_bp parameter of xfs_attr_set_iter
since we only have one caller that passes dac->leaf_bp

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c        | 97 +++--------------------------------------
 fs/xfs/libxfs/xfs_attr.h        | 10 ++---
 fs/xfs/libxfs/xfs_attr_remote.c |  1 -
 fs/xfs/xfs_attr_item.c          |  8 ++--
 4 files changed, 11 insertions(+), 105 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index cec861e..8b62447 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -63,8 +63,6 @@ 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);
-int xfs_attr_set_iter(struct xfs_delattr_context *dac,
-		      struct xfs_buf **leaf_bp);
 
 int
 xfs_inode_hasattr(
@@ -223,67 +221,13 @@ 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.
- */
-STATIC 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);
-		if (error)
-			return error;
-	} else
-		error = xfs_trans_roll_inode(&args->trans, args->dp);
-
-	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_delattr_context	*dac,
-	struct xfs_buf			**leaf_bp)
+	struct xfs_delattr_context	*dac)
 {
 	struct xfs_da_args		*args = dac->da_args;
 	struct xfs_inode		*dp = args->dp;
+	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
 	int				error = 0;
 
 	/*
@@ -316,7 +260,6 @@ xfs_attr_set_fmt(
 	 * add.
 	 */
 	trace_xfs_attr_set_fmt_return(XFS_DAS_UNINIT, args->dp);
-	dac->flags |= XFS_DAC_DEFER_FINISH;
 	return -EAGAIN;
 }
 
@@ -329,10 +272,10 @@ xfs_attr_set_fmt(
  */
 int
 xfs_attr_set_iter(
-	struct xfs_delattr_context	*dac,
-	struct xfs_buf			**leaf_bp)
+	struct xfs_delattr_context	*dac)
 {
 	struct xfs_da_args              *args = dac->da_args;
+	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
 	struct xfs_inode		*dp = args->dp;
 	struct xfs_buf			*bp = NULL;
 	struct xfs_da_state		*state = NULL;
@@ -344,7 +287,7 @@ xfs_attr_set_iter(
 	switch (dac->dela_state) {
 	case XFS_DAS_UNINIT:
 		if (xfs_attr_is_shortform(dp))
-			return xfs_attr_set_fmt(dac, leaf_bp);
+			return xfs_attr_set_fmt(dac);
 
 		/*
 		 * After a shortform to leaf conversion, we need to hold the
@@ -381,7 +324,6 @@ xfs_attr_set_iter(
 				 * be a node, so we'll fall down into the node
 				 * handling code below
 				 */
-				dac->flags |= XFS_DAC_DEFER_FINISH;
 				trace_xfs_attr_set_iter_return(
 					dac->dela_state, args->dp);
 				return -EAGAIN;
@@ -687,32 +629,6 @@ xfs_has_attr(
 
 /*
  * Remove the attribute specified in @args.
- */
-int
-xfs_attr_remove_args(
-	struct xfs_da_args	*args)
-{
-	int				error;
-	struct xfs_delattr_context	dac = {
-		.da_args	= 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;
-}
-
-/*
- * Remove the attribute specified in @args.
  *
  * This function may return -EAGAIN to signal that the transaction needs to be
  * rolled.  Callers should continue calling this function until they receive a
@@ -1297,7 +1213,6 @@ xfs_attr_node_addname(
 			 * this. dela_state is still unset by this function at
 			 * this point.
 			 */
-			dac->flags |= XFS_DAC_DEFER_FINISH;
 			trace_xfs_attr_node_addname_return(
 					dac->dela_state, args->dp);
 			return -EAGAIN;
@@ -1312,7 +1227,6 @@ xfs_attr_node_addname(
 		error = xfs_da3_split(state);
 		if (error)
 			goto out;
-		dac->flags |= XFS_DAC_DEFER_FINISH;
 	} else {
 		/*
 		 * Addition succeeded, update Btree hashvals.
@@ -1599,7 +1513,6 @@ xfs_attr_node_removename_iter(
 			if (error)
 				goto out;
 
-			dac->flags |= XFS_DAC_DEFER_FINISH;
 			dac->dela_state = XFS_DAS_RM_SHRINK;
 			trace_xfs_attr_node_removename_iter_return(
 					dac->dela_state, args->dp);
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 4abf02c..f82c0b1 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -393,9 +393,8 @@ enum xfs_delattr_state {
 /*
  * 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*/
-#define XFS_DAC_DELAYED_OP_INIT		0x04 /* delayed operations init*/
+#define XFS_DAC_LEAF_ADDNAME_INIT	0x01 /* xfs_attr_leaf_addname init*/
+#define XFS_DAC_DELAYED_OP_INIT		0x02 /* delayed operations init*/
 
 /*
  * Context used for keeping track of delayed attribute operations
@@ -452,11 +451,8 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
 int xfs_attr_get_ilocked(struct xfs_da_args *args);
 int xfs_attr_get(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
-int xfs_attr_set_args(struct xfs_da_args *args);
-int xfs_attr_set_iter(struct xfs_delattr_context *dac,
-		      struct xfs_buf **leaf_bp);
+int xfs_attr_set_iter(struct xfs_delattr_context *dac);
 int xfs_has_attr(struct xfs_da_args *args);
-int xfs_attr_remove_args(struct xfs_da_args *args);
 int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
 bool xfs_attr_namecheck(const void *name, size_t length);
 void xfs_delattr_context_init(struct xfs_delattr_context *dac,
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index b6554a3..78bb552 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -762,7 +762,6 @@ xfs_attr_rmtval_remove(
 	 * by the parent
 	 */
 	if (!done) {
-		dac->flags |= XFS_DAC_DEFER_FINISH;
 		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
 		return -EAGAIN;
 	}
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 8c8f72d..13b289b 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -291,7 +291,6 @@ int
 xfs_trans_attr(
 	struct xfs_delattr_context	*dac,
 	struct xfs_attrd_log_item	*attrdp,
-	struct xfs_buf			**leaf_bp,
 	uint32_t			op_flags)
 {
 	struct xfs_da_args		*args = dac->da_args;
@@ -304,7 +303,7 @@ xfs_trans_attr(
 	switch (op_flags) {
 	case XFS_ATTR_OP_FLAGS_SET:
 		args->op_flags |= XFS_DA_OP_ADDNAME;
-		error = xfs_attr_set_iter(dac, leaf_bp);
+		error = xfs_attr_set_iter(dac);
 		break;
 	case XFS_ATTR_OP_FLAGS_REMOVE:
 		ASSERT(XFS_IFORK_Q(args->dp));
@@ -428,8 +427,7 @@ xfs_attr_finish_item(
 	 */
 	dac->da_args->trans = tp;
 
-	error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
-			       attr->xattri_op_flags);
+	error = xfs_trans_attr(dac, done_item, attr->xattri_op_flags);
 	if (error != -EAGAIN)
 		kmem_free(attr);
 
@@ -625,7 +623,7 @@ xfs_attri_item_recover(
 	xfs_trans_ijoin(args.trans, ip, 0);
 
 	error = xfs_trans_attr(&attr.xattri_dac, done_item,
-			       &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
+			       attrp->alfi_op_flags);
 	if (error == -EAGAIN) {
 		/*
 		 * There's more work to do, so make a new xfs_attr_item and add
-- 
2.7.4


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

* [PATCH v15 20/22] xfs: Add delayed attributes error tag
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (18 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 21/22] xfs: Add delattr mount option Allison Henderson
  2021-02-18 16:53 ` [PATCH v15 22/22] xfs: Merge xfs_delattr_context into xfs_attr_item Allison Henderson
  21 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

This patch adds an error tag that we can use to test delayed attribute
recovery and replay

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_errortag.h | 4 +++-
 fs/xfs/xfs_attr_item.c       | 8 ++++++++
 fs/xfs/xfs_error.c           | 3 +++
 3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/libxfs/xfs_errortag.h b/fs/xfs/libxfs/xfs_errortag.h
index 6ca9084..72ad14b 100644
--- a/fs/xfs/libxfs/xfs_errortag.h
+++ b/fs/xfs/libxfs/xfs_errortag.h
@@ -58,7 +58,8 @@
 #define XFS_ERRTAG_BUF_IOERROR				35
 #define XFS_ERRTAG_REDUCE_MAX_IEXTENTS			36
 #define XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT		37
-#define XFS_ERRTAG_MAX					38
+#define XFS_ERRTAG_DELAYED_ATTR				38
+#define XFS_ERRTAG_MAX					39
 
 /*
  * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
@@ -101,5 +102,6 @@
 #define XFS_RANDOM_BUF_IOERROR				XFS_RANDOM_DEFAULT
 #define XFS_RANDOM_REDUCE_MAX_IEXTENTS			1
 #define XFS_RANDOM_BMAP_ALLOC_MINLEN_EXTENT		1
+#define XFS_RANDOM_DELAYED_ATTR				1
 
 #endif /* __XFS_ERRORTAG_H_ */
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 13b289b..842f84d 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -40,6 +40,8 @@
 #include "xfs_trans_space.h"
 #include "xfs_log_priv.h"
 #include "xfs_log_recover.h"
+#include "xfs_error.h"
+#include "xfs_errortag.h"
 
 static const struct xfs_item_ops xfs_attri_item_ops;
 static const struct xfs_item_ops xfs_attrd_item_ops;
@@ -300,6 +302,11 @@ xfs_trans_attr(
 	if (error)
 		return error;
 
+	if (XFS_TEST_ERROR(false, args->dp->i_mount, XFS_ERRTAG_DELAYED_ATTR)) {
+		error = -EIO;
+		goto out;
+	}
+
 	switch (op_flags) {
 	case XFS_ATTR_OP_FLAGS_SET:
 		args->op_flags |= XFS_DA_OP_ADDNAME;
@@ -314,6 +321,7 @@ xfs_trans_attr(
 		break;
 	}
 
+out:
 	/*
 	 * Mark the transaction dirty, even on error. This ensures the
 	 * transaction is aborted, which:
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index 185b491..39d1130 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -56,6 +56,7 @@ static unsigned int xfs_errortag_random_default[] = {
 	XFS_RANDOM_BUF_IOERROR,
 	XFS_RANDOM_REDUCE_MAX_IEXTENTS,
 	XFS_RANDOM_BMAP_ALLOC_MINLEN_EXTENT,
+	XFS_RANDOM_DELAYED_ATTR,
 };
 
 struct xfs_errortag_attr {
@@ -168,6 +169,7 @@ XFS_ERRORTAG_ATTR_RW(iunlink_fallback,	XFS_ERRTAG_IUNLINK_FALLBACK);
 XFS_ERRORTAG_ATTR_RW(buf_ioerror,	XFS_ERRTAG_BUF_IOERROR);
 XFS_ERRORTAG_ATTR_RW(reduce_max_iextents,	XFS_ERRTAG_REDUCE_MAX_IEXTENTS);
 XFS_ERRORTAG_ATTR_RW(bmap_alloc_minlen_extent,	XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT);
+XFS_ERRORTAG_ATTR_RW(delayed_attr,	XFS_ERRTAG_DELAYED_ATTR);
 
 static struct attribute *xfs_errortag_attrs[] = {
 	XFS_ERRORTAG_ATTR_LIST(noerror),
@@ -208,6 +210,7 @@ static struct attribute *xfs_errortag_attrs[] = {
 	XFS_ERRORTAG_ATTR_LIST(buf_ioerror),
 	XFS_ERRORTAG_ATTR_LIST(reduce_max_iextents),
 	XFS_ERRORTAG_ATTR_LIST(bmap_alloc_minlen_extent),
+	XFS_ERRORTAG_ATTR_LIST(delayed_attr),
 	NULL,
 };
 
-- 
2.7.4


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

* [PATCH v15 21/22] xfs: Add delattr mount option
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (19 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 20/22] xfs: Add delayed attributes error tag Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  2021-02-26  4:29   ` Darrick J. Wong
  2021-02-18 16:53 ` [PATCH v15 22/22] xfs: Merge xfs_delattr_context into xfs_attr_item Allison Henderson
  21 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

This patch adds a mount option to enable delayed attributes. Eventually
this can be removed when delayed attrs becomes permanent.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.h | 2 +-
 fs/xfs/xfs_mount.h       | 1 +
 fs/xfs/xfs_super.c       | 6 +++++-
 fs/xfs/xfs_xattr.c       | 2 ++
 4 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index f82c0b1..35f3a53 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -30,7 +30,7 @@ struct xfs_attr_list_context;
 
 static inline bool xfs_hasdelattr(struct xfs_mount *mp)
 {
-	return false;
+	return mp->m_flags & XFS_MOUNT_DELATTR;
 }
 
 /*
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 659ad95..57cd914 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -250,6 +250,7 @@ typedef struct xfs_mount {
 #define XFS_MOUNT_NOATTR2	(1ULL << 25)	/* disable use of attr2 format */
 #define XFS_MOUNT_DAX_ALWAYS	(1ULL << 26)
 #define XFS_MOUNT_DAX_NEVER	(1ULL << 27)
+#define XFS_MOUNT_DELATTR	(1ULL << 28)	/* enable delayed attributes */
 
 /*
  * Max and min values for mount-option defined I/O
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 21b1d03..f6b08f9 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -93,7 +93,7 @@ enum {
 	Opt_filestreams, Opt_quota, Opt_noquota, Opt_usrquota, Opt_grpquota,
 	Opt_prjquota, Opt_uquota, Opt_gquota, Opt_pquota,
 	Opt_uqnoenforce, Opt_gqnoenforce, Opt_pqnoenforce, Opt_qnoenforce,
-	Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum,
+	Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum, Opt_delattr
 };
 
 static const struct fs_parameter_spec xfs_fs_parameters[] = {
@@ -138,6 +138,7 @@ static const struct fs_parameter_spec xfs_fs_parameters[] = {
 	fsparam_flag("nodiscard",	Opt_nodiscard),
 	fsparam_flag("dax",		Opt_dax),
 	fsparam_enum("dax",		Opt_dax_enum, dax_param_enums),
+	fsparam_flag("delattr",		Opt_delattr),
 	{}
 };
 
@@ -1263,6 +1264,9 @@ xfs_fs_parse_param(
 		xfs_mount_set_dax_mode(mp, result.uint_32);
 		return 0;
 #endif
+	case Opt_delattr:
+		mp->m_flags |= XFS_MOUNT_DELATTR;
+		return 0;
 	/* Following mount options will be removed in September 2025 */
 	case Opt_ikeep:
 		xfs_warn(mp, "%s mount option is deprecated.", param->key);
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index 9b0c790..8ec61df 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -8,6 +8,8 @@
 #include "xfs_shared.h"
 #include "xfs_format.h"
 #include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
 #include "xfs_da_format.h"
 #include "xfs_inode.h"
 #include "xfs_da_btree.h"
-- 
2.7.4


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

* [PATCH v15 22/22] xfs: Merge xfs_delattr_context into xfs_attr_item
  2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
                   ` (20 preceding siblings ...)
  2021-02-18 16:53 ` [PATCH v15 21/22] xfs: Add delattr mount option Allison Henderson
@ 2021-02-18 16:53 ` Allison Henderson
  21 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-18 16:53 UTC (permalink / raw)
  To: linux-xfs

This is a clean up patch that merges xfs_delattr_context into
xfs_attr_item.  Now that the refactoring is complete and the delayed
operation infastructure is in place, we can combine these to eliminate
the extra struct

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c        | 159 ++++++++++++++++++++--------------------
 fs/xfs/libxfs/xfs_attr.h        |  40 +++++-----
 fs/xfs/libxfs/xfs_attr_remote.c |  35 ++++-----
 fs/xfs/libxfs/xfs_attr_remote.h |   6 +-
 fs/xfs/xfs_attr_item.c          |  46 ++++++------
 5 files changed, 139 insertions(+), 147 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 8b62447..76ad617 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -55,10 +55,10 @@ 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_delattr_context *dac);
-STATIC int xfs_attr_node_addname_find_attr(struct xfs_delattr_context *dac);
-STATIC int xfs_attr_node_addname_work(struct xfs_delattr_context *dac);
-STATIC int xfs_attr_node_removename_iter(struct xfs_delattr_context *dac);
+STATIC int xfs_attr_node_addname(struct xfs_attr_item *attr);
+STATIC int xfs_attr_node_addname_find_attr(struct xfs_attr_item *attr);
+STATIC int xfs_attr_node_addname_work(struct xfs_attr_item *attr);
+STATIC int xfs_attr_node_removename_iter(struct xfs_attr_item *attr);
 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);
@@ -223,11 +223,11 @@ xfs_attr_is_shortform(
 
 STATIC int
 xfs_attr_set_fmt(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	struct xfs_inode		*dp = args->dp;
-	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
+	struct xfs_buf			**leaf_bp = &attr->xattri_leaf_bp;
 	int				error = 0;
 
 	/*
@@ -272,10 +272,10 @@ xfs_attr_set_fmt(
  */
 int
 xfs_attr_set_iter(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args              *args = dac->da_args;
-	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
+	struct xfs_da_args              *args = attr->xattri_da_args;
+	struct xfs_buf			**leaf_bp = &attr->xattri_leaf_bp;
 	struct xfs_inode		*dp = args->dp;
 	struct xfs_buf			*bp = NULL;
 	struct xfs_da_state		*state = NULL;
@@ -284,10 +284,10 @@ xfs_attr_set_iter(
 	struct xfs_mount		*mp = args->dp->i_mount;
 
 	/* State machine switch */
-	switch (dac->dela_state) {
+	switch (attr->xattri_dela_state) {
 	case XFS_DAS_UNINIT:
 		if (xfs_attr_is_shortform(dp))
-			return xfs_attr_set_fmt(dac);
+			return xfs_attr_set_fmt(attr);
 
 		/*
 		 * After a shortform to leaf conversion, we need to hold the
@@ -325,18 +325,18 @@ xfs_attr_set_iter(
 				 * handling code below
 				 */
 				trace_xfs_attr_set_iter_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 				return -EAGAIN;
 			}
 			else if (error)
 				return error;
 		}
 		else {
-			error = xfs_attr_node_addname_find_attr(dac);
+			error = xfs_attr_node_addname_find_attr(attr);
 			if (error)
 				return error;
 
-			error = xfs_attr_node_addname(dac);
+			error = xfs_attr_node_addname(attr);
 			if (error)
 				return error;
 
@@ -347,14 +347,15 @@ xfs_attr_set_iter(
 			if (!args->rmtblkno && !args->rmtblkno2)
 				return error;
 
-			dac->dela_state = XFS_DAS_FOUND_NBLK;
-			trace_xfs_attr_set_iter_return(dac->dela_state,
+			attr->xattri_dela_state = XFS_DAS_FOUND_NBLK;
+			trace_xfs_attr_set_iter_return(attr->xattri_dela_state,
 						       args->dp);
 			return -EAGAIN;
 		}
 
-		dac->dela_state = XFS_DAS_FOUND_LBLK;
-		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
+		attr->xattri_dela_state = XFS_DAS_FOUND_LBLK;
+		trace_xfs_attr_set_iter_return(attr->xattri_dela_state,
+					       args->dp);
 		return -EAGAIN;
 
         case XFS_DAS_FOUND_LBLK:
@@ -366,10 +367,10 @@ xfs_attr_set_iter(
 		 */
 
 		/* 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 ((attr->xattri_flags & XFS_DAC_LEAF_ADDNAME_INIT) == 0) {
+			attr->xattri_flags |= XFS_DAC_LEAF_ADDNAME_INIT;
 			if (args->rmtblkno > 0) {
-				error = xfs_attr_rmtval_find_space(dac);
+				error = xfs_attr_rmtval_find_space(attr);
 				if (error)
 					return error;
 			}
@@ -379,12 +380,12 @@ xfs_attr_set_iter(
 		 * Roll through the "value", allocating blocks on disk as
 		 * required.
 		 */
-		if (dac->blkcnt > 0) {
-			error = xfs_attr_rmtval_set_blk(dac);
+		if (attr->xattri_blkcnt > 0) {
+			error = xfs_attr_rmtval_set_blk(attr);
 			if (error)
 				return error;
 
-			trace_xfs_attr_set_iter_return(dac->dela_state,
+			trace_xfs_attr_set_iter_return(attr->xattri_dela_state,
 						       args->dp);
 			return -EAGAIN;
 		}
@@ -422,8 +423,8 @@ xfs_attr_set_iter(
 			 * Commit the flag value change and start the next trans
 			 * in series.
 			 */
-			dac->dela_state = XFS_DAS_FLIP_LFLAG;
-			trace_xfs_attr_set_iter_return(dac->dela_state,
+			attr->xattri_dela_state = XFS_DAS_FLIP_LFLAG;
+			trace_xfs_attr_set_iter_return(attr->xattri_dela_state,
 						       args->dp);
 			return -EAGAIN;
 		}
@@ -441,15 +442,15 @@ xfs_attr_set_iter(
 			return error;
 
 		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
-		dac->dela_state = XFS_DAS_RM_LBLK;
+		attr->xattri_dela_state = XFS_DAS_RM_LBLK;
 
 		/* fallthrough */
 	case XFS_DAS_RM_LBLK:
 		if (args->rmtblkno) {
-			error = xfs_attr_rmtval_remove(dac);
+			error = xfs_attr_rmtval_remove(attr);
 			if (error == -EAGAIN)
 				trace_xfs_attr_set_iter_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 			if (error)
 				return error;
 		}
@@ -487,7 +488,7 @@ xfs_attr_set_iter(
 			 * Open coded xfs_attr_rmtval_set without trans
 			 * handling
 			 */
-			error = xfs_attr_rmtval_find_space(dac);
+			error = xfs_attr_rmtval_find_space(attr);
 			if (error)
 				return error;
 
@@ -496,19 +497,19 @@ xfs_attr_set_iter(
 			 * as required.  Set the state in case of -EAGAIN return
 			 * code
 			 */
-			dac->dela_state = XFS_DAS_ALLOC_NODE;
+			attr->xattri_dela_state = XFS_DAS_ALLOC_NODE;
 		}
 
 		/* fallthrough */
 	case XFS_DAS_ALLOC_NODE:
 		if (args->rmtblkno > 0) {
-			if (dac->blkcnt > 0) {
-				error = xfs_attr_rmtval_set_blk(dac);
+			if (attr->xattri_blkcnt > 0) {
+				error = xfs_attr_rmtval_set_blk(attr);
 				if (error)
 					return error;
 
 				trace_xfs_attr_set_iter_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 				return -EAGAIN;
 			}
 
@@ -545,8 +546,8 @@ xfs_attr_set_iter(
 			 * Commit the flag value change and start the next trans
 			 * in series
 			 */
-			dac->dela_state = XFS_DAS_FLIP_NFLAG;
-			trace_xfs_attr_set_iter_return(dac->dela_state,
+			attr->xattri_dela_state = XFS_DAS_FLIP_NFLAG;
+			trace_xfs_attr_set_iter_return(attr->xattri_dela_state,
 						       args->dp);
 			return -EAGAIN;
 		}
@@ -564,21 +565,21 @@ xfs_attr_set_iter(
 			return error;
 
 		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
-		dac->dela_state = XFS_DAS_RM_NBLK;
+		attr->xattri_dela_state = XFS_DAS_RM_NBLK;
 
 		/* fallthrough */
 	case XFS_DAS_RM_NBLK:
 		if (args->rmtblkno) {
-			error = xfs_attr_rmtval_remove(dac);
+			error = xfs_attr_rmtval_remove(attr);
 			if (error == -EAGAIN)
 				trace_xfs_attr_set_iter_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 
 			if (error)
 				return error;
 		}
 
-		error = xfs_attr_node_addname_work(dac);
+		error = xfs_attr_node_addname_work(attr);
 
 out:
 		if (state)
@@ -588,7 +589,7 @@ xfs_attr_set_iter(
 		return retval;
 
 	default:
-		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
+		ASSERT(attr->xattri_dela_state != XFS_DAS_RM_SHRINK);
 		break;
 	}
 
@@ -636,13 +637,13 @@ xfs_has_attr(
  */
 int
 xfs_attr_remove_iter(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	struct xfs_inode		*dp = args->dp;
 
 	/* If we are shrinking a node, resume shrink */
-	if (dac->dela_state == XFS_DAS_RM_SHRINK)
+	if (attr->xattri_dela_state == XFS_DAS_RM_SHRINK)
 		goto node;
 
 	if (!xfs_inode_hasattr(dp))
@@ -657,7 +658,7 @@ xfs_attr_remove_iter(
 		return xfs_attr_leaf_removename(args);
 node:
 	/* If we are not short form or leaf, then proceed to remove node */
-	return  xfs_attr_node_removename_iter(dac);
+	return  xfs_attr_node_removename_iter(attr);
 }
 
 /*
@@ -808,7 +809,7 @@ xfs_attr_item_init(
 
 	new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS);
 	new->xattri_op_flags = op_flags;
-	new->xattri_dac.da_args = args;
+	new->xattri_da_args = args;
 
 	*attr = new;
 	return 0;
@@ -1121,16 +1122,16 @@ xfs_attr_node_hasname(
 
 STATIC int
 xfs_attr_node_addname_find_attr(
-	struct xfs_delattr_context	*dac)
+	 struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_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, &dac->da_state);
+	retval = xfs_attr_node_hasname(args, &attr->xattri_da_state);
 	if (retval != -ENOATTR && retval != -EEXIST)
 		return retval;
 
@@ -1158,8 +1159,8 @@ xfs_attr_node_addname_find_attr(
 
 	return 0;
 out:
-	if (dac->da_state)
-		xfs_da_state_free(dac->da_state);
+	if (attr->xattri_da_state)
+		xfs_da_state_free(attr->xattri_da_state);
 	return retval;
 }
 
@@ -1180,10 +1181,10 @@ xfs_attr_node_addname_find_attr(
  */
 STATIC int
 xfs_attr_node_addname(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
-	struct xfs_da_state		*state = dac->da_state;
+	struct xfs_da_args		*args = attr->xattri_da_args;
+	struct xfs_da_state		*state = attr->xattri_da_state;
 	struct xfs_da_state_blk		*blk;
 	int				error;
 
@@ -1214,7 +1215,7 @@ xfs_attr_node_addname(
 			 * this point.
 			 */
 			trace_xfs_attr_node_addname_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 			return -EAGAIN;
 		}
 
@@ -1243,9 +1244,9 @@ xfs_attr_node_addname(
 
 STATIC
 int xfs_attr_node_addname_work(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	struct xfs_da_state		*state = NULL;
 	struct xfs_da_state_blk		*blk;
 	int				retval = 0;
@@ -1353,10 +1354,10 @@ xfs_attr_leaf_mark_incomplete(
  */
 STATIC
 int xfs_attr_node_removename_setup(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
-	struct xfs_da_state		**state = &dac->da_state;
+	struct xfs_da_args		*args = attr->xattri_da_args;
+	struct xfs_da_state		**state = &attr->xattri_da_state;
 	int				error;
 
 	error = xfs_attr_node_hasname(args, state);
@@ -1384,7 +1385,7 @@ int xfs_attr_node_removename_setup(
 
 STATIC int
 xfs_attr_node_remove_rmt (
-	struct xfs_delattr_context	*dac,
+	struct xfs_attr_item		*attr,
 	struct xfs_da_state		*state)
 {
 	int				error = 0;
@@ -1392,10 +1393,10 @@ xfs_attr_node_remove_rmt (
 	/*
 	 * May return -EAGAIN to request that the caller recall this function
 	 */
-	error = xfs_attr_rmtval_remove(dac);
+	error = xfs_attr_rmtval_remove(attr);
 	if (error == -EAGAIN)
-		trace_xfs_attr_node_remove_rmt_return(dac->dela_state,
-						      dac->da_args->dp);
+		trace_xfs_attr_node_remove_rmt_return(attr->xattri_dela_state,
+						      attr->xattri_da_args->dp);
 	if (error)
 		return error;
 
@@ -1439,10 +1440,10 @@ xfs_attr_node_remove_cleanup(
  */
 STATIC int
 xfs_attr_node_remove_step(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
-	struct xfs_da_state		*state = dac->da_state;
+	struct xfs_da_args		*args = attr->xattri_da_args;
+	struct xfs_da_state		*state = attr->xattri_da_state;
 	int				error = 0;
 
 	/*
@@ -1454,7 +1455,7 @@ xfs_attr_node_remove_step(
 		/*
 		 * May return -EAGAIN. Remove blocks until args->rmtblkno == 0
 		 */
-		error = xfs_attr_node_remove_rmt(dac, state);
+		error = xfs_attr_node_remove_rmt(attr, state);
 		if (error)
 			return error;
 	}
@@ -1475,29 +1476,29 @@ xfs_attr_node_remove_step(
  */
 STATIC int
 xfs_attr_node_removename_iter(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	struct xfs_da_state		*state = NULL;
 	int				retval, error;
 	struct xfs_inode		*dp = args->dp;
 
 	trace_xfs_attr_node_removename(args);
 
-	if (!dac->da_state) {
-		error = xfs_attr_node_removename_setup(dac);
+	if (!attr->xattri_da_state) {
+		error = xfs_attr_node_removename_setup(attr);
 		if (error)
 			goto out;
 	}
-	state = dac->da_state;
+	state = attr->xattri_da_state;
 
-	switch (dac->dela_state) {
+	switch (attr->xattri_dela_state) {
 	case XFS_DAS_UNINIT:
 		/*
 		 * repeatedly remove remote blocks, remove the entry and join.
 		 * returns -EAGAIN or 0 for completion of the step.
 		 */
-		error = xfs_attr_node_remove_step(dac);
+		error = xfs_attr_node_remove_step(attr);
 		if (error)
 			break;
 
@@ -1513,9 +1514,9 @@ xfs_attr_node_removename_iter(
 			if (error)
 				goto out;
 
-			dac->dela_state = XFS_DAS_RM_SHRINK;
+			attr->xattri_dela_state = XFS_DAS_RM_SHRINK;
 			trace_xfs_attr_node_removename_iter_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 			return -EAGAIN;
 		}
 
@@ -1536,7 +1537,7 @@ xfs_attr_node_removename_iter(
 
 	if (error == -EAGAIN) {
 		trace_xfs_attr_node_removename_iter_return(
-					dac->dela_state, args->dp);
+					attr->xattri_dela_state, args->dp);
 		return error;
 	}
 out:
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 35f3a53..c0015a4 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -370,7 +370,7 @@ struct xfs_attr_list_context {
  */
 
 /*
- * Enum values for xfs_delattr_context.da_state
+ * Enum values for xfs_attr_item.xattri_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
@@ -391,7 +391,7 @@ enum xfs_delattr_state {
 };
 
 /*
- * Defines for xfs_delattr_context.flags
+ * Defines for xfs_attr_item.xattri_flags
  */
 #define XFS_DAC_LEAF_ADDNAME_INIT	0x01 /* xfs_attr_leaf_addname init*/
 #define XFS_DAC_DELAYED_OP_INIT		0x02 /* delayed operations init*/
@@ -399,32 +399,25 @@ enum xfs_delattr_state {
 /*
  * Context used for keeping track of delayed attribute operations
  */
-struct xfs_delattr_context {
-	struct xfs_da_args      *da_args;
+struct xfs_attr_item {
+	struct xfs_da_args		*xattri_da_args;
 
 	/*
 	 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
 	 */
-	struct xfs_buf		*leaf_bp;
+	struct xfs_buf			*xattri_leaf_bp;
 
 	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
-	struct xfs_bmbt_irec	map;
-	xfs_dablk_t		lblkno;
-	int			blkcnt;
+	struct xfs_bmbt_irec		xattri_map;
+	xfs_dablk_t			xattri_lblkno;
+	int				xattri_blkcnt;
 
 	/* Used in xfs_attr_node_removename to roll through removing blocks */
-	struct xfs_da_state     *da_state;
+	struct xfs_da_state		*xattri_da_state;
 
 	/* Used to keep track of current state of delayed operation */
-	unsigned int            flags;
-	enum xfs_delattr_state  dela_state;
-};
-
-/*
- * List of attrs to commit later.
- */
-struct xfs_attr_item {
-	struct xfs_delattr_context	xattri_dac;
+	unsigned int			xattri_flags;
+	enum xfs_delattr_state		xattri_dela_state;
 
 	/*
 	 * Indicates if the attr operation is a set or a remove
@@ -432,7 +425,10 @@ struct xfs_attr_item {
 	 */
 	uint32_t			xattri_op_flags;
 
-	/* used to log this item to an intent */
+	/*
+	 * used to log this item to an intent containing a list of attrs to
+	 * commit later
+	 */
 	struct list_head		xattri_list;
 };
 
@@ -451,12 +447,10 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
 int xfs_attr_get_ilocked(struct xfs_da_args *args);
 int xfs_attr_get(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
-int xfs_attr_set_iter(struct xfs_delattr_context *dac);
+int xfs_attr_set_iter(struct xfs_attr_item *attr);
 int xfs_has_attr(struct xfs_da_args *args);
-int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
+int xfs_attr_remove_iter(struct xfs_attr_item *attr);
 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);
 int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
 int xfs_attr_set_deferred(struct xfs_da_args *args);
 int xfs_attr_remove_deferred(struct xfs_da_args *args);
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 78bb552..ad06018 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -634,14 +634,14 @@ xfs_attr_rmtval_set(
  */
 int
 xfs_attr_rmtval_find_space(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
-	struct xfs_bmbt_irec		*map = &dac->map;
+	struct xfs_da_args		*args = attr->xattri_da_args;
+	struct xfs_bmbt_irec		*map = &attr->xattri_map;
 	int				error;
 
-	dac->lblkno = 0;
-	dac->blkcnt = 0;
+	attr->xattri_lblkno = 0;
+	attr->xattri_blkcnt = 0;
 	args->rmtblkcnt = 0;
 	args->rmtblkno = 0;
 	memset(map, 0, sizeof(struct xfs_bmbt_irec));
@@ -650,8 +650,8 @@ xfs_attr_rmtval_find_space(
 	if (error)
 		return error;
 
-	dac->blkcnt = args->rmtblkcnt;
-	dac->lblkno = args->rmtblkno;
+	attr->xattri_blkcnt = args->rmtblkcnt;
+	attr->xattri_lblkno = args->rmtblkno;
 
 	return 0;
 }
@@ -664,17 +664,17 @@ xfs_attr_rmtval_find_space(
  */
 int
 xfs_attr_rmtval_set_blk(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	struct xfs_inode		*dp = args->dp;
-	struct xfs_bmbt_irec		*map = &dac->map;
+	struct xfs_bmbt_irec		*map = &attr->xattri_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,
+	error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)attr->xattri_lblkno,
+				attr->xattri_blkcnt, XFS_BMAPI_ATTRFORK, args->total,
 				map, &nmap);
 	if (error)
 		return error;
@@ -684,8 +684,8 @@ xfs_attr_rmtval_set_blk(
 	       (map->br_startblock != HOLESTARTBLOCK));
 
 	/* roll attribute extent map forwards */
-	dac->lblkno += map->br_blockcount;
-	dac->blkcnt -= map->br_blockcount;
+	attr->xattri_lblkno += map->br_blockcount;
+	attr->xattri_blkcnt -= map->br_blockcount;
 
 	return 0;
 }
@@ -738,9 +738,9 @@ xfs_attr_rmtval_invalidate(
  */
 int
 xfs_attr_rmtval_remove(
-	struct xfs_delattr_context	*dac)
+	struct xfs_attr_item		*attr)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	int				error, done;
 
 	/*
@@ -762,7 +762,8 @@ xfs_attr_rmtval_remove(
 	 * by the parent
 	 */
 	if (!done) {
-		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
+		trace_xfs_attr_rmtval_remove_return(attr->xattri_dela_state,
+						    args->dp);
 		return -EAGAIN;
 	}
 
diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h
index 6ae91af..d3aa27d 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.h
+++ b/fs/xfs/libxfs/xfs_attr_remote.h
@@ -13,9 +13,9 @@ int xfs_attr_rmtval_set(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_rmtval_remove(struct xfs_attr_item *attr);
 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);
+int xfs_attr_rmtval_set_blk(struct xfs_attr_item *attr);
+int xfs_attr_rmtval_find_space(struct xfs_attr_item *attr);
 #endif /* __XFS_ATTR_REMOTE_H__ */
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 842f84d..b943c20 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -291,11 +291,11 @@ xfs_attrd_item_release(
  */
 int
 xfs_trans_attr(
-	struct xfs_delattr_context	*dac,
+	struct xfs_attr_item		*attr,
 	struct xfs_attrd_log_item	*attrdp,
 	uint32_t			op_flags)
 {
-	struct xfs_da_args		*args = dac->da_args;
+	struct xfs_da_args		*args = attr->xattri_da_args;
 	int				error;
 
 	error = xfs_qm_dqattach_locked(args->dp, 0);
@@ -310,11 +310,11 @@ xfs_trans_attr(
 	switch (op_flags) {
 	case XFS_ATTR_OP_FLAGS_SET:
 		args->op_flags |= XFS_DA_OP_ADDNAME;
-		error = xfs_attr_set_iter(dac);
+		error = xfs_attr_set_iter(attr);
 		break;
 	case XFS_ATTR_OP_FLAGS_REMOVE:
 		ASSERT(XFS_IFORK_Q(args->dp));
-		error = xfs_attr_remove_iter(dac);
+		error = xfs_attr_remove_iter(attr);
 		break;
 	default:
 		error = -EFSCORRUPTED;
@@ -358,16 +358,16 @@ xfs_attr_log_item(
 	 * structure with fields from this xfs_attr_item
 	 */
 	attrp = &attrip->attri_format;
-	attrp->alfi_ino = attr->xattri_dac.da_args->dp->i_ino;
+	attrp->alfi_ino = attr->xattri_da_args->dp->i_ino;
 	attrp->alfi_op_flags = attr->xattri_op_flags;
-	attrp->alfi_value_len = attr->xattri_dac.da_args->valuelen;
-	attrp->alfi_name_len = attr->xattri_dac.da_args->namelen;
-	attrp->alfi_attr_flags = attr->xattri_dac.da_args->attr_filter;
-
-	attrip->attri_name = (void *)attr->xattri_dac.da_args->name;
-	attrip->attri_value = attr->xattri_dac.da_args->value;
-	attrip->attri_name_len = attr->xattri_dac.da_args->namelen;
-	attrip->attri_value_len = attr->xattri_dac.da_args->valuelen;
+	attrp->alfi_value_len = attr->xattri_da_args->valuelen;
+	attrp->alfi_name_len = attr->xattri_da_args->namelen;
+	attrp->alfi_attr_flags = attr->xattri_da_args->attr_filter;
+
+	attrip->attri_name = (void *)attr->xattri_da_args->name;
+	attrip->attri_value = attr->xattri_da_args->value;
+	attrip->attri_name_len = attr->xattri_da_args->namelen;
+	attrip->attri_value_len = attr->xattri_da_args->valuelen;
 }
 
 /* Get an ATTRI. */
@@ -408,10 +408,8 @@ xfs_attr_finish_item(
 	struct xfs_attr_item		*attr;
 	struct xfs_attrd_log_item	*done_item = NULL;
 	int				error;
-	struct xfs_delattr_context	*dac;
 
 	attr = container_of(item, struct xfs_attr_item, xattri_list);
-	dac = &attr->xattri_dac;
 	if (done)
 		done_item = ATTRD_ITEM(done);
 
@@ -423,19 +421,18 @@ xfs_attr_finish_item(
 	 * in a standard delay op, so we need to catch this here and rejoin the
 	 * leaf to the new transaction
 	 */
-	if (attr->xattri_dac.leaf_bp &&
-	    attr->xattri_dac.leaf_bp->b_transp != tp) {
-		xfs_trans_bjoin(tp, attr->xattri_dac.leaf_bp);
-		xfs_trans_bhold(tp, attr->xattri_dac.leaf_bp);
+	if (attr->xattri_leaf_bp && attr->xattri_leaf_bp->b_transp != tp) {
+		xfs_trans_bjoin(tp, attr->xattri_leaf_bp);
+		xfs_trans_bhold(tp, attr->xattri_leaf_bp);
 	}
 
 	/*
 	 * Always reset trans after EAGAIN cycle
 	 * since the transaction is new
 	 */
-	dac->da_args->trans = tp;
+	attr->xattri_da_args->trans = tp;
 
-	error = xfs_trans_attr(dac, done_item, attr->xattri_op_flags);
+	error = xfs_trans_attr(attr, done_item, attr->xattri_op_flags);
 	if (error != -EAGAIN)
 		kmem_free(attr);
 
@@ -570,7 +567,7 @@ xfs_attri_item_recover(
 	struct xfs_attrd_log_item	*done_item = NULL;
 	struct xfs_attr_item		attr = {
 		.xattri_op_flags	= attrip->attri_format.alfi_op_flags,
-		.xattri_dac.da_args	= &args,
+		.xattri_da_args		= &args,
 	};
 
 	/*
@@ -630,8 +627,7 @@ xfs_attri_item_recover(
 	xfs_ilock(ip, XFS_ILOCK_EXCL);
 	xfs_trans_ijoin(args.trans, ip, 0);
 
-	error = xfs_trans_attr(&attr.xattri_dac, done_item,
-			       attrp->alfi_op_flags);
+	error = xfs_trans_attr(&attr, done_item, attrp->alfi_op_flags);
 	if (error == -EAGAIN) {
 		/*
 		 * There's more work to do, so make a new xfs_attr_item and add
@@ -648,7 +644,7 @@ xfs_attri_item_recover(
 		memcpy(new_args, &args, sizeof(struct xfs_da_args));
 		memcpy(new_attr, &attr, sizeof(struct xfs_attr_item));
 
-		new_attr->xattri_dac.da_args = new_args;
+		new_attr->xattri_da_args = new_args;
 		memset(&new_attr->xattri_list, 0, sizeof(struct list_head));
 
 		xfs_defer_add(args.trans, XFS_DEFER_OPS_TYPE_ATTR,
-- 
2.7.4


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

* Re: [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step
  2021-02-18 16:53 ` [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step Allison Henderson
@ 2021-02-24 15:03   ` Brian Foster
  2021-02-25  6:17     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:03 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:27AM -0700, Allison Henderson wrote:
> From: Allison Collins <allison.henderson@oracle.com>
> 
> This patch adds a new helper function xfs_attr_node_remove_step.  This
> will help simplify and modularize the calling function
> xfs_attr_node_removename.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 46 ++++++++++++++++++++++++++++++++++------------
>  1 file changed, 34 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 472b303..28ff93d 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -1267,18 +1262,45 @@ xfs_attr_node_removename(
>  	if (retval && (state->path.active > 1)) {
>  		error = xfs_da3_join(state);
>  		if (error)
> -			goto out;
> +			return error;
>  		error = xfs_defer_finish(&args->trans);
>  		if (error)
> -			goto out;
> +			return error;
>  		/*
>  		 * Commit the Btree join operation and start a new trans.
>  		 */
>  		error = xfs_trans_roll_inode(&args->trans, dp);
>  		if (error)
> -			goto out;
> +			return error;
>  	}
>  
> +	return error;

Maybe just return 0 here since it looks like error might not have been
assigned..? With that fixed:

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

> +}
> +
> +/*
> + * Remove a name from a B-tree attribute list.
> + *
> + * This routine will find the blocks of the name to remove, remove them and
> + * shrink the tree if needed.
> + */
> +STATIC int
> +xfs_attr_node_removename(
> +	struct xfs_da_args	*args)
> +{
> +	struct xfs_da_state	*state = NULL;
> +	int			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;
> +
> +	error = xfs_attr_node_remove_step(args, state);
> +	if (error)
> +		goto out;
> +
>  	/*
>  	 * If the result is small enough, push it all into the inode.
>  	 */
> -- 
> 2.7.4
> 


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

* Re: [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup
  2021-02-18 16:53 ` [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
@ 2021-02-24 15:03   ` Brian Foster
  2021-02-25  6:17     ` Allison Henderson
  2021-02-26  3:00   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:03 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:28AM -0700, Allison Henderson wrote:
> 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>
> ---

Looks like I sent a review for this on v14...

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 28ff93d..4e6c89d 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1220,6 +1220,25 @@ xfs_attr_node_remove_rmt(
>  	return xfs_attr_refillstate(state);
>  }
>  
> +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.
>   *
> @@ -1232,7 +1251,6 @@ xfs_attr_node_remove_step(
>  	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;
>  
> @@ -1247,14 +1265,7 @@ xfs_attr_node_remove_step(
>  		if (error)
>  			return error;
>  	}
> -
> -	/*
> -	 * 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step
  2021-02-18 16:53 ` [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step Allison Henderson
@ 2021-02-24 15:04   ` Brian Foster
  2021-02-25  6:18     ` Allison Henderson
  2021-02-26  3:02   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:04 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:29AM -0700, Allison Henderson wrote:
> This patch hoists transaction handling in xfs_attr_node_removename to
> xfs_attr_node_remove_step.  This will help keep transaction handling in
> higher level functions instead of buried in subfunctions when we
> introduce delay attributes
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---

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

>  fs/xfs/libxfs/xfs_attr.c | 45 ++++++++++++++++++++++-----------------------
>  1 file changed, 22 insertions(+), 23 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4e6c89d..3cf76e2 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1251,9 +1251,7 @@ xfs_attr_node_remove_step(
>  	struct xfs_da_args	*args,
>  	struct xfs_da_state	*state)
>  {
> -	int			retval, error;
> -	struct xfs_inode	*dp = args->dp;
> -
> +	int			error = 0;
>  
>  	/*
>  	 * If there is an out-of-line value, de-allocate the blocks.
> @@ -1265,25 +1263,6 @@ xfs_attr_node_remove_step(
>  		if (error)
>  			return error;
>  	}
> -	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)
> -			return error;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> -		/*
> -		 * Commit the Btree join operation and start a new trans.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> -			return error;
> -	}
>  
>  	return error;
>  }
> @@ -1299,7 +1278,7 @@ xfs_attr_node_removename(
>  	struct xfs_da_args	*args)
>  {
>  	struct xfs_da_state	*state = NULL;
> -	int			error;
> +	int			retval, error;
>  	struct xfs_inode	*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
> @@ -1312,6 +1291,26 @@ xfs_attr_node_removename(
>  	if (error)
>  		goto out;
>  
> +	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.
> +		 */
> +		error = xfs_trans_roll_inode(&args->trans, dp);
> +		if (error)
> +			goto out;
> +	}
> +
>  	/*
>  	 * If the result is small enough, push it all into the inode.
>  	 */
> -- 
> 2.7.4
> 


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

* Re: [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform
  2021-02-18 16:53 ` [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform Allison Henderson
@ 2021-02-24 15:04   ` Brian Foster
  2021-02-25  6:18     ` Allison Henderson
  2021-02-26  3:03   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:04 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:30AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---

LGTM:

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

>  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 3cf76e2..a064c5b 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt
  2021-02-18 16:53 ` [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt Allison Henderson
@ 2021-02-24 15:04   ` Brian Foster
  2021-02-25  6:18     ` Allison Henderson
  2021-02-26  3:07   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:04 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:31AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---

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

>  fs/xfs/libxfs/xfs_attr.c | 77 +++++++++++++++++++++++++++---------------------
>  1 file changed, 44 insertions(+), 33 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index a064c5b..205ad26 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -216,6 +216,46 @@ 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 -EAGAIN;
> +}
> +
>  /*
>   * Set the attribute specified in @args.
>   */
> @@ -224,8 +264,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 +273,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)
> +		error = xfs_attr_set_fmt(args);
> +		if (error != -EAGAIN)
>  			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;
> -		}
>  	}
>  
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> @@ -297,8 +309,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-02-18 16:53 ` [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work Allison Henderson
@ 2021-02-24 15:04   ` Brian Foster
  2021-02-25  6:18     ` Allison Henderson
  2021-02-26  4:02   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:04 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:32AM -0700, 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 205ad26..bee8d3fb 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_work(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);
> @@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
>  			return error;
>  	}
>  
> +	error = xfs_attr_node_addname_work(args);
> +out:
> +	if (state)
> +		xfs_da_state_free(state);
> +	if (error)
> +		return error;
> +	return retval;
> +}
> +
> +
> +STATIC
> +int xfs_attr_node_addname_work(
> +	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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-02-18 16:53 ` [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
@ 2021-02-24 15:04   ` Brian Foster
  2021-02-25  6:18     ` Allison Henderson
  2021-02-26  4:06   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 15:04 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:33AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 80 ++++++++++++++++++++++++++++++------------------
>  1 file changed, 51 insertions(+), 29 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index bee8d3fb..4333b61 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -941,6 +931,38 @@ xfs_attr_node_addname(
>  		args->rmtvaluelen = 0;
>  	}
>  
> +	return 0;
> +out:

Nit: can we call this label 'error' since it appears to be used when we
want to return the current retval as an operational 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);

This moves the tracepoint into the looping sequence whereas previously
it would only execute once. I don't see a clean way to fix that with the
breakdown as of this patch, and it's not a huge deal, but it would be
nice to fix that before the end of the series if we haven't already.
Otherwise LGTM:

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

> +
> +	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) {
> @@ -966,7 +988,7 @@ xfs_attr_node_addname(
>  			if (error)
>  				goto out;
>  
> -			goto restart;
> +			return -EAGAIN;
>  		}
>  
>  		/*
> -- 
> 2.7.4
> 


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

* Re: [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname
  2021-02-18 16:53 ` [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
@ 2021-02-24 18:42   ` Brian Foster
  2021-02-25  6:19     ` Allison Henderson
  2021-03-01 18:19   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 18:42 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:35AM -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 asetheic right away, but it is a
> preliminary step to to manageing the state machine code.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  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 19a532a..bfd4466 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -286,10 +287,101 @@ xfs_attr_set_args(
...
>  			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:

Hmm, I'm not a fan of this unconditional return followed by a jump label
in the middle of the function. It's a pretty clear indication that this
is just two functions smashed together, so I'm not sure what the
advantage of this is. I'll continue on to see what falls out of the next
patches..

Brian

>  		/*
>  		 * Promote the attribute list to the Btree format.
>  		 */
> @@ -731,115 +823,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname
  2021-02-18 16:53 ` [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname Allison Henderson
@ 2021-02-24 18:42   ` Brian Foster
  2021-02-25  6:19     ` Allison Henderson
  2021-03-01 18:05   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 18:42 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:34AM -0700, 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>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 165 ++++++++++++++++++++++++-----------------------
>  1 file changed, 83 insertions(+), 82 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4333b61..19a532a 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -320,8 +322,82 @@ 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);
> +		retval = error;

It looks like this is the only use of retval. Otherwise this function is
getting a bit big, but the factoring LGTM:

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

> +		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_work(args);
> +out:
> +	if (state)
> +		xfs_da_state_free(state);
> +	if (error)
> +		return error;
> +	return retval;
>  
> -	return error;
>  }
>  
>  /*
> @@ -955,7 +1031,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);
>  
> @@ -963,8 +1039,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
> @@ -1010,85 +1086,10 @@ 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_work(args);
>  out:
>  	if (state)
>  		xfs_da_state_free(state);
> -	if (error)
> -		return error;
> -	return retval;
> +	return error;
>  }
>  
>  
> -- 
> 2.7.4
> 


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

* Re: [PATCH v15 10/22] xfs: Hoist node transaction handling
  2021-02-18 16:53 ` [PATCH v15 10/22] xfs: Hoist node transaction handling Allison Henderson
@ 2021-02-24 18:43   ` Brian Foster
  2021-02-25  6:20     ` Allison Henderson
  2021-03-01 18:20   ` Darrick J. Wong
  1 sibling, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 18:43 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:36AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 53 +++++++++++++++++++++++++-----------------------
>  1 file changed, 28 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index bfd4466..56d4b56 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -288,8 +288,34 @@ 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;
		}

(I think we usually try to add braces around all branches of an if/else
if at least one branch requires them.)

Otherwise, the factoring looks Ok to me and this does improve on the
wart from the previous patch:

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

>  			return error;
>  
> @@ -381,32 +407,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-18 16:53 ` [PATCH v15 11/22] xfs: Add delay ready attr remove routines Allison Henderson
@ 2021-02-24 18:45   ` Brian Foster
  2021-02-25  7:01     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-24 18:45 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:37AM -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 has become xfs_attr_remove_iter, which
> uses a sort of state machine like switch to keep track of where it was
> when EAGAIN was returned. xfs_attr_node_removename has also been
> modified to use the switch, and a new version of xfs_attr_remove_args
> consists of a simple loop to refresh the transaction until the operation
> is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
>  fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
>  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, 294 insertions(+), 83 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 56d4b56..d46b92a 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
...
> @@ -221,6 +221,34 @@ 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);
> +		if (error)
> +			return error;

No need for the error check here.

> +	} else
> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
> +
> +	return error;
> +}
> +
>  STATIC int
>  xfs_attr_set_fmt(
>  	struct xfs_da_args	*args)
> @@ -531,23 +559,58 @@ 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,
> +	};
> +
> +	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;
> +}
> +
> +/*
> + * Remove the attribute specified in @args.
> + *
> + * This function may return -EAGAIN to signal that the transaction needs to be
> + * rolled.  Callers should continue calling this function until they receive a
> + * return value other than -EAGAIN.
> + */
> +int
> +xfs_attr_remove_iter(
> +	struct xfs_delattr_context	*dac)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_inode		*dp = args->dp;
>  
> -	if (!xfs_inode_hasattr(dp)) {
> -		error = -ENOATTR;
> -	} else if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
> +	/* If we are shrinking a node, resume shrink */
> +	if (dac->dela_state == XFS_DAS_RM_SHRINK)
> +		goto node;
> +
> +	if (!xfs_inode_hasattr(dp))
> +		return -ENOATTR;
> +
> +	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);
> +		return xfs_attr_shortform_remove(args);
>  	}
>  
> -	return error;
> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
> +		return xfs_attr_leaf_removename(args);
> +node:
> +	/* If we are not short form or leaf, then proceed to remove node */
> +	return  xfs_attr_node_removename_iter(dac);

Extra whitespace before the function name;

Also, can we lift xfs_attr_node_removename_iter() into this function,
form the current implementation of xfs_attr_remove_iter() into the
UNINIT state, and move the current UNINIT state into a new RMT_BLK state
to support reentry? ISTM that would condense everything to a single
switch statement that can live inside xfs_attr_remove_iter(). IOW, we
can kill off the 'node:' level and multi-layer state management here.
Hm?

>  }
>  
>  /*
...
> @@ -1207,22 +1272,28 @@ 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;
>  }
>  
>  STATIC int
> -xfs_attr_node_remove_rmt(
> -	struct xfs_da_args	*args,
> -	struct xfs_da_state	*state)
> +xfs_attr_node_remove_rmt (

Extra whitespace before the opening brace.

> +	struct xfs_delattr_context	*dac,
> +	struct xfs_da_state		*state)
>  {
> -	int			error = 0;
> +	int				error = 0;
>  
> -	error = xfs_attr_rmtval_remove(args);
> +	/*
> +	 * May return -EAGAIN to request that the caller recall this function
> +	 */
> +	error = __xfs_attr_rmtval_remove(dac);
>  	if (error)
>  		return error;
>  
...
> @@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
>   *
>   * This routine will find the blocks of the name to remove, remove them and
>   * shrink the tree if needed.
> + *
> + * This routine is meant to function as either an inline or delayed operation,
> + * and may return -EAGAIN when the transaction needs to be rolled.  Calling
> + * functions will need to handle this, and recall the function until a
> + * successful error code is returned.
>   */
>  STATIC int
> -xfs_attr_node_removename(
> -	struct xfs_da_args	*args)
> +xfs_attr_node_removename_iter(
> +	struct xfs_delattr_context	*dac)
>  {
> -	struct xfs_da_state	*state = NULL;
> -	int			retval, error;
> -	struct xfs_inode	*dp = args->dp;
> +	struct xfs_da_args		*args = dac->da_args;
> +	struct xfs_da_state		*state = NULL;
> +	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;
> -
> -	error = xfs_attr_node_remove_step(args, state);
> -	if (error)
> -		goto out;
> -
> -	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 (!dac->da_state) {
> +		error = xfs_attr_node_removename_setup(dac);
>  		if (error)
>  			goto out;
> +	}
> +	state = dac->da_state;
> +
> +	switch (dac->dela_state) {
> +	case XFS_DAS_UNINIT:
>  		/*
> -		 * Commit the Btree join operation and start a new trans.
> +		 * repeatedly remove remote blocks, remove the entry and join.
> +		 * returns -EAGAIN or 0 for completion of the step.
>  		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> +		error = xfs_attr_node_remove_step(dac);
>  		if (error)
> -			goto out;
> -	}
> +			break;

Hmm.. so re: my comment further down on xfs_attr_rmtval_remove(),
wouldn't that change semantics here? I.e., once remote blocks are
removed this would previously carry on with a clean transaction. Now it
looks like we'd carry on with the dirty transaction that removed the
last remote extent. This suggests that perhaps we should return once
more and fall into a new state to remove the name..? Also, it would be
nice to remove the several seemingly unnecessary layers of indirection
here. For example, something like the following (also considering my
comment above wrt to xfs_attr_remove_iter() and UNINIT):

	case UNINIT:
		...
		/* fallthrough */
	case RMTBLK:
		if (args->rmtblkno > 0) {
			dac->dela_state = RMTBLK;
			error = __xfs_attr_rmtval_remove(dac);
			if (error)
				break;

			ASSERT(args->rmtblkno == 0);
			xfs_attr_refillstate(state);
			dac->flags |= XFS_DAC_DEFER_FINISH;
			dac->dela_state = RMNAME;
			return -EAGAIN;
		}
		/* fallthrough */
	case RMNAME:
		...
	...

>  
> -	/*
> -	 * 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);
> +		retval = xfs_attr_node_remove_cleanup(args, state);
>  
...
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index 48d8e9c..f09820c 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
...
> @@ -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;

Previously this would roll once and exit the loop on retval == 0. Now it
looks like we break out of the loop immediately. Why the change?

Brian

>  
> -		/*
> -		 * 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 dont 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 reminicent 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] 87+ messages in thread

* Re: [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step
  2021-02-24 15:03   ` Brian Foster
@ 2021-02-25  6:17     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:17 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:03 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:27AM -0700, Allison Henderson wrote:
>> From: Allison Collins <allison.henderson@oracle.com>
>>
>> This patch adds a new helper function xfs_attr_node_remove_step.  This
>> will help simplify and modularize the calling function
>> xfs_attr_node_removename.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
>> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 46 ++++++++++++++++++++++++++++++++++------------
>>   1 file changed, 34 insertions(+), 12 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 472b303..28ff93d 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -1267,18 +1262,45 @@ xfs_attr_node_removename(
>>   	if (retval && (state->path.active > 1)) {
>>   		error = xfs_da3_join(state);
>>   		if (error)
>> -			goto out;
>> +			return error;
>>   		error = xfs_defer_finish(&args->trans);
>>   		if (error)
>> -			goto out;
>> +			return error;
>>   		/*
>>   		 * Commit the Btree join operation and start a new trans.
>>   		 */
>>   		error = xfs_trans_roll_inode(&args->trans, dp);
>>   		if (error)
>> -			goto out;
>> +			return error;
>>   	}
>>   
>> +	return error;
> 
> Maybe just return 0 here since it looks like error might not have been
> assigned..? With that fixed:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Sure, will do.  Thanks!

Allison

> 
>> +}
>> +
>> +/*
>> + * Remove a name from a B-tree attribute list.
>> + *
>> + * This routine will find the blocks of the name to remove, remove them and
>> + * shrink the tree if needed.
>> + */
>> +STATIC int
>> +xfs_attr_node_removename(
>> +	struct xfs_da_args	*args)
>> +{
>> +	struct xfs_da_state	*state = NULL;
>> +	int			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;
>> +
>> +	error = xfs_attr_node_remove_step(args, state);
>> +	if (error)
>> +		goto out;
>> +
>>   	/*
>>   	 * If the result is small enough, push it all into the inode.
>>   	 */
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup
  2021-02-24 15:03   ` Brian Foster
@ 2021-02-25  6:17     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:17 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:03 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:28AM -0700, Allison Henderson wrote:
>> 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>
>> ---
> 
> Looks like I sent a review for this on v14...
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Sorry about that, I got tied up in the extra refactoring.  Will add. 
Thanks again!

Allison

> 
>>   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 28ff93d..4e6c89d 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1220,6 +1220,25 @@ xfs_attr_node_remove_rmt(
>>   	return xfs_attr_refillstate(state);
>>   }
>>   
>> +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.
>>    *
>> @@ -1232,7 +1251,6 @@ xfs_attr_node_remove_step(
>>   	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;
>>   
>> @@ -1247,14 +1265,7 @@ xfs_attr_node_remove_step(
>>   		if (error)
>>   			return error;
>>   	}
>> -
>> -	/*
>> -	 * 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-25  6:18     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:18 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:04 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:29AM -0700, Allison Henderson wrote:
>> This patch hoists transaction handling in xfs_attr_node_removename to
>> xfs_attr_node_remove_step.  This will help keep transaction handling in
>> higher level functions instead of buried in subfunctions when we
>> introduce delay attributes
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Great, thank you!
Allison
> 
>>   fs/xfs/libxfs/xfs_attr.c | 45 ++++++++++++++++++++++-----------------------
>>   1 file changed, 22 insertions(+), 23 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4e6c89d..3cf76e2 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1251,9 +1251,7 @@ xfs_attr_node_remove_step(
>>   	struct xfs_da_args	*args,
>>   	struct xfs_da_state	*state)
>>   {
>> -	int			retval, error;
>> -	struct xfs_inode	*dp = args->dp;
>> -
>> +	int			error = 0;
>>   
>>   	/*
>>   	 * If there is an out-of-line value, de-allocate the blocks.
>> @@ -1265,25 +1263,6 @@ xfs_attr_node_remove_step(
>>   		if (error)
>>   			return error;
>>   	}
>> -	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)
>> -			return error;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> -		/*
>> -		 * Commit the Btree join operation and start a new trans.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -		if (error)
>> -			return error;
>> -	}
>>   
>>   	return error;
>>   }
>> @@ -1299,7 +1278,7 @@ xfs_attr_node_removename(
>>   	struct xfs_da_args	*args)
>>   {
>>   	struct xfs_da_state	*state = NULL;
>> -	int			error;
>> +	int			retval, error;
>>   	struct xfs_inode	*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>> @@ -1312,6 +1291,26 @@ xfs_attr_node_removename(
>>   	if (error)
>>   		goto out;
>>   
>> +	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.
>> +		 */
>> +		error = xfs_trans_roll_inode(&args->trans, dp);
>> +		if (error)
>> +			goto out;
>> +	}
>> +
>>   	/*
>>   	 * If the result is small enough, push it all into the inode.
>>   	 */
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-25  6:18     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:18 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:04 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:30AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
> 
> LGTM:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Thanks!
Allison
> 
>>   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 3cf76e2..a064c5b 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-25  6:18     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:18 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:04 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:31AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Will add.  Thank you!!

Allison
> 
>>   fs/xfs/libxfs/xfs_attr.c | 77 +++++++++++++++++++++++++++---------------------
>>   1 file changed, 44 insertions(+), 33 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index a064c5b..205ad26 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -216,6 +216,46 @@ 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 -EAGAIN;
>> +}
>> +
>>   /*
>>    * Set the attribute specified in @args.
>>    */
>> @@ -224,8 +264,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 +273,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)
>> +		error = xfs_attr_set_fmt(args);
>> +		if (error != -EAGAIN)
>>   			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;
>> -		}
>>   	}
>>   
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> @@ -297,8 +309,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-25  6:18     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:18 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:04 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:32AM -0700, 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>
Alrighty, thanks!
Allison

> 
>>   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 205ad26..bee8d3fb 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_work(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);
>> @@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
>>   			return error;
>>   	}
>>   
>> +	error = xfs_attr_node_addname_work(args);
>> +out:
>> +	if (state)
>> +		xfs_da_state_free(state);
>> +	if (error)
>> +		return error;
>> +	return retval;
>> +}
>> +
>> +
>> +STATIC
>> +int xfs_attr_node_addname_work(
>> +	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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-25  6:18     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:18 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 8:04 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:33AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 80 ++++++++++++++++++++++++++++++------------------
>>   1 file changed, 51 insertions(+), 29 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index bee8d3fb..4333b61 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -941,6 +931,38 @@ xfs_attr_node_addname(
>>   		args->rmtvaluelen = 0;
>>   	}
>>   
>> +	return 0;
>> +out:
> 
> Nit: can we call this label 'error' since it appears to be used when we
> want to return the current retval as an operational error?
Sure, will change out

> 
>> +	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);
> 
> This moves the tracepoint into the looping sequence whereas previously
> it would only execute once. I don't see a clean way to fix that with the
> breakdown as of this patch, and it's not a huge deal, but it would be
> nice to fix that before the end of the series if we haven't already.
> Otherwise LGTM:
I see, I could hoist it out, but it is short lived really.  Once we get 
into the state machine it wont much matter if it's hoisted or not.  In 
the greater scheme of things, I think it makes more sense for it to stay 
where it is.  A lot of these patches are not particularly elegant during 
this refactoring phase, and then things sort of come together once the 
state machine gets here.

> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Thank you!
Allison

> 
>> +
>> +	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) {
>> @@ -966,7 +988,7 @@ xfs_attr_node_addname(
>>   			if (error)
>>   				goto out;
>>   
>> -			goto restart;
>> +			return -EAGAIN;
>>   		}
>>   
>>   		/*
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname
  2021-02-24 18:42   ` Brian Foster
@ 2021-02-25  6:19     ` Allison Henderson
  2021-02-25 14:20       ` Brian Foster
  0 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:19 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 11:42 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:35AM -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 asetheic right away, but it is a
>> preliminary step to to manageing the state machine code.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   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 19a532a..bfd4466 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -286,10 +287,101 @@ xfs_attr_set_args(
> ...
>>   			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:
> 
> Hmm, I'm not a fan of this unconditional return followed by a jump label
> in the middle of the function. It's a pretty clear indication that this
> is just two functions smashed together, so I'm not sure what the
> advantage of this is. I'll continue on to see what falls out of the next
> patches..
> 
> Brian

Yes, it does kinda look a little displaced, but the point of it is to 
bring code that will require state management into the same scope so 
that the state switch can span all the operations it affects.  Which 
seemed to be what the RFC was striving for?  Looking ahead at the other 
reviews, I think it came together for you?

Allison

> 
>>   		/*
>>   		 * Promote the attribute list to the Btree format.
>>   		 */
>> @@ -731,115 +823,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname
  2021-02-24 18:42   ` Brian Foster
@ 2021-02-25  6:19     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:19 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 11:42 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:34AM -0700, 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>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 165 ++++++++++++++++++++++++-----------------------
>>   1 file changed, 83 insertions(+), 82 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4333b61..19a532a 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -320,8 +322,82 @@ 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);
>> +		retval = error;
> 
> It looks like this is the only use of retval. Otherwise this function is
> getting a bit big, but the factoring LGTM:
Ok, will clean out.
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Thank you!
Allison
> 
>> +		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_work(args);
>> +out:
>> +	if (state)
>> +		xfs_da_state_free(state);
>> +	if (error)
>> +		return error;
>> +	return retval;
>>   
>> -	return error;
>>   }
>>   
>>   /*
>> @@ -955,7 +1031,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);
>>   
>> @@ -963,8 +1039,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
>> @@ -1010,85 +1086,10 @@ 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_work(args);
>>   out:
>>   	if (state)
>>   		xfs_da_state_free(state);
>> -	if (error)
>> -		return error;
>> -	return retval;
>> +	return error;
>>   }
>>   
>>   
>> -- 
>> 2.7.4
>>
> 

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

* Re: [PATCH v15 10/22] xfs: Hoist node transaction handling
  2021-02-24 18:43   ` Brian Foster
@ 2021-02-25  6:20     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  6:20 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 11:43 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:36AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 53 +++++++++++++++++++++++++-----------------------
>>   1 file changed, 28 insertions(+), 25 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index bfd4466..56d4b56 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -288,8 +288,34 @@ 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;
> 		}
> 
> (I think we usually try to add braces around all branches of an if/else
> if at least one branch requires them.)
Ok, will fix

> 
> Otherwise, the factoring looks Ok to me and this does improve on the
> wart from the previous patch:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
Great, thanks!
Allison

> 
>>   			return error;
>>   
>> @@ -381,32 +407,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-24 18:45   ` Brian Foster
@ 2021-02-25  7:01     ` Allison Henderson
  2021-02-25 14:22       ` Brian Foster
  0 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-25  7:01 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/24/21 11:45 AM, Brian Foster wrote:
> On Thu, Feb 18, 2021 at 09:53:37AM -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 has become xfs_attr_remove_iter, which
>> uses a sort of state machine like switch to keep track of where it was
>> when EAGAIN was returned. xfs_attr_node_removename has also been
>> modified to use the switch, and a new version of xfs_attr_remove_args
>> consists of a simple loop to refresh the transaction until the operation
>> is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
>>   fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
>>   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, 294 insertions(+), 83 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 56d4b56..d46b92a 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>> @@ -221,6 +221,34 @@ 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);
>> +		if (error)
>> +			return error;
> 
> No need for the error check here.
Sure, will clean up

> 
>> +	} else
>> +		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> +
>> +	return error;
>> +}
>> +
>>   STATIC int
>>   xfs_attr_set_fmt(
>>   	struct xfs_da_args	*args)
>> @@ -531,23 +559,58 @@ 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,
>> +	};
>> +
>> +	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;
>> +}
>> +
>> +/*
>> + * Remove the attribute specified in @args.
>> + *
>> + * This function may return -EAGAIN to signal that the transaction needs to be
>> + * rolled.  Callers should continue calling this function until they receive a
>> + * return value other than -EAGAIN.
>> + */
>> +int
>> +xfs_attr_remove_iter(
>> +	struct xfs_delattr_context	*dac)
>> +{
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_inode		*dp = args->dp;
>>   
>> -	if (!xfs_inode_hasattr(dp)) {
>> -		error = -ENOATTR;
>> -	} else if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) {
>> +	/* If we are shrinking a node, resume shrink */
>> +	if (dac->dela_state == XFS_DAS_RM_SHRINK)
>> +		goto node;
>> +
>> +	if (!xfs_inode_hasattr(dp))
>> +		return -ENOATTR;
>> +
>> +	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);
>> +		return xfs_attr_shortform_remove(args);
>>   	}
>>   
>> -	return error;
>> +	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
>> +		return xfs_attr_leaf_removename(args);
>> +node:
>> +	/* If we are not short form or leaf, then proceed to remove node */
>> +	return  xfs_attr_node_removename_iter(dac);
> 
> Extra whitespace before the function name;
Ok, will fix

> 
> Also, can we lift xfs_attr_node_removename_iter() into this function,
> form the current implementation of xfs_attr_remove_iter() into the
> UNINIT state, and move the current UNINIT state into a new RMT_BLK state
> to support reentry? ISTM that would condense everything to a single
> switch statement that can live inside xfs_attr_remove_iter(). IOW, we
> can kill off the 'node:' level and multi-layer state management here.
> Hm?
Sure, I think the lift should be fine, though I dont think we'll need 
the extra state.  See below....

> 
>>   }
>>   
>>   /*
> ...
>> @@ -1207,22 +1272,28 @@ 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;
>>   }
>>   
>>   STATIC int
>> -xfs_attr_node_remove_rmt(
>> -	struct xfs_da_args	*args,
>> -	struct xfs_da_state	*state)
>> +xfs_attr_node_remove_rmt (
> 
> Extra whitespace before the opening brace.
ok, will fix

> 
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_da_state		*state)
>>   {
>> -	int			error = 0;
>> +	int				error = 0;
>>   
>> -	error = xfs_attr_rmtval_remove(args);
>> +	/*
>> +	 * May return -EAGAIN to request that the caller recall this function
>> +	 */
>> +	error = __xfs_attr_rmtval_remove(dac);
>>   	if (error)
>>   		return error;
>>   
> ...
>> @@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
>>    *
>>    * This routine will find the blocks of the name to remove, remove them and
>>    * shrink the tree if needed.
>> + *
>> + * This routine is meant to function as either an inline or delayed operation,
>> + * and may return -EAGAIN when the transaction needs to be rolled.  Calling
>> + * functions will need to handle this, and recall the function until a
>> + * successful error code is returned.
>>    */
>>   STATIC int
>> -xfs_attr_node_removename(
>> -	struct xfs_da_args	*args)
>> +xfs_attr_node_removename_iter(
>> +	struct xfs_delattr_context	*dac)
>>   {
>> -	struct xfs_da_state	*state = NULL;
>> -	int			retval, error;
>> -	struct xfs_inode	*dp = args->dp;
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	struct xfs_da_state		*state = NULL;
>> +	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;
>> -
>> -	error = xfs_attr_node_remove_step(args, state);
>> -	if (error)
>> -		goto out;
>> -
>> -	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 (!dac->da_state) {
>> +		error = xfs_attr_node_removename_setup(dac);
>>   		if (error)
>>   			goto out;
>> +	}
>> +	state = dac->da_state;
>> +
>> +	switch (dac->dela_state) {
>> +	case XFS_DAS_UNINIT:
>>   		/*
>> -		 * Commit the Btree join operation and start a new trans.
>> +		 * repeatedly remove remote blocks, remove the entry and join.
>> +		 * returns -EAGAIN or 0 for completion of the step.
>>   		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> +		error = xfs_attr_node_remove_step(dac);
>>   		if (error)
>> -			goto out;
>> -	}
>> +			break;
> 
> Hmm.. so re: my comment further down on xfs_attr_rmtval_remove(),
> wouldn't that change semantics here? I.e., once remote blocks are
> removed this would previously carry on with a clean transaction. Now it
> looks like we'd carry on with the dirty transaction that removed the
> last remote extent. This suggests that perhaps we should return once
> more and fall into a new state to remove the name..? 
I suspect the diff might be making this a bit difficult to see.  The 
roll that you see being removed here belongs to the transaction we 
hoisted up  in patch 3 which happens after the clean up below, and we 
have the corresponding EAGAIN fot that one.  I think the diff gets 
things a little interlaced here because the switch adds another level of 
indentation.

some times i do like to I use a graphical diffviewer like diffuse when 
patches get weird like this.  Something like this:

git config --global diff.tool  diffuse
git difftool 3c53e49 e201c09

You'd need to download the branch and also the diffuse tool, but 
sometimes i think it makes some of these diffs a bit easier to see

Also, it would be
> nice to remove the several seemingly unnecessary layers of indirection
> here. For example, something like the following (also considering my
> comment above wrt to xfs_attr_remove_iter() and UNINIT):
> 
> 	case UNINIT:
> 		...
> 		/* fallthrough */
> 	case RMTBLK:
> 		if (args->rmtblkno > 0) {
> 			dac->dela_state = RMTBLK;
> 			error = __xfs_attr_rmtval_remove(dac);
> 			if (error)
> 				break;
> 
> 			ASSERT(args->rmtblkno == 0);
> 			xfs_attr_refillstate(state);
> 			dac->flags |= XFS_DAC_DEFER_FINISH;
> 			dac->dela_state = RMNAME;
> 			return -EAGAIN;
> 		}
Ok, this looks to me like we've hoisted both xfs_attr_node_remove_rmt 
and xfs_attr_node_remove_step into this scope, but I still think this 
adds an extra roll where non previously was.  With out that extra 
EAGAIN, I think we are fine to have all that just under the UNINIT case. 
  I also think it's also worth noteing here that this is kind of a 
reverse of patch 1, which I think we put in for reasons of trying to 
modularize the higher level functions as much as possible.

I suspect some of where you were going with this may have been 
influenced by the earlier diff confusion too.  Maybe take a second look 
there before we go too much down this change....


> 		/* fallthrough */
> 	case RMNAME:
> 		...
> 	...
> 
>>   
>> -	/*
>> -	 * 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);
>> +		retval = xfs_attr_node_remove_cleanup(args, state);
>>   
> ...
I think the overlooked EAGAIN was in this area that got clipped out.....

>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index 48d8e9c..f09820c 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> ...
>> @@ -685,31 +687,29 @@ c(
>>   	 * 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;
> 
> Previously this would roll once and exit the loop on retval == 0. Now it
> looks like we break out of the loop immediately. Why the change?

Gosh, I think sometime in reviewing v9, we had come up with a 
"xfs_attr_roll_again" helper that took the error code as a paramater and 
decided whether or not to roll.  And then in v10 i think people thought 
that was weird and we turned it into xfs_attr_trans_roll.  I think I 
likley forgot to restore the orginal retval handling here.  This whole 
function disappears in the next patch, but the original error handling 
should be restored to keep things consistent. Thx for the catch!


Thx for the reviews!!  I know it's complicated!  I've chased my tail 
many times with it myself :-)

Allison




> 
> Brian
> 
>>   
>> -		/*
>> -		 * 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 dont 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 reminicent 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] 87+ messages in thread

* Re: [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname
  2021-02-25  6:19     ` Allison Henderson
@ 2021-02-25 14:20       ` Brian Foster
  0 siblings, 0 replies; 87+ messages in thread
From: Brian Foster @ 2021-02-25 14:20 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Wed, Feb 24, 2021 at 11:19:41PM -0700, Allison Henderson wrote:
> 
> 
> On 2/24/21 11:42 AM, Brian Foster wrote:
> > On Thu, Feb 18, 2021 at 09:53:35AM -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 asetheic right away, but it is a
> > > preliminary step to to manageing the state machine code.
> > > 
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > ---
> > >   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 19a532a..bfd4466 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > ...
> > > @@ -286,10 +287,101 @@ xfs_attr_set_args(
> > ...
> > >   			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:
> > 
> > Hmm, I'm not a fan of this unconditional return followed by a jump label
> > in the middle of the function. It's a pretty clear indication that this
> > is just two functions smashed together, so I'm not sure what the
> > advantage of this is. I'll continue on to see what falls out of the next
> > patches..
> > 
> > Brian
> 
> Yes, it does kinda look a little displaced, but the point of it is to bring
> code that will require state management into the same scope so that the
> state switch can span all the operations it affects.  Which seemed to be
> what the RFC was striving for?  Looking ahead at the other reviews, I think
> it came together for you?
> 

Yes, I think so. If the subsequent patches line everything up nicely in
that single switch statement in one place (which it looks like we're
pretty close to), then I can probably live with this transient quirk.
That said, I might wait until at least the attr set path looks pretty
much finalized and come back and take a last look at this one, if
nothing else just to think about if there's any simple/unintrusive way
to avoid it..

Brian

> Allison
> 
> > 
> > >   		/*
> > >   		 * Promote the attribute list to the Btree format.
> > >   		 */
> > > @@ -731,115 +823,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-25  7:01     ` Allison Henderson
@ 2021-02-25 14:22       ` Brian Foster
  2021-02-25 22:28         ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-25 14:22 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 25, 2021 at 12:01:10AM -0700, Allison Henderson wrote:
> 
> 
> On 2/24/21 11:45 AM, Brian Foster wrote:
> > On Thu, Feb 18, 2021 at 09:53:37AM -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 has become xfs_attr_remove_iter, which
> > > uses a sort of state machine like switch to keep track of where it was
> > > when EAGAIN was returned. xfs_attr_node_removename has also been
> > > modified to use the switch, and a new version of xfs_attr_remove_args
> > > consists of a simple loop to refresh the transaction until the operation
> > > is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
> > >   fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
> > >   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, 294 insertions(+), 83 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index 56d4b56..d46b92a 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
...
> > > @@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
> > >    *
> > >    * This routine will find the blocks of the name to remove, remove them and
> > >    * shrink the tree if needed.
> > > + *
> > > + * This routine is meant to function as either an inline or delayed operation,
> > > + * and may return -EAGAIN when the transaction needs to be rolled.  Calling
> > > + * functions will need to handle this, and recall the function until a
> > > + * successful error code is returned.
> > >    */
> > >   STATIC int
> > > -xfs_attr_node_removename(
> > > -	struct xfs_da_args	*args)
> > > +xfs_attr_node_removename_iter(
> > > +	struct xfs_delattr_context	*dac)
> > >   {
> > > -	struct xfs_da_state	*state = NULL;
> > > -	int			retval, error;
> > > -	struct xfs_inode	*dp = args->dp;
> > > +	struct xfs_da_args		*args = dac->da_args;
> > > +	struct xfs_da_state		*state = NULL;
> > > +	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;
> > > -
> > > -	error = xfs_attr_node_remove_step(args, state);
> > > -	if (error)
> > > -		goto out;
> > > -
> > > -	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 (!dac->da_state) {
> > > +		error = xfs_attr_node_removename_setup(dac);
> > >   		if (error)
> > >   			goto out;
> > > +	}
> > > +	state = dac->da_state;
> > > +
> > > +	switch (dac->dela_state) {
> > > +	case XFS_DAS_UNINIT:
> > >   		/*
> > > -		 * Commit the Btree join operation and start a new trans.
> > > +		 * repeatedly remove remote blocks, remove the entry and join.
> > > +		 * returns -EAGAIN or 0 for completion of the step.
> > >   		 */
> > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > +		error = xfs_attr_node_remove_step(dac);
> > >   		if (error)
> > > -			goto out;
> > > -	}
> > > +			break;
> > 
> > Hmm.. so re: my comment further down on xfs_attr_rmtval_remove(),
> > wouldn't that change semantics here? I.e., once remote blocks are
> > removed this would previously carry on with a clean transaction. Now it
> > looks like we'd carry on with the dirty transaction that removed the
> > last remote extent. This suggests that perhaps we should return once
> > more and fall into a new state to remove the name..?
> I suspect the diff might be making this a bit difficult to see.  The roll
> that you see being removed here belongs to the transaction we hoisted up  in
> patch 3 which happens after the clean up below, and we have the
> corresponding EAGAIN fot that one.  I think the diff gets things a little
> interlaced here because the switch adds another level of indentation.
> 

Hmm.. the roll in patch 3 appears to be related to the _cleanup()
helper. What I'm referring to here is the state of the transaction after
the final remote block is removed from the attr. I'm not sure we're
talking about the same thing here..

> some times i do like to I use a graphical diffviewer like diffuse when
> patches get weird like this.  Something like this:
> 
> git config --global diff.tool  diffuse
> git difftool 3c53e49 e201c09
> 
> You'd need to download the branch and also the diffuse tool, but sometimes i
> think it makes some of these diffs a bit easier to see
> 

I think it's easier just to refer to the code directly. The current
upstream code flows down into:

...
xfs_attr_node_removename()
 xfs_attr_node_remove_rmt()
  xfs_attr_rmtval_remove()

... which then implements the following loop:

        do {
                retval = __xfs_attr_rmtval_remove(args);
                if (retval && retval != -EAGAIN)
                        return retval;

                /*
                 * Close out trans and start the next one in the chain.
                 */
                error = xfs_trans_roll_inode(&args->trans, args->dp);
                if (error)
                        return error;
        } while (retval == -EAGAIN);

This rolls the transaction when retval == -EAGAIN or retval == 0, thus
always returns with a clean transaction after the remote block removal
completes.

The code as of this patch does:

...
xfs_attr_node_removename_iter()
 xfs_attr_node_remove_step()
  xfs_attr_node_remove_rmt()
   __xfs_attr_rmtval_remove()

... which either returns -EAGAIN (since the roll is now implemented at
the very top) or 0 when done == true. The transaction might be dirty in
the latter case, but xfs_attr_node_removename_iter() moves right on to
xfs_attr_node_remove_cleanup() which can now do more work in that same
transaction. Am I following that correctly?

> Also, it would be
> > nice to remove the several seemingly unnecessary layers of indirection
> > here. For example, something like the following (also considering my
> > comment above wrt to xfs_attr_remove_iter() and UNINIT):
> > 
> > 	case UNINIT:
> > 		...
> > 		/* fallthrough */
> > 	case RMTBLK:
> > 		if (args->rmtblkno > 0) {
> > 			dac->dela_state = RMTBLK;
> > 			error = __xfs_attr_rmtval_remove(dac);
> > 			if (error)
> > 				break;
> > 
> > 			ASSERT(args->rmtblkno == 0);
> > 			xfs_attr_refillstate(state);
> > 			dac->flags |= XFS_DAC_DEFER_FINISH;
> > 			dac->dela_state = RMNAME;
> > 			return -EAGAIN;
> > 		}
> Ok, this looks to me like we've hoisted both xfs_attr_node_remove_rmt and
> xfs_attr_node_remove_step into this scope, but I still think this adds an
> extra roll where non previously was.  With out that extra EAGAIN, I think we
> are fine to have all that just under the UNINIT case.  I also think it's
> also worth noteing here that this is kind of a reverse of patch 1, which I
> think we put in for reasons of trying to modularize the higher level
> functions as much as possible.
> 
> I suspect some of where you were going with this may have been influenced by
> the earlier diff confusion too.  Maybe take a second look there before we go
> too much down this change....
> 

I can certainly be getting lost somewhere in all the refactoring. If so,
can you point out where in the flow described above?

Brian

> 
> > 		/* fallthrough */
> > 	case RMNAME:
> > 		...
> > 	...
> > 
> > > -	/*
> > > -	 * 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);
> > > +		retval = xfs_attr_node_remove_cleanup(args, state);
> > ...
> I think the overlooked EAGAIN was in this area that got clipped out.....
> 
> > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > > index 48d8e9c..f09820c 100644
> > > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > ...
> > > @@ -685,31 +687,29 @@ c(
> > >   	 * 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;
> > 
> > Previously this would roll once and exit the loop on retval == 0. Now it
> > looks like we break out of the loop immediately. Why the change?
> 
> Gosh, I think sometime in reviewing v9, we had come up with a
> "xfs_attr_roll_again" helper that took the error code as a paramater and
> decided whether or not to roll.  And then in v10 i think people thought that
> was weird and we turned it into xfs_attr_trans_roll.  I think I likley
> forgot to restore the orginal retval handling here.  This whole function
> disappears in the next patch, but the original error handling should be
> restored to keep things consistent. Thx for the catch!
> 
> 
> Thx for the reviews!!  I know it's complicated!  I've chased my tail many
> times with it myself :-)
> 
> Allison
> 
> 
> 
> 
> > 
> > Brian
> > 
> > > -		/*
> > > -		 * 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 dont 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 reminicent 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] 87+ messages in thread

* Re: [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-25 14:22       ` Brian Foster
@ 2021-02-25 22:28         ` Allison Henderson
  2021-02-28 15:39           ` Brian Foster
  0 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-25 22:28 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/25/21 7:22 AM, Brian Foster wrote:
> On Thu, Feb 25, 2021 at 12:01:10AM -0700, Allison Henderson wrote:
>>
>>
>> On 2/24/21 11:45 AM, Brian Foster wrote:
>>> On Thu, Feb 18, 2021 at 09:53:37AM -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 has become xfs_attr_remove_iter, which
>>>> uses a sort of state machine like switch to keep track of where it was
>>>> when EAGAIN was returned. xfs_attr_node_removename has also been
>>>> modified to use the switch, and a new version of xfs_attr_remove_args
>>>> consists of a simple loop to refresh the transaction until the operation
>>>> is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
>>>>    fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
>>>>    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, 294 insertions(+), 83 deletions(-)
>>>>
>>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>>> index 56d4b56..d46b92a 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr.c
> ...
>>>> @@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
>>>>     *
>>>>     * This routine will find the blocks of the name to remove, remove them and
>>>>     * shrink the tree if needed.
>>>> + *
>>>> + * This routine is meant to function as either an inline or delayed operation,
>>>> + * and may return -EAGAIN when the transaction needs to be rolled.  Calling
>>>> + * functions will need to handle this, and recall the function until a
>>>> + * successful error code is returned.
>>>>     */
>>>>    STATIC int
>>>> -xfs_attr_node_removename(
>>>> -	struct xfs_da_args	*args)
>>>> +xfs_attr_node_removename_iter(
>>>> +	struct xfs_delattr_context	*dac)
>>>>    {
>>>> -	struct xfs_da_state	*state = NULL;
>>>> -	int			retval, error;
>>>> -	struct xfs_inode	*dp = args->dp;
>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>> +	struct xfs_da_state		*state = NULL;
>>>> +	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;
>>>> -
>>>> -	error = xfs_attr_node_remove_step(args, state);
>>>> -	if (error)
>>>> -		goto out;
>>>> -
>>>> -	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 (!dac->da_state) {
>>>> +		error = xfs_attr_node_removename_setup(dac);
>>>>    		if (error)
>>>>    			goto out;
>>>> +	}
>>>> +	state = dac->da_state;
>>>> +
>>>> +	switch (dac->dela_state) {
>>>> +	case XFS_DAS_UNINIT:
>>>>    		/*
>>>> -		 * Commit the Btree join operation and start a new trans.
>>>> +		 * repeatedly remove remote blocks, remove the entry and join.
>>>> +		 * returns -EAGAIN or 0 for completion of the step.
>>>>    		 */
>>>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>>>> +		error = xfs_attr_node_remove_step(dac);
>>>>    		if (error)
>>>> -			goto out;
>>>> -	}
>>>> +			break;
>>>
>>> Hmm.. so re: my comment further down on xfs_attr_rmtval_remove(),
>>> wouldn't that change semantics here? I.e., once remote blocks are
>>> removed this would previously carry on with a clean transaction. Now it
>>> looks like we'd carry on with the dirty transaction that removed the
>>> last remote extent. This suggests that perhaps we should return once
>>> more and fall into a new state to remove the name..?
>> I suspect the diff might be making this a bit difficult to see.  The roll
>> that you see being removed here belongs to the transaction we hoisted up  in
>> patch 3 which happens after the clean up below, and we have the
>> corresponding EAGAIN fot that one.  I think the diff gets things a little
>> interlaced here because the switch adds another level of indentation.
>>
> 
> Hmm.. the roll in patch 3 appears to be related to the _cleanup()
> helper. What I'm referring to here is the state of the transaction after
> the final remote block is removed from the attr. I'm not sure we're
> talking about the same thing here..
> 
>> some times i do like to I use a graphical diffviewer like diffuse when
>> patches get weird like this.  Something like this:
>>
>> git config --global diff.tool  diffuse
>> git difftool 3c53e49 e201c09
>>
>> You'd need to download the branch and also the diffuse tool, but sometimes i
>> think it makes some of these diffs a bit easier to see
>>
> 
> I think it's easier just to refer to the code directly. The current
> upstream code flows down into:
> 
> ...
> xfs_attr_node_removename()
>   xfs_attr_node_remove_rmt()
>    xfs_attr_rmtval_remove()
> 
> ... which then implements the following loop:
> 
>          do {
>                  retval = __xfs_attr_rmtval_remove(args);
>                  if (retval && retval != -EAGAIN)
>                          return retval;
> 
>                  /*
>                   * Close out trans and start the next one in the chain.
>                   */
>                  error = xfs_trans_roll_inode(&args->trans, args->dp);
>                  if (error)
>                          return error;
>          } while (retval == -EAGAIN);
> 
> This rolls the transaction when retval == -EAGAIN or retval == 0, thus
> always returns with a clean transaction after the remote block removal
> completes.
> 
> The code as of this patch does:
> 
> ...
> xfs_attr_node_removename_iter()
>   xfs_attr_node_remove_step()
>    xfs_attr_node_remove_rmt()
>     __xfs_attr_rmtval_remove()
> 
> ... which either returns -EAGAIN (since the roll is now implemented at
> the very top) or 0 when done == true. The transaction might be dirty in
> the latter case, but xfs_attr_node_removename_iter() moves right on to
> xfs_attr_node_remove_cleanup() which can now do more work in that same
> transaction. Am I following that correctly?
> 
>> Also, it would be
>>> nice to remove the several seemingly unnecessary layers of indirection
>>> here. For example, something like the following (also considering my
>>> comment above wrt to xfs_attr_remove_iter() and UNINIT):
>>>
>>> 	case UNINIT:
>>> 		...
>>> 		/* fallthrough */
>>> 	case RMTBLK:
>>> 		if (args->rmtblkno > 0) {
>>> 			dac->dela_state = RMTBLK;
>>> 			error = __xfs_attr_rmtval_remove(dac);
>>> 			if (error)
>>> 				break;
>>>
>>> 			ASSERT(args->rmtblkno == 0);
>>> 			xfs_attr_refillstate(state);
>>> 			dac->flags |= XFS_DAC_DEFER_FINISH;
>>> 			dac->dela_state = RMNAME;
>>> 			return -EAGAIN;
>>> 		}
>> Ok, this looks to me like we've hoisted both xfs_attr_node_remove_rmt and
>> xfs_attr_node_remove_step into this scope, but I still think this adds an
>> extra roll where non previously was.  With out that extra EAGAIN, I think we
>> are fine to have all that just under the UNINIT case.  I also think it's
>> also worth noteing here that this is kind of a reverse of patch 1, which I
>> think we put in for reasons of trying to modularize the higher level
>> functions as much as possible.
>>
>> I suspect some of where you were going with this may have been influenced by
>> the earlier diff confusion too.  Maybe take a second look there before we go
>> too much down this change....
>>
> 
> I can certainly be getting lost somewhere in all the refactoring. If so,
> can you point out where in the flow described above?
Ok, I think see it.  So basically I think this means we cant have the 
helpers because it's ambiguos as to if the transaction is dirty or not. 
  I dont see that there's anything in the review history where we 
rationalized that away, so I think we just overlooked it.  So I think 
what this means is that we need to reverse apply commit 72b97ea40d 
(which is where we added xfs_attr_node_remove_rmt), then drop patch 1 
which leaves no need for patch 3, since the transaction will have not 
moved.  Then add state RMTBLK?  I think that arrives at what you have here.

Allison

> 
> Brian
> 
>>
>>> 		/* fallthrough */
>>> 	case RMNAME:
>>> 		...
>>> 	...
>>>
>>>> -	/*
>>>> -	 * 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);
>>>> +		retval = xfs_attr_node_remove_cleanup(args, state);
>>> ...
>> I think the overlooked EAGAIN was in this area that got clipped out.....
>>
>>>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>>>> index 48d8e9c..f09820c 100644
>>>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>>>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>>> ...
>>>> @@ -685,31 +687,29 @@ c(
>>>>    	 * 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;
>>>
>>> Previously this would roll once and exit the loop on retval == 0. Now it
>>> looks like we break out of the loop immediately. Why the change?
>>
>> Gosh, I think sometime in reviewing v9, we had come up with a
>> "xfs_attr_roll_again" helper that took the error code as a paramater and
>> decided whether or not to roll.  And then in v10 i think people thought that
>> was weird and we turned it into xfs_attr_trans_roll.  I think I likley
>> forgot to restore the orginal retval handling here.  This whole function
>> disappears in the next patch, but the original error handling should be
>> restored to keep things consistent. Thx for the catch!
>>
>>
>> Thx for the reviews!!  I know it's complicated!  I've chased my tail many
>> times with it myself :-)
>>
>> Allison
>>
>>
>>
>>
>>>
>>> Brian
>>>
>>>> -		/*
>>>> -		 * 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 dont 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 reminicent 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] 87+ messages in thread

* Re: [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup
  2021-02-18 16:53 ` [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
  2021-02-24 15:03   ` Brian Foster
@ 2021-02-26  3:00   ` Darrick J. Wong
  2021-02-27  0:48     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  3:00 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:28AM -0700, Allison Henderson wrote:
> 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>

Looks ok,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  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 28ff93d..4e6c89d 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1220,6 +1220,25 @@ xfs_attr_node_remove_rmt(
>  	return xfs_attr_refillstate(state);
>  }
>  
> +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.
>   *
> @@ -1232,7 +1251,6 @@ xfs_attr_node_remove_step(
>  	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;
>  
> @@ -1247,14 +1265,7 @@ xfs_attr_node_remove_step(
>  		if (error)
>  			return error;
>  	}
> -
> -	/*
> -	 * 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step
  2021-02-18 16:53 ` [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step Allison Henderson
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-26  3:02   ` Darrick J. Wong
  2021-02-27  0:48     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  3:02 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:29AM -0700, Allison Henderson wrote:
> This patch hoists transaction handling in xfs_attr_node_removename to
> xfs_attr_node_remove_step.  This will help keep transaction handling in
> higher level functions instead of buried in subfunctions when we
> introduce delay attributes
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

Looks ok,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c | 45 ++++++++++++++++++++++-----------------------
>  1 file changed, 22 insertions(+), 23 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4e6c89d..3cf76e2 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1251,9 +1251,7 @@ xfs_attr_node_remove_step(
>  	struct xfs_da_args	*args,
>  	struct xfs_da_state	*state)
>  {
> -	int			retval, error;
> -	struct xfs_inode	*dp = args->dp;
> -
> +	int			error = 0;
>  
>  	/*
>  	 * If there is an out-of-line value, de-allocate the blocks.
> @@ -1265,25 +1263,6 @@ xfs_attr_node_remove_step(
>  		if (error)
>  			return error;
>  	}
> -	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)
> -			return error;
> -		error = xfs_defer_finish(&args->trans);
> -		if (error)
> -			return error;
> -		/*
> -		 * Commit the Btree join operation and start a new trans.
> -		 */
> -		error = xfs_trans_roll_inode(&args->trans, dp);
> -		if (error)
> -			return error;
> -	}
>  
>  	return error;
>  }
> @@ -1299,7 +1278,7 @@ xfs_attr_node_removename(
>  	struct xfs_da_args	*args)
>  {
>  	struct xfs_da_state	*state = NULL;
> -	int			error;
> +	int			retval, error;
>  	struct xfs_inode	*dp = args->dp;
>  
>  	trace_xfs_attr_node_removename(args);
> @@ -1312,6 +1291,26 @@ xfs_attr_node_removename(
>  	if (error)
>  		goto out;
>  
> +	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.
> +		 */
> +		error = xfs_trans_roll_inode(&args->trans, dp);
> +		if (error)
> +			goto out;
> +	}
> +
>  	/*
>  	 * If the result is small enough, push it all into the inode.
>  	 */
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform
  2021-02-18 16:53 ` [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform Allison Henderson
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-26  3:03   ` Darrick J. Wong
  2021-02-27  0:48     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  3:03 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:30AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

Whoah, /removing/ a function! :)

Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  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 3cf76e2..a064c5b 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt
  2021-02-18 16:53 ` [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt Allison Henderson
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-26  3:07   ` Darrick J. Wong
  2021-02-27  0:49     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  3:07 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:31AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 77 +++++++++++++++++++++++++++---------------------
>  1 file changed, 44 insertions(+), 33 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index a064c5b..205ad26 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -216,6 +216,46 @@ 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);

Shouldn't this pass the error back to the caller?

--D

> +
> +	return -EAGAIN;
> +}
> +
>  /*
>   * Set the attribute specified in @args.
>   */
> @@ -224,8 +264,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 +273,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)
> +		error = xfs_attr_set_fmt(args);
> +		if (error != -EAGAIN)
>  			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;
> -		}
>  	}
>  
>  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
> @@ -297,8 +309,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-02-18 16:53 ` [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work Allison Henderson
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-26  4:02   ` Darrick J. Wong
  2021-02-27  0:54     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  4:02 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:32AM -0700, 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>
> ---
>  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 205ad26..bee8d3fb 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_work(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);
> @@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
>  			return error;
>  	}
>  
> +	error = xfs_attr_node_addname_work(args);
> +out:
> +	if (state)
> +		xfs_da_state_free(state);
> +	if (error)
> +		return error;
> +	return retval;
> +}
> +
> +
> +STATIC
> +int xfs_attr_node_addname_work(

What, erm, work does this function do?  Since it survives to the end of
the patchset, I think this needs a better name (or at least needs a
comment about what it's actually supposed to do).

AFAICT you're splitting node_addname() into two functions because we're
at a transaction roll point, and this "_work" function exists to remove
the copy of the xattr key that has the "INCOMPLETE" bit set (aka the old
one), right?

--D

> +	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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-02-18 16:53 ` [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
  2021-02-24 15:04   ` Brian Foster
@ 2021-02-26  4:06   ` Darrick J. Wong
  2021-02-27  0:54     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  4:06 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:33AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

Looks ok to me,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c | 80 ++++++++++++++++++++++++++++++------------------
>  1 file changed, 51 insertions(+), 29 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index bee8d3fb..4333b61 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_work(struct xfs_da_args *args);
>  STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
> @@ -265,6 +268,7 @@ xfs_attr_set_args(
>  	struct xfs_da_args	*args)
>  {
>  	struct xfs_inode	*dp = args->dp;
> +	struct xfs_da_state     *state;
>  	int			error;
>  
>  	/*
> @@ -310,7 +314,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;
>  }
>  
>  /*
> @@ -883,42 +894,21 @@ 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;
>  
> -	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;
>  	if (retval == -EEXIST) {
> @@ -941,6 +931,38 @@ xfs_attr_node_addname(
>  		args->rmtvaluelen = 0;
>  	}
>  
> +	return 0;
> +out:
> +	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) {
> @@ -966,7 +988,7 @@ xfs_attr_node_addname(
>  			if (error)
>  				goto out;
>  
> -			goto restart;
> +			return -EAGAIN;
>  		}
>  
>  		/*
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 21/22] xfs: Add delattr mount option
  2021-02-18 16:53 ` [PATCH v15 21/22] xfs: Add delattr mount option Allison Henderson
@ 2021-02-26  4:29   ` Darrick J. Wong
  2021-02-27  0:55     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  4:29 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:47AM -0700, Allison Henderson wrote:
> This patch adds a mount option to enable delayed attributes. Eventually
> this can be removed when delayed attrs becomes permanent.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.h | 2 +-
>  fs/xfs/xfs_mount.h       | 1 +
>  fs/xfs/xfs_super.c       | 6 +++++-
>  fs/xfs/xfs_xattr.c       | 2 ++
>  4 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index f82c0b1..35f3a53 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -30,7 +30,7 @@ struct xfs_attr_list_context;
>  
>  static inline bool xfs_hasdelattr(struct xfs_mount *mp)
>  {
> -	return false;
> +	return mp->m_flags & XFS_MOUNT_DELATTR;
>  }
>  
>  /*
> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
> index 659ad95..57cd914 100644
> --- a/fs/xfs/xfs_mount.h
> +++ b/fs/xfs/xfs_mount.h
> @@ -250,6 +250,7 @@ typedef struct xfs_mount {
>  #define XFS_MOUNT_NOATTR2	(1ULL << 25)	/* disable use of attr2 format */
>  #define XFS_MOUNT_DAX_ALWAYS	(1ULL << 26)
>  #define XFS_MOUNT_DAX_NEVER	(1ULL << 27)
> +#define XFS_MOUNT_DELATTR	(1ULL << 28)	/* enable delayed attributes */
>  
>  /*
>   * Max and min values for mount-option defined I/O
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 21b1d03..f6b08f9 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -93,7 +93,7 @@ enum {
>  	Opt_filestreams, Opt_quota, Opt_noquota, Opt_usrquota, Opt_grpquota,
>  	Opt_prjquota, Opt_uquota, Opt_gquota, Opt_pquota,
>  	Opt_uqnoenforce, Opt_gqnoenforce, Opt_pqnoenforce, Opt_qnoenforce,
> -	Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum,
> +	Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum, Opt_delattr
>  };
>  
>  static const struct fs_parameter_spec xfs_fs_parameters[] = {
> @@ -138,6 +138,7 @@ static const struct fs_parameter_spec xfs_fs_parameters[] = {
>  	fsparam_flag("nodiscard",	Opt_nodiscard),
>  	fsparam_flag("dax",		Opt_dax),
>  	fsparam_enum("dax",		Opt_dax_enum, dax_param_enums),
> +	fsparam_flag("delattr",		Opt_delattr),

I wonder if this ought to be hidden behind CONFIG_XFS_DEBUG=y, but
other than that this looks fine to me.

--D

>  	{}
>  };
>  
> @@ -1263,6 +1264,9 @@ xfs_fs_parse_param(
>  		xfs_mount_set_dax_mode(mp, result.uint_32);
>  		return 0;
>  #endif
> +	case Opt_delattr:
> +		mp->m_flags |= XFS_MOUNT_DELATTR;
> +		return 0;
>  	/* Following mount options will be removed in September 2025 */
>  	case Opt_ikeep:
>  		xfs_warn(mp, "%s mount option is deprecated.", param->key);
> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> index 9b0c790..8ec61df 100644
> --- a/fs/xfs/xfs_xattr.c
> +++ b/fs/xfs/xfs_xattr.c
> @@ -8,6 +8,8 @@
>  #include "xfs_shared.h"
>  #include "xfs_format.h"
>  #include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
>  #include "xfs_da_format.h"
>  #include "xfs_inode.h"
>  #include "xfs_da_btree.h"
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations
  2021-02-18 16:53 ` [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations Allison Henderson
@ 2021-02-26  4:58   ` Darrick J. Wong
  2021-02-27  0:56     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  4:58 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:42AM -0700, Allison Henderson wrote:
> Currently attributes are modified directly across one or more
> transactions. But they are not logged or replayed in the event of an
> error. The goal of delayed attributes is to enable logging and replaying
> of attribute operations using the existing delayed operations
> infrastructure.  This will later enable the attributes to become part of
> larger multi part operations that also must first be recorded to the
> log.  This is mostly of interest in the scheme of parent pointers which
> would need to maintain an attribute containing parent inode information
> any time an inode is moved, created, or removed.  Parent pointers would
> then be of interest to any feature that would need to quickly derive an
> inode path from the mount point. Online scrub, nfs lookups and fs grow
> or shrink operations are all features that could take advantage of this.
> 
> This patch adds two new log item types for setting or removing
> attributes as deferred operations.  The xfs_attri_log_item logs an
> intent to set or remove an attribute.  The corresponding
> xfs_attrd_log_item holds a reference to the xfs_attri_log_item and is
> freed once the transaction is done.  Both log items use a generic
> xfs_attr_log_format structure that contains the attribute name, value,
> flags, inode, and an op_flag that indicates if the operations is a set
> or remove.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

I saw a 36K patch and thought "Jeez!  This should be broken up!"

Then I looked at atomic extent swap (which does add its log item code
gradually) and decided that maybe you just don't want to explode the
patchset from 22 to 30.  That sounds (kind of) reasonable.

(...and by 'kind of', I probably mean that if I end up taking everything
up to this patch for 5.13 then this part really ought to get broken up a
bit for a future submission.  But let's wait until the end of this
review to decide that...)

> ---
>  fs/xfs/Makefile                 |   1 +
>  fs/xfs/libxfs/xfs_attr.c        |   7 +-
>  fs/xfs/libxfs/xfs_attr.h        |  31 ++
>  fs/xfs/libxfs/xfs_defer.c       |   1 +
>  fs/xfs/libxfs/xfs_defer.h       |   3 +
>  fs/xfs/libxfs/xfs_log_format.h  |  44 ++-
>  fs/xfs/libxfs/xfs_log_recover.h |   2 +
>  fs/xfs/scrub/common.c           |   2 +
>  fs/xfs/xfs_acl.c                |   2 +
>  fs/xfs/xfs_attr_item.c          | 828 ++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_attr_item.h          |  52 +++
>  fs/xfs/xfs_attr_list.c          |   1 +
>  fs/xfs/xfs_ioctl.c              |   2 +
>  fs/xfs/xfs_ioctl32.c            |   2 +
>  fs/xfs/xfs_iops.c               |   2 +
>  fs/xfs/xfs_log.c                |   4 +
>  fs/xfs/xfs_log_recover.c        |   2 +
>  fs/xfs/xfs_ondisk.h             |   2 +
>  fs/xfs/xfs_xattr.c              |   1 +
>  19 files changed, 984 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index 04611a1..b056cfc 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -102,6 +102,7 @@ xfs-y				+= xfs_log.o \
>  				   xfs_buf_item_recover.o \
>  				   xfs_dquot_item_recover.o \
>  				   xfs_extfree_item.o \
> +				   xfs_attr_item.o \
>  				   xfs_icreate_item.o \
>  				   xfs_inode_item.o \
>  				   xfs_inode_item_recover.o \
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 2b8e481..e4c1b4b 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -24,6 +24,7 @@
>  #include "xfs_quota.h"
>  #include "xfs_trans_space.h"
>  #include "xfs_trace.h"
> +#include "xfs_attr_item.h"
>  
>  /*
>   * xfs_attr.c
> @@ -61,8 +62,8 @@ 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_attr_set_iter(struct xfs_delattr_context *dac,
> +		      struct xfs_buf **leaf_bp);
>  
>  int
>  xfs_inode_hasattr(
> @@ -144,7 +145,7 @@ xfs_attr_get(
>  /*
>   * Calculate how many blocks we need for the new attribute,
>   */
> -STATIC int
> +int
>  xfs_attr_calc_size(
>  	struct xfs_da_args	*args,
>  	int			*local)
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 603887e..ee79763 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -28,6 +28,11 @@ struct xfs_attr_list_context;
>   */
>  #define	ATTR_MAX_VALUELEN	(64*1024)	/* max length of a value */
>  
> +static inline bool xfs_hasdelattr(struct xfs_mount *mp)
> +{
> +	return false;
> +}
> +
>  /*
>   * Kernel-internal version of the attrlist cursor.
>   */
> @@ -390,6 +395,7 @@ enum xfs_delattr_state {
>   */
>  #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
>  #define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
> +#define XFS_DAC_DELAYED_OP_INIT		0x04 /* delayed operations init*/
>  
>  /*
>   * Context used for keeping track of delayed attribute operations
> @@ -397,6 +403,11 @@ enum xfs_delattr_state {
>  struct xfs_delattr_context {
>  	struct xfs_da_args      *da_args;
>  
> +	/*
> +	 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
> +	 */
> +	struct xfs_buf		*leaf_bp;
> +
>  	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
>  	struct xfs_bmbt_irec	map;
>  	xfs_dablk_t		lblkno;
> @@ -410,6 +421,23 @@ struct xfs_delattr_context {
>  	enum xfs_delattr_state  dela_state;
>  };
>  
> +/*
> + * List of attrs to commit later.
> + */
> +struct xfs_attr_item {
> +	struct xfs_delattr_context	xattri_dac;
> +
> +	/*
> +	 * Indicates if the attr operation is a set or a remove
> +	 * XFS_ATTR_OP_FLAGS_{SET,REMOVE}
> +	 */
> +	uint32_t			xattri_op_flags;
> +
> +	/* used to log this item to an intent */
> +	struct list_head		xattri_list;
> +};
> +
> +
>  /*========================================================================
>   * Function prototypes for the kernel.
>   *========================================================================*/
> @@ -425,11 +453,14 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>  int xfs_attr_get(struct xfs_da_args *args);
>  int xfs_attr_set(struct xfs_da_args *args);
>  int xfs_attr_set_args(struct xfs_da_args *args);
> +int xfs_attr_set_iter(struct xfs_delattr_context *dac,
> +		      struct xfs_buf **leaf_bp);
>  int xfs_has_attr(struct xfs_da_args *args);
>  int xfs_attr_remove_args(struct xfs_da_args *args);
>  int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>  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);
> +int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
>  
>  #endif	/* __XFS_ATTR_H__ */
> diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
> index eff4a12..e9caff7 100644
> --- a/fs/xfs/libxfs/xfs_defer.c
> +++ b/fs/xfs/libxfs/xfs_defer.c
> @@ -178,6 +178,7 @@ static const struct xfs_defer_op_type *defer_op_types[] = {
>  	[XFS_DEFER_OPS_TYPE_RMAP]	= &xfs_rmap_update_defer_type,
>  	[XFS_DEFER_OPS_TYPE_FREE]	= &xfs_extent_free_defer_type,
>  	[XFS_DEFER_OPS_TYPE_AGFL_FREE]	= &xfs_agfl_free_defer_type,
> +	[XFS_DEFER_OPS_TYPE_ATTR]	= &xfs_attr_defer_type,
>  };
>  
>  static void
> diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
> index 05472f7..72a5789 100644
> --- a/fs/xfs/libxfs/xfs_defer.h
> +++ b/fs/xfs/libxfs/xfs_defer.h
> @@ -19,6 +19,7 @@ enum xfs_defer_ops_type {
>  	XFS_DEFER_OPS_TYPE_RMAP,
>  	XFS_DEFER_OPS_TYPE_FREE,
>  	XFS_DEFER_OPS_TYPE_AGFL_FREE,
> +	XFS_DEFER_OPS_TYPE_ATTR,
>  	XFS_DEFER_OPS_TYPE_MAX,
>  };
>  
> @@ -63,6 +64,8 @@ extern const struct xfs_defer_op_type xfs_refcount_update_defer_type;
>  extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
>  extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
>  extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
> +extern const struct xfs_defer_op_type xfs_attr_defer_type;
> +
>  
>  /*
>   * This structure enables a dfops user to detach the chain of deferred
> diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
> index 8bd00da..19963b6 100644
> --- a/fs/xfs/libxfs/xfs_log_format.h
> +++ b/fs/xfs/libxfs/xfs_log_format.h
> @@ -117,7 +117,12 @@ struct xfs_unmount_log_format {
>  #define XLOG_REG_TYPE_CUD_FORMAT	24
>  #define XLOG_REG_TYPE_BUI_FORMAT	25
>  #define XLOG_REG_TYPE_BUD_FORMAT	26
> -#define XLOG_REG_TYPE_MAX		26
> +#define XLOG_REG_TYPE_ATTRI_FORMAT	27
> +#define XLOG_REG_TYPE_ATTRD_FORMAT	28
> +#define XLOG_REG_TYPE_ATTR_NAME	29
> +#define XLOG_REG_TYPE_ATTR_VALUE	30
> +#define XLOG_REG_TYPE_MAX		30
> +
>  
>  /*
>   * Flags to log operation header
> @@ -240,6 +245,8 @@ typedef struct xfs_trans_header {
>  #define	XFS_LI_CUD		0x1243
>  #define	XFS_LI_BUI		0x1244	/* bmbt update intent */
>  #define	XFS_LI_BUD		0x1245
> +#define	XFS_LI_ATTRI		0x1246  /* attr set/remove intent*/
> +#define	XFS_LI_ATTRD		0x1247  /* attr set/remove done */
>  
>  #define XFS_LI_TYPE_DESC \
>  	{ XFS_LI_EFI,		"XFS_LI_EFI" }, \
> @@ -255,7 +262,9 @@ typedef struct xfs_trans_header {
>  	{ XFS_LI_CUI,		"XFS_LI_CUI" }, \
>  	{ XFS_LI_CUD,		"XFS_LI_CUD" }, \
>  	{ XFS_LI_BUI,		"XFS_LI_BUI" }, \
> -	{ XFS_LI_BUD,		"XFS_LI_BUD" }
> +	{ XFS_LI_BUD,		"XFS_LI_BUD" }, \
> +	{ XFS_LI_ATTRI,		"XFS_LI_ATTRI" }, \
> +	{ XFS_LI_ATTRD,		"XFS_LI_ATTRD" }
>  
>  /*
>   * Inode Log Item Format definitions.
> @@ -863,4 +872,35 @@ struct xfs_icreate_log {
>  	__be32		icl_gen;	/* inode generation number to use */
>  };
>  
> +/*
> + * Flags for deferred attribute operations.
> + * Upper bits are flags, lower byte is type code
> + */
> +#define XFS_ATTR_OP_FLAGS_SET		1	/* Set the attribute */
> +#define XFS_ATTR_OP_FLAGS_REMOVE	2	/* Remove the attribute */
> +#define XFS_ATTR_OP_FLAGS_TYPE_MASK	0x0FF	/* Flags type mask */
> +
> +/*
> + * This is the structure used to lay out an attr log item in the
> + * log.
> + */
> +struct xfs_attri_log_format {
> +	uint16_t	alfi_type;	/* attri log item type */
> +	uint16_t	alfi_size;	/* size of this item */
> +	uint32_t	__pad;		/* pad to 64 bit aligned */
> +	uint64_t	alfi_id;	/* attri identifier */
> +	uint64_t	alfi_ino;	/* the inode for this attr operation */
> +	uint32_t	alfi_op_flags;	/* marks the op as a set or remove */
> +	uint32_t	alfi_name_len;	/* attr name length */
> +	uint32_t	alfi_value_len;	/* attr value length */
> +	uint32_t	alfi_attr_flags;/* attr flags */
> +};
> +
> +struct xfs_attrd_log_format {
> +	uint16_t	alfd_type;	/* attrd log item type */
> +	uint16_t	alfd_size;	/* size of this item */
> +	uint32_t	__pad;		/* pad to 64 bit aligned */
> +	uint64_t	alfd_alf_id;	/* id of corresponding attri */
> +};
> +
>  #endif /* __XFS_LOG_FORMAT_H__ */
> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
> index 3cca2bf..b6e5514 100644
> --- a/fs/xfs/libxfs/xfs_log_recover.h
> +++ b/fs/xfs/libxfs/xfs_log_recover.h
> @@ -72,6 +72,8 @@ extern const struct xlog_recover_item_ops xlog_rui_item_ops;
>  extern const struct xlog_recover_item_ops xlog_rud_item_ops;
>  extern const struct xlog_recover_item_ops xlog_cui_item_ops;
>  extern const struct xlog_recover_item_ops xlog_cud_item_ops;
> +extern const struct xlog_recover_item_ops xlog_attri_item_ops;
> +extern const struct xlog_recover_item_ops xlog_attrd_item_ops;
>  
>  /*
>   * Macros, structures, prototypes for internal log manager use.
> diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
> index 53456f3..ac35121 100644
> --- a/fs/xfs/scrub/common.c
> +++ b/fs/xfs/scrub/common.c
> @@ -24,6 +24,8 @@
>  #include "xfs_rmap_btree.h"
>  #include "xfs_log.h"
>  #include "xfs_trans_priv.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_reflink.h"
>  #include "scrub/scrub.h"
> diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
> index 779cb73..79f7bd2 100644
> --- a/fs/xfs/xfs_acl.c
> +++ b/fs/xfs/xfs_acl.c
> @@ -10,6 +10,8 @@
>  #include "xfs_trans_resv.h"
>  #include "xfs_mount.h"
>  #include "xfs_inode.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_trace.h"
>  #include "xfs_error.h"
> diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
> new file mode 100644
> index 0000000..8c8f72d
> --- /dev/null
> +++ b/fs/xfs/xfs_attr_item.c
> @@ -0,0 +1,828 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2021 Oracle.  All Rights Reserved.
> + * Author: Allison Collins <allison.henderson@oracle.com>
> + */
> +
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_bit.h"
> +#include "xfs_shared.h"
> +#include "xfs_mount.h"
> +#include "xfs_defer.h"
> +#include "xfs_da_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_trans.h"
> +#include "xfs_bmap.h"
> +#include "xfs_bmap_btree.h"
> +#include "xfs_trans_priv.h"
> +#include "xfs_buf_item.h"
> +#include "xfs_attr_item.h"
> +#include "xfs_log.h"
> +#include "xfs_btree.h"
> +#include "xfs_rmap.h"
> +#include "xfs_inode.h"
> +#include "xfs_icache.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_attr.h"
> +#include "xfs_shared.h"
> +#include "xfs_attr_item.h"
> +#include "xfs_alloc.h"
> +#include "xfs_bmap.h"
> +#include "xfs_trace.h"
> +#include "libxfs/xfs_da_format.h"
> +#include "xfs_inode.h"
> +#include "xfs_quota.h"
> +#include "xfs_trans_space.h"
> +#include "xfs_log_priv.h"
> +#include "xfs_log_recover.h"
> +
> +static const struct xfs_item_ops xfs_attri_item_ops;
> +static const struct xfs_item_ops xfs_attrd_item_ops;
> +
> +/* iovec length must be 32-bit aligned */
> +static inline size_t ATTR_NVEC_SIZE(size_t size)
> +{
> +	return size == sizeof(int32_t) ? size :
> +	       sizeof(int32_t) + round_up(size, sizeof(int32_t));
> +}
> +
> +static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip)
> +{
> +	return container_of(lip, struct xfs_attri_log_item, attri_item);
> +}
> +
> +STATIC void
> +xfs_attri_item_free(
> +	struct xfs_attri_log_item	*attrip)
> +{
> +	kmem_free(attrip->attri_item.li_lv_shadow);
> +	kmem_free(attrip);
> +}
> +
> +/*
> + * Freeing the attrip requires that we remove it from the AIL if it has already
> + * been placed there. However, the ATTRI may not yet have been placed in the
> + * AIL when called by xfs_attri_release() from ATTRD processing due to the
> + * ordering of committed vs unpin operations in bulk insert operations. Hence
> + * the reference count to ensure only the last caller frees the ATTRI.
> + */
> +STATIC void
> +xfs_attri_release(
> +	struct xfs_attri_log_item	*attrip)
> +{
> +	ASSERT(atomic_read(&attrip->attri_refcount) > 0);
> +	if (atomic_dec_and_test(&attrip->attri_refcount)) {
> +		xfs_trans_ail_delete(&attrip->attri_item,
> +				     SHUTDOWN_LOG_IO_ERROR);
> +		xfs_attri_item_free(attrip);
> +	}
> +}
> +
> +STATIC void
> +xfs_attri_item_size(
> +	struct xfs_log_item	*lip,
> +	int			*nvecs,
> +	int			*nbytes)
> +{
> +	struct xfs_attri_log_item       *attrip = ATTRI_ITEM(lip);
> +
> +	*nvecs += 1;
> +	*nbytes += sizeof(struct xfs_attri_log_format);
> +
> +	/* Attr set and remove operations require a name */
> +	ASSERT(attrip->attri_name_len > 0);
> +
> +	*nvecs += 1;
> +	*nbytes += ATTR_NVEC_SIZE(attrip->attri_name_len);
> +
> +	/*
> +	 * Set ops can accept a value of 0 len to clear an attr value.  Remove
> +	 * ops do not need a value at all.  So only account for the value
> +	 * when it is needed.
> +	 */
> +	if (attrip->attri_value_len > 0) {
> +		*nvecs += 1;
> +		*nbytes += ATTR_NVEC_SIZE(attrip->attri_value_len);
> +	}
> +}
> +
> +/*
> + * This is called to fill in the log iovecs for the given attri log
> + * item. We use  1 iovec for the attri_format_item, 1 for the name, and
> + * another for the value if it is present
> + */
> +STATIC void
> +xfs_attri_item_format(
> +	struct xfs_log_item	*lip,
> +	struct xfs_log_vec	*lv)
> +{
> +	struct xfs_attri_log_item	*attrip = ATTRI_ITEM(lip);
> +	struct xfs_log_iovec		*vecp = NULL;
> +
> +	attrip->attri_format.alfi_type = XFS_LI_ATTRI;
> +	attrip->attri_format.alfi_size = 1;
> +
> +	/*
> +	 * This size accounting must be done before copying the attrip into the
> +	 * iovec.  If we do it after, the wrong size will be recorded to the log
> +	 * and we trip across assertion checks for bad region sizes later during
> +	 * the log recovery.
> +	 */
> +
> +	ASSERT(attrip->attri_name_len > 0);
> +	attrip->attri_format.alfi_size++;
> +
> +	if (attrip->attri_value_len > 0)
> +		attrip->attri_format.alfi_size++;
> +
> +	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT,
> +			&attrip->attri_format,
> +			sizeof(struct xfs_attri_log_format));
> +	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME,
> +			attrip->attri_name,
> +			ATTR_NVEC_SIZE(attrip->attri_name_len));
> +	if (attrip->attri_value_len > 0)
> +		xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE,
> +				attrip->attri_value,
> +				ATTR_NVEC_SIZE(attrip->attri_value_len));
> +}
> +
> +/*
> + * The unpin operation is the last place an ATTRI is manipulated in the log. It
> + * is either inserted in the AIL or aborted in the event of a log I/O error. In
> + * either case, the ATTRI transaction has been successfully committed to make
> + * it this far. Therefore, we expect whoever committed the ATTRI to either
> + * construct and commit the ATTRD or drop the ATTRD's reference in the event of
> + * error. Simply drop the log's ATTRI reference now that the log is done with
> + * it.
> + */
> +STATIC void
> +xfs_attri_item_unpin(
> +	struct xfs_log_item	*lip,
> +	int			remove)
> +{
> +	xfs_attri_release(ATTRI_ITEM(lip));
> +}
> +
> +
> +STATIC void
> +xfs_attri_item_release(
> +	struct xfs_log_item	*lip)
> +{
> +	xfs_attri_release(ATTRI_ITEM(lip));
> +}
> +
> +/*
> + * Allocate and initialize an attri item.  Caller may allocate an additional
> + * trailing buffer of the specified size
> + */
> +STATIC struct xfs_attri_log_item *
> +xfs_attri_init(
> +	struct xfs_mount		*mp,
> +	int				buffer_size)
> +
> +{
> +	struct xfs_attri_log_item	*attrip;
> +	uint				size;
> +
> +	size = sizeof(struct xfs_attri_log_item) + buffer_size;
> +	attrip = kmem_alloc_large(size, KM_ZERO);
> +	if (attrip == NULL)
> +		return NULL;
> +
> +	xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI,
> +			  &xfs_attri_item_ops);
> +	attrip->attri_format.alfi_id = (uintptr_t)(void *)attrip;
> +	atomic_set(&attrip->attri_refcount, 2);
> +
> +	return attrip;
> +}
> +
> +/*
> + * Copy an attr format buffer from the given buf, and into the destination attr
> + * format structure.
> + */
> +STATIC int
> +xfs_attri_copy_format(
> +	struct xfs_log_iovec		*buf,
> +	struct xfs_attri_log_format	*dst_attr_fmt)
> +{
> +	struct xfs_attri_log_format	*src_attr_fmt = buf->i_addr;
> +	uint				len;
> +
> +	len = sizeof(struct xfs_attri_log_format);
> +	if (buf->i_len != len)
> +		return -EFSCORRUPTED;
> +
> +	memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len);
> +	return 0;
> +}
> +
> +static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip)
> +{
> +	return container_of(lip, struct xfs_attrd_log_item, attrd_item);
> +}
> +
> +STATIC void
> +xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp)
> +{
> +	kmem_free(attrdp->attrd_item.li_lv_shadow);
> +	kmem_free(attrdp);
> +}
> +
> +STATIC void
> +xfs_attrd_item_size(
> +	struct xfs_log_item		*lip,
> +	int				*nvecs,
> +	int				*nbytes)
> +{
> +	*nvecs += 1;
> +	*nbytes += sizeof(struct xfs_attrd_log_format);
> +}
> +
> +/*
> + * This is called to fill in the log iovecs for the given attrd log item. We use
> + * only 1 iovec for the attrd_format, and we point that at the attr_log_format
> + * structure embedded in the attrd item.
> + */
> +STATIC void
> +xfs_attrd_item_format(
> +	struct xfs_log_item	*lip,
> +	struct xfs_log_vec	*lv)
> +{
> +	struct xfs_attrd_log_item	*attrdp = ATTRD_ITEM(lip);
> +	struct xfs_log_iovec		*vecp = NULL;
> +
> +	attrdp->attrd_format.alfd_type = XFS_LI_ATTRD;
> +	attrdp->attrd_format.alfd_size = 1;
> +
> +	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRD_FORMAT,
> +			&attrdp->attrd_format,
> +			sizeof(struct xfs_attrd_log_format));
> +}
> +
> +/*
> + * The ATTRD is either committed or aborted if the transaction is cancelled. If
> + * the transaction is cancelled, drop our reference to the ATTRI and free the
> + * ATTRD.
> + */
> +STATIC void
> +xfs_attrd_item_release(
> +	struct xfs_log_item		*lip)
> +{
> +	struct xfs_attrd_log_item	*attrdp = ATTRD_ITEM(lip);
> +
> +	xfs_attri_release(attrdp->attrd_attrip);
> +	xfs_attrd_item_free(attrdp);
> +}
> +
> +/*
> + * Performs one step of an attribute update intent and marks the attrd item
> + * dirty..  An attr operation may be a set or a remove.  Note that the
> + * transaction is marked dirty regardless of whether the operation succeeds or
> + * fails to support the ATTRI/ATTRD lifecycle rules.
> + */
> +int
> +xfs_trans_attr(

xfs_attri_finish_update() ?

> +	struct xfs_delattr_context	*dac,
> +	struct xfs_attrd_log_item	*attrdp,
> +	struct xfs_buf			**leaf_bp,
> +	uint32_t			op_flags)
> +{
> +	struct xfs_da_args		*args = dac->da_args;
> +	int				error;
> +
> +	error = xfs_qm_dqattach_locked(args->dp, 0);
> +	if (error)
> +		return error;
> +
> +	switch (op_flags) {
> +	case XFS_ATTR_OP_FLAGS_SET:
> +		args->op_flags |= XFS_DA_OP_ADDNAME;
> +		error = xfs_attr_set_iter(dac, leaf_bp);
> +		break;
> +	case XFS_ATTR_OP_FLAGS_REMOVE:
> +		ASSERT(XFS_IFORK_Q(args->dp));
> +		error = xfs_attr_remove_iter(dac);
> +		break;
> +	default:
> +		error = -EFSCORRUPTED;
> +		break;
> +	}
> +
> +	/*
> +	 * Mark the transaction dirty, even on error. This ensures the
> +	 * transaction is aborted, which:
> +	 *
> +	 * 1.) releases the ATTRI and frees the ATTRD
> +	 * 2.) shuts down the filesystem
> +	 */
> +	args->trans->t_flags |= XFS_TRANS_DIRTY;
> +
> +	/*
> +	 * attr intent/done items are null when delayed attributes are disabled
> +	 */
> +	if (attrdp)
> +		set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
> +
> +	return error;
> +}
> +
> +/* Log an attr to the intent item. */
> +STATIC void
> +xfs_attr_log_item(
> +	struct xfs_trans		*tp,
> +	struct xfs_attri_log_item	*attrip,
> +	struct xfs_attr_item		*attr)
> +{
> +	struct xfs_attri_log_format	*attrp;
> +
> +	tp->t_flags |= XFS_TRANS_DIRTY;
> +	set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags);
> +
> +	/*
> +	 * At this point the xfs_attr_item has been constructed, and we've
> +	 * created the log intent. Fill in the attri log item and log format
> +	 * structure with fields from this xfs_attr_item
> +	 */
> +	attrp = &attrip->attri_format;
> +	attrp->alfi_ino = attr->xattri_dac.da_args->dp->i_ino;
> +	attrp->alfi_op_flags = attr->xattri_op_flags;
> +	attrp->alfi_value_len = attr->xattri_dac.da_args->valuelen;
> +	attrp->alfi_name_len = attr->xattri_dac.da_args->namelen;
> +	attrp->alfi_attr_flags = attr->xattri_dac.da_args->attr_filter;
> +
> +	attrip->attri_name = (void *)attr->xattri_dac.da_args->name;
> +	attrip->attri_value = attr->xattri_dac.da_args->value;
> +	attrip->attri_name_len = attr->xattri_dac.da_args->namelen;
> +	attrip->attri_value_len = attr->xattri_dac.da_args->valuelen;
> +}
> +
> +/* Get an ATTRI. */
> +static struct xfs_log_item *
> +xfs_attr_create_intent(
> +	struct xfs_trans		*tp,
> +	struct list_head		*items,
> +	unsigned int			count,
> +	bool				sort)
> +{
> +	struct xfs_mount		*mp = tp->t_mountp;
> +	struct xfs_attri_log_item	*attrip;
> +	struct xfs_attr_item		*attr;
> +
> +	ASSERT(count == 1);
> +
> +	if (!xfs_hasdelattr(mp))
> +		return NULL;
> +
> +	attrip = xfs_attri_init(mp, 0);
> +	if (attrip == NULL)
> +		return NULL;
> +
> +	xfs_trans_add_item(tp, &attrip->attri_item);
> +	list_for_each_entry(attr, items, xattri_list)
> +		xfs_attr_log_item(tp, attrip, attr);
> +	return &attrip->attri_item;
> +}
> +
> +/* Process an attr. */
> +STATIC int
> +xfs_attr_finish_item(
> +	struct xfs_trans		*tp,
> +	struct xfs_log_item		*done,
> +	struct list_head		*item,
> +	struct xfs_btree_cur		**state)
> +{
> +	struct xfs_attr_item		*attr;
> +	struct xfs_attrd_log_item	*done_item = NULL;
> +	int				error;
> +	struct xfs_delattr_context	*dac;
> +
> +	attr = container_of(item, struct xfs_attr_item, xattri_list);
> +	dac = &attr->xattri_dac;
> +	if (done)
> +		done_item = ATTRD_ITEM(done);
> +
> +	/*
> +	 * Corner case that can happen during a recovery.  Because the first
> +	 * iteration of a multi part delay op happens in xfs_attri_item_recover
> +	 * to maintain the order of the log replay items.  But the new
> +	 * transactions do not automatically rejoin during a recovery as they do
> +	 * in a standard delay op, so we need to catch this here and rejoin the
> +	 * leaf to the new transaction
> +	 */
> +	if (attr->xattri_dac.leaf_bp &&
> +	    attr->xattri_dac.leaf_bp->b_transp != tp) {
> +		xfs_trans_bjoin(tp, attr->xattri_dac.leaf_bp);
> +		xfs_trans_bhold(tp, attr->xattri_dac.leaf_bp);
> +	}
> +
> +	/*
> +	 * Always reset trans after EAGAIN cycle
> +	 * since the transaction is new
> +	 */
> +	dac->da_args->trans = tp;
> +
> +	error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
> +			       attr->xattri_op_flags);
> +	if (error != -EAGAIN)
> +		kmem_free(attr);
> +
> +	return error;
> +}
> +
> +/* Abort all pending ATTRs. */
> +STATIC void
> +xfs_attr_abort_intent(
> +	struct xfs_log_item		*intent)
> +{
> +	xfs_attri_release(ATTRI_ITEM(intent));
> +}
> +
> +/* Cancel an attr */
> +STATIC void
> +xfs_attr_cancel_item(
> +	struct list_head		*item)
> +{
> +	struct xfs_attr_item		*attr;
> +
> +	attr = container_of(item, struct xfs_attr_item, xattri_list);
> +	kmem_free(attr);
> +}
> +
> +STATIC xfs_lsn_t
> +xfs_attri_item_committed(
> +	struct xfs_log_item		*lip,
> +	xfs_lsn_t			lsn)
> +{
> +	struct xfs_attri_log_item	*attrip;
> +	/*
> +	 * The attrip refers to xfs_attr_item memory to log the name and value
> +	 * with the intent item. This already occurred when the intent was
> +	 * committed so these fields are no longer accessed. Clear them out of
 +	 * caution since we're about to free the xfs_attr_item.
> +	 */
> +	attrip = ATTRI_ITEM(lip);
> +	attrip->attri_name = NULL;
> +	attrip->attri_value = NULL;
> +
> +	/*
> +	 * The ATTRI is logged only once and cannot be moved in the log, so
> +	 * simply return the lsn at which it's been logged.
> +	 */
> +	return lsn;
> +}
> +
> +STATIC bool
> +xfs_attri_item_match(
> +	struct xfs_log_item	*lip,
> +	uint64_t		intent_id)
> +{
> +	return ATTRI_ITEM(lip)->attri_format.alfi_id == intent_id;
> +}
> +
> +/*
> + * This routine is called to allocate an "attr free done" log item.
> + */
> +struct xfs_attrd_log_item *
> +xfs_trans_get_attrd(struct xfs_trans		*tp,
> +		  struct xfs_attri_log_item	*attrip)
> +{
> +	struct xfs_attrd_log_item		*attrdp;
> +	uint					size;
> +
> +	ASSERT(tp != NULL);
> +
> +	size = sizeof(struct xfs_attrd_log_item);
> +	attrdp = kmem_zalloc(size, 0);
> +
> +	xfs_log_item_init(tp->t_mountp, &attrdp->attrd_item, XFS_LI_ATTRD,
> +			  &xfs_attrd_item_ops);
> +	attrdp->attrd_attrip = attrip;
> +	attrdp->attrd_format.alfd_alf_id = attrip->attri_format.alfi_id;
> +
> +	xfs_trans_add_item(tp, &attrdp->attrd_item);
> +	return attrdp;
> +}
> +
> +static const struct xfs_item_ops xfs_attrd_item_ops = {
> +	.flags		= XFS_ITEM_RELEASE_WHEN_COMMITTED,
> +	.iop_size	= xfs_attrd_item_size,
> +	.iop_format	= xfs_attrd_item_format,
> +	.iop_release    = xfs_attrd_item_release,
> +};
> +
> +
> +/* Get an ATTRD so we can process all the attrs. */
> +static struct xfs_log_item *
> +xfs_attr_create_done(
> +	struct xfs_trans		*tp,
> +	struct xfs_log_item		*intent,
> +	unsigned int			count)
> +{
> +	if (!intent)
> +		return NULL;
> +
> +	return &xfs_trans_get_attrd(tp, ATTRI_ITEM(intent))->attrd_item;
> +}
> +
> +const struct xfs_defer_op_type xfs_attr_defer_type = {
> +	.max_items	= 1,
> +	.create_intent	= xfs_attr_create_intent,
> +	.abort_intent	= xfs_attr_abort_intent,
> +	.create_done	= xfs_attr_create_done,
> +	.finish_item	= xfs_attr_finish_item,
> +	.cancel_item	= xfs_attr_cancel_item,
> +};
> +
> +/*
> + * Process an attr intent item that was recovered from the log.  We need to
> + * delete the attr that it describes.
> + */
> +STATIC int
> +xfs_attri_item_recover(
> +	struct xfs_log_item		*lip,
> +	struct list_head		*capture_list)
> +{
> +	struct xfs_attri_log_item	*attrip = ATTRI_ITEM(lip);
> +	struct xfs_attr_item		*new_attr;
> +	struct xfs_mount		*mp = lip->li_mountp;
> +	struct xfs_inode		*ip;
> +	struct xfs_da_args		args;
> +	struct xfs_da_args		*new_args;
> +	struct xfs_trans_res		tres;
> +	bool				rsvd;
> +	struct xfs_attri_log_format	*attrp;
> +	int				error;
> +	int				total;
> +	int				local;
> +	struct xfs_attrd_log_item	*done_item = NULL;
> +	struct xfs_attr_item		attr = {
> +		.xattri_op_flags	= attrip->attri_format.alfi_op_flags,
> +		.xattri_dac.da_args	= &args,
> +	};
> +
> +	/*
> +	 * First check the validity of the attr described by the ATTRI.  If any
> +	 * are bad, then assume that all are bad and just toss the ATTRI.
> +	 */
> +	attrp = &attrip->attri_format;
> +	if (!(attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET ||
> +	      attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE) ||
> +	    (attrp->alfi_value_len > XATTR_SIZE_MAX) ||
> +	    (attrp->alfi_name_len > XATTR_NAME_MAX) ||
> +	    (attrp->alfi_name_len == 0) ||
> +	    xfs_verify_ino(mp, attrp->alfi_ino) == false ||

Please put this validation logic in a separate predicate.

It probably ought to ensure that there aren't illegal characters in the
attr name too.

> +	    !xfs_hasdelattr(mp)) {

Weird nit: If you mount with -o delattr, crash, and remount without that
mount option, we'll report that as a corruption error.

Not sure what we want to do about that, it's a rough edge but it's also
an experimental feature.

> +		return -EFSCORRUPTED;
> +	}
> +
> +	error = xfs_iget(mp, 0, attrp->alfi_ino, 0, 0, &ip);
> +	if (error)
> +		return error;
> +
> +	if (VFS_I(ip)->i_nlink == 0)
> +		xfs_iflags_set(ip, XFS_IRECOVERY);

> +
> +	memset(&args, 0, sizeof(struct xfs_da_args));
> +	args.dp = ip;
> +	args.geo = mp->m_attr_geo;
> +	args.op_flags = attrp->alfi_op_flags;
> +	args.whichfork = XFS_ATTR_FORK;
> +	args.name = attrip->attri_name;
> +	args.namelen = attrp->alfi_name_len;
> +	args.hashval = xfs_da_hashname(args.name, args.namelen);
> +	args.attr_filter = attrp->alfi_attr_flags;
> +
> +	if (attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET) {
> +		args.value = attrip->attri_value;
> +		args.valuelen = attrp->alfi_value_len;
> +		args.total = xfs_attr_calc_size(&args, &local);
> +
> +		tres.tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
> +				 M_RES(mp)->tr_attrsetrt.tr_logres *
> +					args.total;
> +		tres.tr_logcount = XFS_ATTRSET_LOG_COUNT;
> +		tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
> +		total = args.total;
> +	} else {
> +		tres = M_RES(mp)->tr_attrrm;
> +		total = XFS_ATTRRM_SPACE_RES(mp);
> +	}

Isn't there already code in xfs_attr.c that does this?

> +	error = xfs_trans_alloc(mp, &tres, total, 0,
> +				rsvd ? XFS_TRANS_RESERVE : 0, &args.trans);
> +	if (error)
> +		return error;
> +
> +	done_item = xfs_trans_get_attrd(args.trans, attrip);
> +
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(args.trans, ip, 0);
> +
> +	error = xfs_trans_attr(&attr.xattri_dac, done_item,
> +			       &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
> +	if (error == -EAGAIN) {
> +		/*
> +		 * There's more work to do, so make a new xfs_attr_item and add
> +		 * it to this transaction.  We dont use xfs_attr_item_init here
> +		 * because we need the info stored in the current attr to
> +		 * continue with this multi-part operation.  So, alloc space
> +		 * for it and the args and copy everything there.
> +		 */
> +		new_attr = kmem_zalloc(sizeof(struct xfs_attr_item) +
> +				       sizeof(struct xfs_da_args), KM_NOFS);
> +		new_args = (struct xfs_da_args *)((char *)new_attr +
> +			   sizeof(struct xfs_attr_item));
> +
> +		memcpy(new_args, &args, sizeof(struct xfs_da_args));
> +		memcpy(new_attr, &attr, sizeof(struct xfs_attr_item));
> +
> +		new_attr->xattri_dac.da_args = new_args;
> +		memset(&new_attr->xattri_list, 0, sizeof(struct list_head));
> +
> +		xfs_defer_add(args.trans, XFS_DEFER_OPS_TYPE_ATTR,
> +			      &new_attr->xattri_list);
> +
> +		/* Do not send -EAGAIN back to caller */
> +		error = 0;
> +	} else if (error) {
> +		xfs_trans_cancel(args.trans);
> +		goto out;
> +	}
> +
> +	xfs_defer_ops_capture_and_commit(args.trans, ip, capture_list);
> +
> +out:
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	xfs_irele(ip);
> +	return error;
> +}
> +
> +/* Relog an intent item to push the log tail forward. */
> +static struct xfs_log_item *
> +xfs_attri_item_relog(
> +	struct xfs_log_item		*intent,
> +	struct xfs_trans		*tp)
> +{
> +	struct xfs_attrd_log_item	*attrdp;
> +	struct xfs_attri_log_item	*old_attrip;
> +	struct xfs_attri_log_item	*new_attrip;
> +	struct xfs_attri_log_format	*new_attrp;
> +	struct xfs_attri_log_format	*old_attrp;
> +	int				buffer_size;
> +
> +	old_attrip = ATTRI_ITEM(intent);
> +	old_attrp = &old_attrip->attri_format;
> +	buffer_size = old_attrp->alfi_value_len + old_attrp->alfi_name_len;
> +
> +	tp->t_flags |= XFS_TRANS_DIRTY;
> +	attrdp = xfs_trans_get_attrd(tp, old_attrip);
> +	set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
> +
> +	new_attrip = xfs_attri_init(tp->t_mountp, buffer_size);
> +	new_attrp = &new_attrip->attri_format;
> +
> +	new_attrp->alfi_ino = old_attrp->alfi_ino;
> +	new_attrp->alfi_op_flags = old_attrp->alfi_op_flags;
> +	new_attrp->alfi_value_len = old_attrp->alfi_value_len;
> +	new_attrp->alfi_name_len = old_attrp->alfi_name_len;
> +	new_attrp->alfi_attr_flags = old_attrp->alfi_attr_flags;
> +
> +	new_attrip->attri_name_len = old_attrip->attri_name_len;
> +	new_attrip->attri_name = ((char *)new_attrip) +
> +				 sizeof(struct xfs_attri_log_item);
> +	memcpy(new_attrip->attri_name, old_attrip->attri_name,
> +		new_attrip->attri_name_len);
> +
> +	new_attrip->attri_value_len = old_attrip->attri_value_len;
> +	if (new_attrip->attri_value_len > 0) {
> +		new_attrip->attri_value = new_attrip->attri_name +
> +					  new_attrip->attri_name_len;
> +
> +		memcpy(new_attrip->attri_value, old_attrip->attri_value,
> +		       new_attrip->attri_value_len);
> +	}
> +
> +	xfs_trans_add_item(tp, &new_attrip->attri_item);
> +	set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags);
> +
> +	return &new_attrip->attri_item;
> +}
> +
> +static const struct xfs_item_ops xfs_attri_item_ops = {
> +	.iop_size	= xfs_attri_item_size,
> +	.iop_format	= xfs_attri_item_format,
> +	.iop_unpin	= xfs_attri_item_unpin,
> +	.iop_committed	= xfs_attri_item_committed,
> +	.iop_release    = xfs_attri_item_release,
> +	.iop_recover	= xfs_attri_item_recover,
> +	.iop_match	= xfs_attri_item_match,
> +	.iop_relog	= xfs_attri_item_relog,
> +};
> +
> +
> +
> +STATIC int
> +xlog_recover_attri_commit_pass2(
> +	struct xlog                     *log,
> +	struct list_head		*buffer_list,
> +	struct xlog_recover_item        *item,
> +	xfs_lsn_t                       lsn)
> +{
> +	int                             error;
> +	struct xfs_mount                *mp = log->l_mp;
> +	struct xfs_attri_log_item       *attrip;
> +	struct xfs_attri_log_format     *attri_formatp;
> +	char				*name = NULL;
> +	char				*value = NULL;
> +	int				region = 0;
> +	int				buffer_size;
> +
> +	attri_formatp = item->ri_buf[region].i_addr;
> +
> +	/* Validate xfs_attri_log_format */
> +	if (attri_formatp->__pad != 0 || attri_formatp->alfi_name_len == 0 ||
> +	    (attri_formatp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE &&
> +	    attri_formatp->alfi_value_len != 0))
> +		return -EFSCORRUPTED;

Might be worth an XFS_ERROR_REPORT here since this is evidence of incore
memory corruption, right?

> +
> +	buffer_size = attri_formatp->alfi_name_len +
> +		      attri_formatp->alfi_value_len;
> +
> +	attrip = xfs_attri_init(mp, buffer_size);
> +	if (attrip == NULL)
> +		return -ENOMEM;
> +
> +	error = xfs_attri_copy_format(&item->ri_buf[region],
> +				      &attrip->attri_format);
> +	if (error) {
> +		xfs_attri_item_free(attrip);
> +		return error;
> +	}
> +
> +	attrip->attri_name_len = attri_formatp->alfi_name_len;
> +	attrip->attri_value_len = attri_formatp->alfi_value_len;
> +	region++;
> +	name = ((char *)attrip) + sizeof(struct xfs_attri_log_item);
> +	memcpy(name, item->ri_buf[region].i_addr, attrip->attri_name_len);
> +	attrip->attri_name = name;
> +
> +	if (attrip->attri_value_len > 0) {
> +		region++;
> +		value = ((char *)attrip) + sizeof(struct xfs_attri_log_item) +
> +			attrip->attri_name_len;
> +		memcpy(value, item->ri_buf[region].i_addr,
> +			attrip->attri_value_len);
> +		attrip->attri_value = value;
> +	}
> +
> +	/*
> +	 * The ATTRI has two references. One for the ATTRD and one for ATTRI to
> +	 * ensure it makes it into the AIL. Insert the ATTRI into the AIL
> +	 * directly and drop the ATTRI reference. Note that
> +	 * xfs_trans_ail_update() drops the AIL lock.
> +	 */
> +	xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn);
> +	xfs_attri_release(attrip);
> +	return 0;
> +}
> +
> +const struct xlog_recover_item_ops xlog_attri_item_ops = {
> +	.item_type	= XFS_LI_ATTRI,
> +	.commit_pass2	= xlog_recover_attri_commit_pass2,
> +};
> +
> +/*
> + * This routine is called when an ATTRD format structure is found in a committed
> + * transaction in the log. Its purpose is to cancel the corresponding ATTRI if
> + * it was still in the log. To do this it searches the AIL for the ATTRI with
> + * an id equal to that in the ATTRD format structure. If we find it we drop
> + * the ATTRD reference, which removes the ATTRI from the AIL and frees it.
> + */
> +STATIC int
> +xlog_recover_attrd_commit_pass2(
> +	struct xlog			*log,
> +	struct list_head		*buffer_list,
> +	struct xlog_recover_item	*item,
> +	xfs_lsn_t			lsn)
> +{
> +	struct xfs_attrd_log_format	*attrd_formatp;
> +
> +	attrd_formatp = item->ri_buf[0].i_addr;
> +	ASSERT((item->ri_buf[0].i_len ==
> +				(sizeof(struct xfs_attrd_log_format))));
> +
> +	xlog_recover_release_intent(log, XFS_LI_ATTRI,
> +				    attrd_formatp->alfd_alf_id);
> +	return 0;
> +}
> +
> +const struct xlog_recover_item_ops xlog_attrd_item_ops = {
> +	.item_type	= XFS_LI_ATTRD,
> +	.commit_pass2	= xlog_recover_attrd_commit_pass2,
> +};
> diff --git a/fs/xfs/xfs_attr_item.h b/fs/xfs/xfs_attr_item.h
> new file mode 100644
> index 0000000..27c6bae
> --- /dev/null
> +++ b/fs/xfs/xfs_attr_item.h
> @@ -0,0 +1,52 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright (C) 2019 Oracle.  All Rights Reserved.
> + * Author: Allison Collins <allison.henderson@oracle.com>
> + */
> +#ifndef	__XFS_ATTR_ITEM_H__
> +#define	__XFS_ATTR_ITEM_H__
> +
> +/* kernel only ATTRI/ATTRD definitions */
> +
> +struct xfs_mount;
> +struct kmem_zone;
> +
> +/*
> + * Define ATTR flag bits. Manipulated by set/clear/test_bit operators.
> + */
> +#define	XFS_ATTRI_RECOVERED	1
> +
> +
> +/*
> + * This is the "attr intention" log item.  It is used to log the fact that some

Well, it's the incore state tracking for attr intent log items... :)

Aside from the nits I 've pointed out so far, this looks more or less ok
to me.

--------------

Ok, so here we are at the end.  Looking at my own atomic extent swap
series, I broke this up into two pieces -- one to create the barebones
log item (with dummy implementations); and a second one to create the
defer ops code and connect it to the log item.

Oh, heh.  The first patch is 16K and the second one is 47K.

That might be a good way to break *this* patch into smaller parts, if
nothing else -- concentrate on getting the log parts working, then
connect the new state machine to defer ops and log items.

That said, as this series gets longer and longer I find it really more
difficult to go through the whole series one by one vs. just diffing the
whole branch and reviewing that.

<shrug> I don't really have a definitive answer for which is better.
The xattr code is very complex, and I struggle even combining both of my
usual strategies and attacking review from both ends.

By the way, have you been stress testing the xattr code with all this
stuff applied?  At some point it becomes easier to pull this in and fix
up the breakage than it is to review 22 slice-n-dice patches every cycle.

--D

> + * attribute operations need to be processed.  An operation is currently either
> + * a set or remove.  Set or remove operations are described by the xfs_attr_item
> + * which may be logged to this intent.
> + *
> + * During a normal attr operation, name and value point to the name and value
> + * feilds of the calling functions xfs_da_args.  During a recovery, the name
> + * and value buffers are copied from the log, and stored in a trailing buffer
> + * attached to the xfs_attr_item until they are committed.  They are freed when
> + * the xfs_attr_item itself is freed when the work is done.
> + */
> +struct xfs_attri_log_item {
> +	struct xfs_log_item		attri_item;
> +	atomic_t			attri_refcount;
> +	int				attri_name_len;
> +	int				attri_value_len;
> +	void				*attri_name;
> +	void				*attri_value;
> +	struct xfs_attri_log_format	attri_format;
> +};
> +
> +/*
> + * This is the "attr done" log item.  It is used to log the fact that some attrs
> + * earlier mentioned in an attri item have been freed.
> + */
> +struct xfs_attrd_log_item {
> +	struct xfs_attri_log_item	*attrd_attrip;
> +	struct xfs_log_item		attrd_item;
> +	struct xfs_attrd_log_format	attrd_format;
> +};
> +
> +#endif	/* __XFS_ATTR_ITEM_H__ */
> diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
> index 8f8837f..d7787a5 100644
> --- a/fs/xfs/xfs_attr_list.c
> +++ b/fs/xfs/xfs_attr_list.c
> @@ -15,6 +15,7 @@
>  #include "xfs_inode.h"
>  #include "xfs_trans.h"
>  #include "xfs_bmap.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_attr_sf.h"
>  #include "xfs_attr_leaf.h"
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index 248083e..6682936 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -15,6 +15,8 @@
>  #include "xfs_iwalk.h"
>  #include "xfs_itable.h"
>  #include "xfs_error.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_bmap.h"
>  #include "xfs_bmap_util.h"
> diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
> index c1771e7..62e1534 100644
> --- a/fs/xfs/xfs_ioctl32.c
> +++ b/fs/xfs/xfs_ioctl32.c
> @@ -17,6 +17,8 @@
>  #include "xfs_itable.h"
>  #include "xfs_fsops.h"
>  #include "xfs_rtalloc.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_ioctl.h"
>  #include "xfs_ioctl32.h"
> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> index 00369502f..ce04721 100644
> --- a/fs/xfs/xfs_iops.c
> +++ b/fs/xfs/xfs_iops.c
> @@ -13,6 +13,8 @@
>  #include "xfs_inode.h"
>  #include "xfs_acl.h"
>  #include "xfs_quota.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_trans.h"
>  #include "xfs_trace.h"
> diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
> index 0604183..290e57b 100644
> --- a/fs/xfs/xfs_log.c
> +++ b/fs/xfs/xfs_log.c
> @@ -2070,6 +2070,10 @@ xlog_print_tic_res(
>  	    REG_TYPE_STR(CUD_FORMAT, "cud_format"),
>  	    REG_TYPE_STR(BUI_FORMAT, "bui_format"),
>  	    REG_TYPE_STR(BUD_FORMAT, "bud_format"),
> +	    REG_TYPE_STR(ATTRI_FORMAT, "attri_format"),
> +	    REG_TYPE_STR(ATTRD_FORMAT, "attrd_format"),
> +	    REG_TYPE_STR(ATTR_NAME, "attr_name"),
> +	    REG_TYPE_STR(ATTR_VALUE, "attr_value"),
>  	};
>  	BUILD_BUG_ON(ARRAY_SIZE(res_type_str) != XLOG_REG_TYPE_MAX + 1);
>  #undef REG_TYPE_STR
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index 295a5c6..c0821b6 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -1775,6 +1775,8 @@ static const struct xlog_recover_item_ops *xlog_recover_item_ops[] = {
>  	&xlog_cud_item_ops,
>  	&xlog_bui_item_ops,
>  	&xlog_bud_item_ops,
> +	&xlog_attri_item_ops,
> +	&xlog_attrd_item_ops,
>  };
>  
>  static const struct xlog_recover_item_ops *
> diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
> index 0aa87c21..bc9c25e 100644
> --- a/fs/xfs/xfs_ondisk.h
> +++ b/fs/xfs/xfs_ondisk.h
> @@ -132,6 +132,8 @@ xfs_check_ondisk_structs(void)
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format,	56);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat,	20);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header,		16);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
>  
>  	/*
>  	 * The v5 superblock format extended several v4 header structures with
> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> index bca48b3..9b0c790 100644
> --- a/fs/xfs/xfs_xattr.c
> +++ b/fs/xfs/xfs_xattr.c
> @@ -10,6 +10,7 @@
>  #include "xfs_log_format.h"
>  #include "xfs_da_format.h"
>  #include "xfs_inode.h"
> +#include "xfs_da_btree.h"
>  #include "xfs_attr.h"
>  #include "xfs_acl.h"
>  #include "xfs_da_btree.h"
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args
  2021-02-18 16:53 ` [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args Allison Henderson
@ 2021-02-26  4:58   ` Darrick J. Wong
  2021-02-27  0:57     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  4:58 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:45AM -0700, Allison Henderson wrote:
> Remove xfs_attr_set_args, xfs_attr_remove_args, and xfs_attr_trans_roll.
> These high level loops are now driven by the delayed operations code,
> and can be removed.
> 
> Additionally collapse in the leaf_bp parameter of xfs_attr_set_iter
> since we only have one caller that passes dac->leaf_bp
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

Looks good to me,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c        | 97 +++--------------------------------------
>  fs/xfs/libxfs/xfs_attr.h        | 10 ++---
>  fs/xfs/libxfs/xfs_attr_remote.c |  1 -
>  fs/xfs/xfs_attr_item.c          |  8 ++--
>  4 files changed, 11 insertions(+), 105 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index cec861e..8b62447 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -63,8 +63,6 @@ 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);
> -int xfs_attr_set_iter(struct xfs_delattr_context *dac,
> -		      struct xfs_buf **leaf_bp);
>  
>  int
>  xfs_inode_hasattr(
> @@ -223,67 +221,13 @@ 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.
> - */
> -STATIC 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);
> -		if (error)
> -			return error;
> -	} else
> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
> -
> -	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_delattr_context	*dac,
> -	struct xfs_buf			**leaf_bp)
> +	struct xfs_delattr_context	*dac)
>  {
>  	struct xfs_da_args		*args = dac->da_args;
>  	struct xfs_inode		*dp = args->dp;
> +	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
>  	int				error = 0;
>  
>  	/*
> @@ -316,7 +260,6 @@ xfs_attr_set_fmt(
>  	 * add.
>  	 */
>  	trace_xfs_attr_set_fmt_return(XFS_DAS_UNINIT, args->dp);
> -	dac->flags |= XFS_DAC_DEFER_FINISH;
>  	return -EAGAIN;
>  }
>  
> @@ -329,10 +272,10 @@ xfs_attr_set_fmt(
>   */
>  int
>  xfs_attr_set_iter(
> -	struct xfs_delattr_context	*dac,
> -	struct xfs_buf			**leaf_bp)
> +	struct xfs_delattr_context	*dac)
>  {
>  	struct xfs_da_args              *args = dac->da_args;
> +	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
>  	struct xfs_inode		*dp = args->dp;
>  	struct xfs_buf			*bp = NULL;
>  	struct xfs_da_state		*state = NULL;
> @@ -344,7 +287,7 @@ xfs_attr_set_iter(
>  	switch (dac->dela_state) {
>  	case XFS_DAS_UNINIT:
>  		if (xfs_attr_is_shortform(dp))
> -			return xfs_attr_set_fmt(dac, leaf_bp);
> +			return xfs_attr_set_fmt(dac);
>  
>  		/*
>  		 * After a shortform to leaf conversion, we need to hold the
> @@ -381,7 +324,6 @@ xfs_attr_set_iter(
>  				 * be a node, so we'll fall down into the node
>  				 * handling code below
>  				 */
> -				dac->flags |= XFS_DAC_DEFER_FINISH;
>  				trace_xfs_attr_set_iter_return(
>  					dac->dela_state, args->dp);
>  				return -EAGAIN;
> @@ -687,32 +629,6 @@ xfs_has_attr(
>  
>  /*
>   * Remove the attribute specified in @args.
> - */
> -int
> -xfs_attr_remove_args(
> -	struct xfs_da_args	*args)
> -{
> -	int				error;
> -	struct xfs_delattr_context	dac = {
> -		.da_args	= 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;
> -}
> -
> -/*
> - * Remove the attribute specified in @args.
>   *
>   * This function may return -EAGAIN to signal that the transaction needs to be
>   * rolled.  Callers should continue calling this function until they receive a
> @@ -1297,7 +1213,6 @@ xfs_attr_node_addname(
>  			 * this. dela_state is still unset by this function at
>  			 * this point.
>  			 */
> -			dac->flags |= XFS_DAC_DEFER_FINISH;
>  			trace_xfs_attr_node_addname_return(
>  					dac->dela_state, args->dp);
>  			return -EAGAIN;
> @@ -1312,7 +1227,6 @@ xfs_attr_node_addname(
>  		error = xfs_da3_split(state);
>  		if (error)
>  			goto out;
> -		dac->flags |= XFS_DAC_DEFER_FINISH;
>  	} else {
>  		/*
>  		 * Addition succeeded, update Btree hashvals.
> @@ -1599,7 +1513,6 @@ xfs_attr_node_removename_iter(
>  			if (error)
>  				goto out;
>  
> -			dac->flags |= XFS_DAC_DEFER_FINISH;
>  			dac->dela_state = XFS_DAS_RM_SHRINK;
>  			trace_xfs_attr_node_removename_iter_return(
>  					dac->dela_state, args->dp);
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 4abf02c..f82c0b1 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -393,9 +393,8 @@ enum xfs_delattr_state {
>  /*
>   * 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*/
> -#define XFS_DAC_DELAYED_OP_INIT		0x04 /* delayed operations init*/
> +#define XFS_DAC_LEAF_ADDNAME_INIT	0x01 /* xfs_attr_leaf_addname init*/
> +#define XFS_DAC_DELAYED_OP_INIT		0x02 /* delayed operations init*/
>  
>  /*
>   * Context used for keeping track of delayed attribute operations
> @@ -452,11 +451,8 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
>  int xfs_attr_get_ilocked(struct xfs_da_args *args);
>  int xfs_attr_get(struct xfs_da_args *args);
>  int xfs_attr_set(struct xfs_da_args *args);
> -int xfs_attr_set_args(struct xfs_da_args *args);
> -int xfs_attr_set_iter(struct xfs_delattr_context *dac,
> -		      struct xfs_buf **leaf_bp);
> +int xfs_attr_set_iter(struct xfs_delattr_context *dac);
>  int xfs_has_attr(struct xfs_da_args *args);
> -int xfs_attr_remove_args(struct xfs_da_args *args);
>  int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>  bool xfs_attr_namecheck(const void *name, size_t length);
>  void xfs_delattr_context_init(struct xfs_delattr_context *dac,
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index b6554a3..78bb552 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -762,7 +762,6 @@ xfs_attr_rmtval_remove(
>  	 * by the parent
>  	 */
>  	if (!done) {
> -		dac->flags |= XFS_DAC_DEFER_FINISH;
>  		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
>  		return -EAGAIN;
>  	}
> diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
> index 8c8f72d..13b289b 100644
> --- a/fs/xfs/xfs_attr_item.c
> +++ b/fs/xfs/xfs_attr_item.c
> @@ -291,7 +291,6 @@ int
>  xfs_trans_attr(
>  	struct xfs_delattr_context	*dac,
>  	struct xfs_attrd_log_item	*attrdp,
> -	struct xfs_buf			**leaf_bp,
>  	uint32_t			op_flags)
>  {
>  	struct xfs_da_args		*args = dac->da_args;
> @@ -304,7 +303,7 @@ xfs_trans_attr(
>  	switch (op_flags) {
>  	case XFS_ATTR_OP_FLAGS_SET:
>  		args->op_flags |= XFS_DA_OP_ADDNAME;
> -		error = xfs_attr_set_iter(dac, leaf_bp);
> +		error = xfs_attr_set_iter(dac);
>  		break;
>  	case XFS_ATTR_OP_FLAGS_REMOVE:
>  		ASSERT(XFS_IFORK_Q(args->dp));
> @@ -428,8 +427,7 @@ xfs_attr_finish_item(
>  	 */
>  	dac->da_args->trans = tp;
>  
> -	error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
> -			       attr->xattri_op_flags);
> +	error = xfs_trans_attr(dac, done_item, attr->xattri_op_flags);
>  	if (error != -EAGAIN)
>  		kmem_free(attr);
>  
> @@ -625,7 +623,7 @@ xfs_attri_item_recover(
>  	xfs_trans_ijoin(args.trans, ip, 0);
>  
>  	error = xfs_trans_attr(&attr.xattri_dac, done_item,
> -			       &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
> +			       attrp->alfi_op_flags);
>  	if (error == -EAGAIN) {
>  		/*
>  		 * There's more work to do, so make a new xfs_attr_item and add
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
  2021-02-18 16:53 ` [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred Allison Henderson
@ 2021-02-26  5:00   ` Darrick J. Wong
  2021-02-27  0:57     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  5:00 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:44AM -0700, Allison Henderson wrote:
> From: Allison Collins <allison.henderson@oracle.com>
> 
> These routines to set up and start a new deferred attribute operations.

"These routine set up and queue a new deferred attribute operation..."?

> These functions are meant to be called by any routine needing to
> initiate a deferred attribute operation as opposed to the existing
> inline operations. New helper function xfs_attr_item_init also added.
> 
> Finally enable delayed attributes in xfs_attr_set and xfs_attr_remove.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

Other than that it seems fine to me,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++--
>  fs/xfs/libxfs/xfs_attr.h |  2 ++
>  2 files changed, 58 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 666cc69..cec861e 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -25,6 +25,7 @@
>  #include "xfs_trans_space.h"
>  #include "xfs_trace.h"
>  #include "xfs_attr_item.h"
> +#include "xfs_attr.h"
>  
>  /*
>   * xfs_attr.c
> @@ -838,9 +839,10 @@ xfs_attr_set(
>  		if (error != -ENOATTR && error != -EEXIST)
>  			goto out_trans_cancel;
>  
> -		error = xfs_attr_set_args(args);
> +		error = xfs_attr_set_deferred(args);
>  		if (error)
>  			goto out_trans_cancel;
> +
>  		/* shortform attribute has already been committed */
>  		if (!args->trans)
>  			goto out_unlock;
> @@ -849,7 +851,7 @@ xfs_attr_set(
>  		if (error != -EEXIST)
>  			goto out_trans_cancel;
>  
> -		error = xfs_attr_remove_args(args);
> +		error = xfs_attr_remove_deferred(args);
>  		if (error)
>  			goto out_trans_cancel;
>  	}
> @@ -879,6 +881,58 @@ xfs_attr_set(
>  	goto out_unlock;
>  }
>  
> +STATIC int
> +xfs_attr_item_init(
> +	struct xfs_da_args	*args,
> +	unsigned int		op_flags,	/* op flag (set or remove) */
> +	struct xfs_attr_item	**attr)		/* new xfs_attr_item */
> +{
> +
> +	struct xfs_attr_item	*new;
> +
> +	new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS);
> +	new->xattri_op_flags = op_flags;
> +	new->xattri_dac.da_args = args;
> +
> +	*attr = new;
> +	return 0;
> +}
> +
> +/* Sets an attribute for an inode as a deferred operation */
> +int
> +xfs_attr_set_deferred(
> +	struct xfs_da_args	*args)
> +{
> +	struct xfs_attr_item	*new;
> +	int			error = 0;
> +
> +	error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new);
> +	if (error)
> +		return error;
> +
> +	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
> +
> +	return 0;
> +}
> +
> +/* Removes an attribute for an inode as a deferred operation */
> +int
> +xfs_attr_remove_deferred(
> +	struct xfs_da_args	*args)
> +{
> +
> +	struct xfs_attr_item	*new;
> +	int			error;
> +
> +	error  = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new);
> +	if (error)
> +		return error;
> +
> +	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
> +
> +	return 0;
> +}
> +
>  /*========================================================================
>   * External routines when attribute list is inside the inode
>   *========================================================================*/
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index ee79763..4abf02c 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -462,5 +462,7 @@ 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);
>  int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
> +int xfs_attr_set_deferred(struct xfs_da_args *args);
> +int xfs_attr_remove_deferred(struct xfs_da_args *args);
>  
>  #endif	/* __XFS_ATTR_H__ */
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs
  2021-02-18 16:53 ` [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs Allison Henderson
@ 2021-02-26  5:02   ` Darrick J. Wong
  2021-02-27  0:56     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  5:02 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:43AM -0700, Allison Henderson wrote:
> This is a clean up patch that skips the flip flag logic for delayed attr
> renames.  Since the log replay keeps the inode locked, we do not need to
> worry about race windows with attr lookups.  So we can skip over
> flipping the flag and the extra transaction roll for it
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

I wonder, have you done much performance analysis of the old vs. new
xattr code paths?  Does skipping the extra step + roll make attr
operations faster?

This looks pretty straightforward though:
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c      | 51 +++++++++++++++++++++++++------------------
>  fs/xfs/libxfs/xfs_attr_leaf.c |  3 ++-
>  2 files changed, 32 insertions(+), 22 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index e4c1b4b..666cc69 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -337,6 +337,7 @@ xfs_attr_set_iter(
>  	struct xfs_da_state		*state = NULL;
>  	int				forkoff, error = 0;
>  	int				retval = 0;
> +	struct xfs_mount		*mp = args->dp->i_mount;
>  
>  	/* State machine switch */
>  	switch (dac->dela_state) {
> @@ -470,16 +471,21 @@ xfs_attr_set_iter(
>  		 * "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.
> -		 */
> -		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> -		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
> -		return -EAGAIN;
> +		if (!xfs_hasdelattr(mp)) {
> +			error = xfs_attr3_leaf_flipflags(args);
> +			if (error)
> +				return error;
> +			/*
> +			 * Commit the flag value change and start the next trans
> +			 * in series.
> +			 */
> +			dac->dela_state = XFS_DAS_FLIP_LFLAG;
> +			trace_xfs_attr_set_iter_return(dac->dela_state,
> +						       args->dp);
> +			return -EAGAIN;
> +		}
> +
> +		/* fallthrough */
>  	case XFS_DAS_FLIP_LFLAG:
>  		/*
>  		 * Dismantle the "old" attribute/value pair by removing a
> @@ -588,17 +594,21 @@ xfs_attr_set_iter(
>  		 * 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
> -		 */
> -		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> -		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
> -		return -EAGAIN;
> +		if (!xfs_hasdelattr(mp)) {
> +			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;
> +			trace_xfs_attr_set_iter_return(dac->dela_state,
> +						       args->dp);
> +			return -EAGAIN;
> +		}
>  
> +		/* fallthrough */
>  	case XFS_DAS_FLIP_NFLAG:
>  		/*
>  		 * Dismantle the "old" attribute/value pair by removing a
> @@ -1277,7 +1287,6 @@ int xfs_attr_node_addname_work(
>  	 * 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.
>  	 */
> -	args->attr_filter |= XFS_ATTR_INCOMPLETE;
>  	state = xfs_da_state_alloc(args);
>  	state->inleaf = 0;
>  	error = xfs_da3_node_lookup_int(state, &retval);
> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
> index 3780141..ec707bd 100644
> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
> @@ -1486,7 +1486,8 @@ xfs_attr3_leaf_add_work(
>  	if (tmp)
>  		entry->flags |= XFS_ATTR_LOCAL;
>  	if (args->op_flags & XFS_DA_OP_RENAME) {
> -		entry->flags |= XFS_ATTR_INCOMPLETE;
> +		if (!xfs_hasdelattr(mp))
> +			entry->flags |= XFS_ATTR_INCOMPLETE;
>  		if ((args->blkno2 == args->blkno) &&
>  		    (args->index2 <= args->index)) {
>  			args->index2++;
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
  2021-02-18 16:53 ` [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans Allison Henderson
@ 2021-02-26  5:06   ` Darrick J. Wong
  2021-02-27  0:57     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  5:06 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:41AM -0700, Allison Henderson wrote:
> Because xattrs can be over a page in size, we need to handle possible
> krealloc errors to avoid warnings
> 
> The warning:
>    WARNING: CPU: 1 PID: 20255 at mm/page_alloc.c:3446
>                  get_page_from_freelist+0x100b/0x1690
> 
> is caused when sizes larger that a page are allocated with the
> __GFP_NOFAIL flag option.  We encounter this error now because attr
> values can be up to 64k in size.  So we cannot use __GFP_NOFAIL, and
> we need to handle the error code if the allocation fails.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/xfs_log_recover.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index 97f3130..295a5c6 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -2061,7 +2061,10 @@ xlog_recover_add_to_cont_trans(
>  	old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
>  	old_len = item->ri_buf[item->ri_cnt-1].i_len;
>  
> -	ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL | __GFP_NOFAIL);
> +	ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL);
> +	if (ptr == NULL)
> +		return -ENOMEM;

Given that we update i_addr anyway, perhaps this should fall back to
kmem_alloc_large+memcpy to avoid introducing another failure point?

--D

> +
>  	memcpy(&ptr[old_len], dp, len);
>  	item->ri_buf[item->ri_cnt-1].i_len += len;
>  	item->ri_buf[item->ri_cnt-1].i_addr = ptr;
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 13/22] xfs: Add state machine tracepoints
  2021-02-18 16:53 ` [PATCH v15 13/22] xfs: Add state machine tracepoints Allison Henderson
@ 2021-02-26  5:06   ` Darrick J. Wong
  2021-02-27  0:57     ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-02-26  5:06 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:39AM -0700, Allison Henderson wrote:
> This is a quick patch to add a new tracepoint: xfs_das_state_return.  We
> use this to track when ever a new state is set or -EAGAIN is returned
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

Looks good!
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c        | 31 ++++++++++++++++++++++++++++++-
>  fs/xfs/libxfs/xfs_attr_remote.c |  1 +
>  fs/xfs/xfs_trace.h              | 25 +++++++++++++++++++++++++
>  3 files changed, 56 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index c7b86d5..ba21475 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -313,6 +313,7 @@ xfs_attr_set_fmt(
>  	 * the attr fork to leaf format and will restart with the leaf
>  	 * add.
>  	 */
> +	trace_xfs_attr_set_fmt_return(XFS_DAS_UNINIT, args->dp);
>  	dac->flags |= XFS_DAC_DEFER_FINISH;
>  	return -EAGAIN;
>  }
> @@ -378,6 +379,8 @@ xfs_attr_set_iter(
>  				 * handling code below
>  				 */
>  				dac->flags |= XFS_DAC_DEFER_FINISH;
> +				trace_xfs_attr_set_iter_return(
> +					dac->dela_state, args->dp);
>  				return -EAGAIN;
>  			}
>  			else if (error)
> @@ -400,10 +403,13 @@ xfs_attr_set_iter(
>  				return error;
>  
>  			dac->dela_state = XFS_DAS_FOUND_NBLK;
> +			trace_xfs_attr_set_iter_return(dac->dela_state,
> +						       args->dp);
>  			return -EAGAIN;
>  		}
>  
>  		dac->dela_state = XFS_DAS_FOUND_LBLK;
> +		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>  		return -EAGAIN;
>  
>          case XFS_DAS_FOUND_LBLK:
> @@ -433,6 +439,8 @@ xfs_attr_set_iter(
>  			if (error)
>  				return error;
>  
> +			trace_xfs_attr_set_iter_return(dac->dela_state,
> +						       args->dp);
>  			return -EAGAIN;
>  		}
>  
> @@ -469,6 +477,7 @@ xfs_attr_set_iter(
>  		 * series.
>  		 */
>  		dac->dela_state = XFS_DAS_FLIP_LFLAG;
> +		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>  		return -EAGAIN;
>  	case XFS_DAS_FLIP_LFLAG:
>  		/*
> @@ -488,6 +497,9 @@ xfs_attr_set_iter(
>  	case XFS_DAS_RM_LBLK:
>  		if (args->rmtblkno) {
>  			error = __xfs_attr_rmtval_remove(dac);
> +			if (error == -EAGAIN)
> +				trace_xfs_attr_set_iter_return(
> +					dac->dela_state, args->dp);
>  			if (error)
>  				return error;
>  		}
> @@ -545,6 +557,8 @@ xfs_attr_set_iter(
>  				if (error)
>  					return error;
>  
> +				trace_xfs_attr_set_iter_return(
> +					dac->dela_state, args->dp);
>  				return -EAGAIN;
>  			}
>  
> @@ -581,6 +595,7 @@ xfs_attr_set_iter(
>  		 * series
>  		 */
>  		dac->dela_state = XFS_DAS_FLIP_NFLAG;
> +		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>  		return -EAGAIN;
>  
>  	case XFS_DAS_FLIP_NFLAG:
> @@ -601,6 +616,10 @@ xfs_attr_set_iter(
>  	case XFS_DAS_RM_NBLK:
>  		if (args->rmtblkno) {
>  			error = __xfs_attr_rmtval_remove(dac);
> +			if (error == -EAGAIN)
> +				trace_xfs_attr_set_iter_return(
> +					dac->dela_state, args->dp);
> +
>  			if (error)
>  				return error;
>  		}
> @@ -1214,6 +1233,8 @@ xfs_attr_node_addname(
>  			 * this point.
>  			 */
>  			dac->flags |= XFS_DAC_DEFER_FINISH;
> +			trace_xfs_attr_node_addname_return(
> +					dac->dela_state, args->dp);
>  			return -EAGAIN;
>  		}
>  
> @@ -1394,6 +1415,9 @@ xfs_attr_node_remove_rmt (
>  	 * May return -EAGAIN to request that the caller recall this function
>  	 */
>  	error = __xfs_attr_rmtval_remove(dac);
> +	if (error == -EAGAIN)
> +		trace_xfs_attr_node_remove_rmt_return(dac->dela_state,
> +						      dac->da_args->dp);
>  	if (error)
>  		return error;
>  
> @@ -1513,6 +1537,8 @@ xfs_attr_node_removename_iter(
>  
>  			dac->flags |= XFS_DAC_DEFER_FINISH;
>  			dac->dela_state = XFS_DAS_RM_SHRINK;
> +			trace_xfs_attr_node_removename_iter_return(
> +					dac->dela_state, args->dp);
>  			return -EAGAIN;
>  		}
>  
> @@ -1531,8 +1557,11 @@ xfs_attr_node_removename_iter(
>  		goto out;
>  	}
>  
> -	if (error == -EAGAIN)
> +	if (error == -EAGAIN) {
> +		trace_xfs_attr_node_removename_iter_return(
> +					dac->dela_state, args->dp);
>  		return error;
> +	}
>  out:
>  	if (state)
>  		xfs_da_state_free(state);
> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> index 6af86bf..b242e1a 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -763,6 +763,7 @@ __xfs_attr_rmtval_remove(
>  	 */
>  	if (!done) {
>  		dac->flags |= XFS_DAC_DEFER_FINISH;
> +		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
>  		return -EAGAIN;
>  	}
>  
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index 363e1bf..7993f55 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -3927,6 +3927,31 @@ DEFINE_EVENT(xfs_eofblocks_class, name,	\
>  DEFINE_EOFBLOCKS_EVENT(xfs_ioc_free_eofblocks);
>  DEFINE_EOFBLOCKS_EVENT(xfs_blockgc_free_space);
>  
> +DECLARE_EVENT_CLASS(xfs_das_state_class,
> +	TP_PROTO(int das, struct xfs_inode *ip),
> +	TP_ARGS(das, ip),
> +	TP_STRUCT__entry(
> +		__field(int, das)
> +		__field(xfs_ino_t, ino)
> +	),
> +	TP_fast_assign(
> +		__entry->das = das;
> +		__entry->ino = ip->i_ino;
> +	),
> +	TP_printk("state change %d ino 0x%llx",
> +		  __entry->das, __entry->ino)
> +)
> +
> +#define DEFINE_DAS_STATE_EVENT(name) \
> +DEFINE_EVENT(xfs_das_state_class, name, \
> +	TP_PROTO(int das, struct xfs_inode *ip), \
> +	TP_ARGS(das, ip))
> +DEFINE_DAS_STATE_EVENT(xfs_attr_set_fmt_return);
> +DEFINE_DAS_STATE_EVENT(xfs_attr_set_iter_return);
> +DEFINE_DAS_STATE_EVENT(xfs_attr_node_addname_return);
> +DEFINE_DAS_STATE_EVENT(xfs_attr_node_removename_iter_return);
> +DEFINE_DAS_STATE_EVENT(xfs_attr_node_remove_rmt_return);
> +DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_remove_return);
>  #endif /* _TRACE_XFS_H */
>  
>  #undef TRACE_INCLUDE_PATH
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step
  2021-02-26  3:02   ` Darrick J. Wong
@ 2021-02-27  0:48     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:48 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 8:02 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:29AM -0700, Allison Henderson wrote:
>> This patch hoists transaction handling in xfs_attr_node_removename to
>> xfs_attr_node_remove_step.  This will help keep transaction handling in
>> higher level functions instead of buried in subfunctions when we
>> introduce delay attributes
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> Looks ok,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Great, thank you!
Allison

> 
> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 45 ++++++++++++++++++++++-----------------------
>>   1 file changed, 22 insertions(+), 23 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4e6c89d..3cf76e2 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1251,9 +1251,7 @@ xfs_attr_node_remove_step(
>>   	struct xfs_da_args	*args,
>>   	struct xfs_da_state	*state)
>>   {
>> -	int			retval, error;
>> -	struct xfs_inode	*dp = args->dp;
>> -
>> +	int			error = 0;
>>   
>>   	/*
>>   	 * If there is an out-of-line value, de-allocate the blocks.
>> @@ -1265,25 +1263,6 @@ xfs_attr_node_remove_step(
>>   		if (error)
>>   			return error;
>>   	}
>> -	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)
>> -			return error;
>> -		error = xfs_defer_finish(&args->trans);
>> -		if (error)
>> -			return error;
>> -		/*
>> -		 * Commit the Btree join operation and start a new trans.
>> -		 */
>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>> -		if (error)
>> -			return error;
>> -	}
>>   
>>   	return error;
>>   }
>> @@ -1299,7 +1278,7 @@ xfs_attr_node_removename(
>>   	struct xfs_da_args	*args)
>>   {
>>   	struct xfs_da_state	*state = NULL;
>> -	int			error;
>> +	int			retval, error;
>>   	struct xfs_inode	*dp = args->dp;
>>   
>>   	trace_xfs_attr_node_removename(args);
>> @@ -1312,6 +1291,26 @@ xfs_attr_node_removename(
>>   	if (error)
>>   		goto out;
>>   
>> +	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.
>> +		 */
>> +		error = xfs_trans_roll_inode(&args->trans, dp);
>> +		if (error)
>> +			goto out;
>> +	}
>> +
>>   	/*
>>   	 * If the result is small enough, push it all into the inode.
>>   	 */
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup
  2021-02-26  3:00   ` Darrick J. Wong
@ 2021-02-27  0:48     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:48 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 8:00 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:28AM -0700, Allison Henderson wrote:
>> 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>
> 
> Looks ok,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Ok, thanks!
Allison

> 
> --D
> 
>> ---
>>   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 28ff93d..4e6c89d 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -1220,6 +1220,25 @@ xfs_attr_node_remove_rmt(
>>   	return xfs_attr_refillstate(state);
>>   }
>>   
>> +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.
>>    *
>> @@ -1232,7 +1251,6 @@ xfs_attr_node_remove_step(
>>   	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;
>>   
>> @@ -1247,14 +1265,7 @@ xfs_attr_node_remove_step(
>>   		if (error)
>>   			return error;
>>   	}
>> -
>> -	/*
>> -	 * 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform
  2021-02-26  3:03   ` Darrick J. Wong
@ 2021-02-27  0:48     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:48 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 8:03 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:30AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> Whoah, /removing/ a function! :)
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Thanks!  Yeah, in this series we do flatten up a lot of helper functions 
with the goal of getting all the states into a single function.

Allison
> 
> --D
> 
>> ---
>>   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 3cf76e2..a064c5b 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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt
  2021-02-26  3:07   ` Darrick J. Wong
@ 2021-02-27  0:49     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:49 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 8:07 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:31AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 77 +++++++++++++++++++++++++++---------------------
>>   1 file changed, 44 insertions(+), 33 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index a064c5b..205ad26 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -216,6 +216,46 @@ 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);
> 
> Shouldn't this pass the error back to the caller?
> 
> --D
Yes, I must of have missed it in this temporary phase of this function. 
  It quickly gets pulled back out when the defer_finishes go away, but 
will fix for this phase in the series.  Thanks for the catch!

Allison

> 
>> +
>> +	return -EAGAIN;
>> +}
>> +
>>   /*
>>    * Set the attribute specified in @args.
>>    */
>> @@ -224,8 +264,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 +273,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)
>> +		error = xfs_attr_set_fmt(args);
>> +		if (error != -EAGAIN)
>>   			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;
>> -		}
>>   	}
>>   
>>   	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
>> @@ -297,8 +309,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-02-26  4:02   ` Darrick J. Wong
@ 2021-02-27  0:54     ` Allison Henderson
  2021-03-01 18:00       ` Darrick J. Wong
  0 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:54 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 9:02 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:32AM -0700, 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>
>> ---
>>   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 205ad26..bee8d3fb 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_work(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);
>> @@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
>>   			return error;
>>   	}
>>   
>> +	error = xfs_attr_node_addname_work(args);
>> +out:
>> +	if (state)
>> +		xfs_da_state_free(state);
>> +	if (error)
>> +		return error;
>> +	return retval;
>> +}
>> +
>> +
>> +STATIC
>> +int xfs_attr_node_addname_work(
> 
> What, erm, work does this function do?  Since it survives to the end of
> the patchset, I think this needs a better name (or at least needs a
> comment about what it's actually supposed to do).
To directly answer the question: it's here to help xfs_attr_set_iter not 
be any bigger than it has to. I think we likely struggled with the name 
because it's almost like it's just the "remainder" of the operation that 
doesnt need state management

> 
> AFAICT you're splitting node_addname() into two functions because we're
> at a transaction roll point, and this "_work" function exists to remove
> the copy of the xattr key that has the "INCOMPLETE" bit set (aka the old
> one), right?
Thats about right. Maybe just a quick comment?
/*
  * Removes the old xattr key marked with the INCOMPLETE bit
  */

I suppose we could consider something like 
"xfs_attr_node_addname_remv_incomplete"?  Or 
xfs_attr_node_addname_cleanup? Trying to cram it into the name maybe 
getting a bit wordy too.

Allison
> 
> --D
> 
>> +	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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr
  2021-02-26  4:06   ` Darrick J. Wong
@ 2021-02-27  0:54     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:54 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 9:06 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:33AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> Looks ok to me,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Thanks!

Allison
> 
> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 80 ++++++++++++++++++++++++++++++------------------
>>   1 file changed, 51 insertions(+), 29 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index bee8d3fb..4333b61 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_work(struct xfs_da_args *args);
>>   STATIC int xfs_attr_node_hasname(xfs_da_args_t *args,
>> @@ -265,6 +268,7 @@ xfs_attr_set_args(
>>   	struct xfs_da_args	*args)
>>   {
>>   	struct xfs_inode	*dp = args->dp;
>> +	struct xfs_da_state     *state;
>>   	int			error;
>>   
>>   	/*
>> @@ -310,7 +314,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;
>>   }
>>   
>>   /*
>> @@ -883,42 +894,21 @@ 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;
>>   
>> -	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;
>>   	if (retval == -EEXIST) {
>> @@ -941,6 +931,38 @@ xfs_attr_node_addname(
>>   		args->rmtvaluelen = 0;
>>   	}
>>   
>> +	return 0;
>> +out:
>> +	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) {
>> @@ -966,7 +988,7 @@ xfs_attr_node_addname(
>>   			if (error)
>>   				goto out;
>>   
>> -			goto restart;
>> +			return -EAGAIN;
>>   		}
>>   
>>   		/*
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 21/22] xfs: Add delattr mount option
  2021-02-26  4:29   ` Darrick J. Wong
@ 2021-02-27  0:55     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:55 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 9:29 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:47AM -0700, Allison Henderson wrote:
>> This patch adds a mount option to enable delayed attributes. Eventually
>> this can be removed when delayed attrs becomes permanent.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.h | 2 +-
>>   fs/xfs/xfs_mount.h       | 1 +
>>   fs/xfs/xfs_super.c       | 6 +++++-
>>   fs/xfs/xfs_xattr.c       | 2 ++
>>   4 files changed, 9 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index f82c0b1..35f3a53 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -30,7 +30,7 @@ struct xfs_attr_list_context;
>>   
>>   static inline bool xfs_hasdelattr(struct xfs_mount *mp)
>>   {
>> -	return false;
>> +	return mp->m_flags & XFS_MOUNT_DELATTR;
>>   }
>>   
>>   /*
>> diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
>> index 659ad95..57cd914 100644
>> --- a/fs/xfs/xfs_mount.h
>> +++ b/fs/xfs/xfs_mount.h
>> @@ -250,6 +250,7 @@ typedef struct xfs_mount {
>>   #define XFS_MOUNT_NOATTR2	(1ULL << 25)	/* disable use of attr2 format */
>>   #define XFS_MOUNT_DAX_ALWAYS	(1ULL << 26)
>>   #define XFS_MOUNT_DAX_NEVER	(1ULL << 27)
>> +#define XFS_MOUNT_DELATTR	(1ULL << 28)	/* enable delayed attributes */
>>   
>>   /*
>>    * Max and min values for mount-option defined I/O
>> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
>> index 21b1d03..f6b08f9 100644
>> --- a/fs/xfs/xfs_super.c
>> +++ b/fs/xfs/xfs_super.c
>> @@ -93,7 +93,7 @@ enum {
>>   	Opt_filestreams, Opt_quota, Opt_noquota, Opt_usrquota, Opt_grpquota,
>>   	Opt_prjquota, Opt_uquota, Opt_gquota, Opt_pquota,
>>   	Opt_uqnoenforce, Opt_gqnoenforce, Opt_pqnoenforce, Opt_qnoenforce,
>> -	Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum,
>> +	Opt_discard, Opt_nodiscard, Opt_dax, Opt_dax_enum, Opt_delattr
>>   };
>>   
>>   static const struct fs_parameter_spec xfs_fs_parameters[] = {
>> @@ -138,6 +138,7 @@ static const struct fs_parameter_spec xfs_fs_parameters[] = {
>>   	fsparam_flag("nodiscard",	Opt_nodiscard),
>>   	fsparam_flag("dax",		Opt_dax),
>>   	fsparam_enum("dax",		Opt_dax_enum, dax_param_enums),
>> +	fsparam_flag("delattr",		Opt_delattr),
> 
> I wonder if this ought to be hidden behind CONFIG_XFS_DEBUG=y, but
> other than that this looks fine to me.
> 
> --D
Sure, I can wrap it with the CONFIG_XFS_DEBUG for now.  I think 
eventually the plan is to get rid of the option all together.

Allison
> 
>>   	{}
>>   };
>>   
>> @@ -1263,6 +1264,9 @@ xfs_fs_parse_param(
>>   		xfs_mount_set_dax_mode(mp, result.uint_32);
>>   		return 0;
>>   #endif
>> +	case Opt_delattr:
>> +		mp->m_flags |= XFS_MOUNT_DELATTR;
>> +		return 0;
>>   	/* Following mount options will be removed in September 2025 */
>>   	case Opt_ikeep:
>>   		xfs_warn(mp, "%s mount option is deprecated.", param->key);
>> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
>> index 9b0c790..8ec61df 100644
>> --- a/fs/xfs/xfs_xattr.c
>> +++ b/fs/xfs/xfs_xattr.c
>> @@ -8,6 +8,8 @@
>>   #include "xfs_shared.h"
>>   #include "xfs_format.h"
>>   #include "xfs_log_format.h"
>> +#include "xfs_trans_resv.h"
>> +#include "xfs_mount.h"
>>   #include "xfs_da_format.h"
>>   #include "xfs_inode.h"
>>   #include "xfs_da_btree.h"
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations
  2021-02-26  4:58   ` Darrick J. Wong
@ 2021-02-27  0:56     ` Allison Henderson
  2021-03-02  7:13       ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:56 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 9:58 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:42AM -0700, Allison Henderson wrote:
>> Currently attributes are modified directly across one or more
>> transactions. But they are not logged or replayed in the event of an
>> error. The goal of delayed attributes is to enable logging and replaying
>> of attribute operations using the existing delayed operations
>> infrastructure.  This will later enable the attributes to become part of
>> larger multi part operations that also must first be recorded to the
>> log.  This is mostly of interest in the scheme of parent pointers which
>> would need to maintain an attribute containing parent inode information
>> any time an inode is moved, created, or removed.  Parent pointers would
>> then be of interest to any feature that would need to quickly derive an
>> inode path from the mount point. Online scrub, nfs lookups and fs grow
>> or shrink operations are all features that could take advantage of this.
>>
>> This patch adds two new log item types for setting or removing
>> attributes as deferred operations.  The xfs_attri_log_item logs an
>> intent to set or remove an attribute.  The corresponding
>> xfs_attrd_log_item holds a reference to the xfs_attri_log_item and is
>> freed once the transaction is done.  Both log items use a generic
>> xfs_attr_log_format structure that contains the attribute name, value,
>> flags, inode, and an op_flag that indicates if the operations is a set
>> or remove.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> I saw a 36K patch and thought "Jeez!  This should be broken up!"
> 
> Then I looked at atomic extent swap (which does add its log item code
> gradually) and decided that maybe you just don't want to explode the
> patchset from 22 to 30.  That sounds (kind of) reasonable.
> 
> (...and by 'kind of', I probably mean that if I end up taking everything
> up to this patch for 5.13 then this part really ought to get broken up a
> bit for a future submission.  But let's wait until the end of this
> review to decide that...)

Yes, I've thought about falling back into a "Delay ready" sub series 
again.  It think the focus right now is the state machine and getting 
everyone on board with that, so I think it's reasonable to delay this 
part for now.  Since really the statemachine is a bit of a pre-req for this.

Allison
> 
>> ---
>>   fs/xfs/Makefile                 |   1 +
>>   fs/xfs/libxfs/xfs_attr.c        |   7 +-
>>   fs/xfs/libxfs/xfs_attr.h        |  31 ++
>>   fs/xfs/libxfs/xfs_defer.c       |   1 +
>>   fs/xfs/libxfs/xfs_defer.h       |   3 +
>>   fs/xfs/libxfs/xfs_log_format.h  |  44 ++-
>>   fs/xfs/libxfs/xfs_log_recover.h |   2 +
>>   fs/xfs/scrub/common.c           |   2 +
>>   fs/xfs/xfs_acl.c                |   2 +
>>   fs/xfs/xfs_attr_item.c          | 828 ++++++++++++++++++++++++++++++++++++++++
>>   fs/xfs/xfs_attr_item.h          |  52 +++
>>   fs/xfs/xfs_attr_list.c          |   1 +
>>   fs/xfs/xfs_ioctl.c              |   2 +
>>   fs/xfs/xfs_ioctl32.c            |   2 +
>>   fs/xfs/xfs_iops.c               |   2 +
>>   fs/xfs/xfs_log.c                |   4 +
>>   fs/xfs/xfs_log_recover.c        |   2 +
>>   fs/xfs/xfs_ondisk.h             |   2 +
>>   fs/xfs/xfs_xattr.c              |   1 +
>>   19 files changed, 984 insertions(+), 5 deletions(-)
>>
>> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
>> index 04611a1..b056cfc 100644
>> --- a/fs/xfs/Makefile
>> +++ b/fs/xfs/Makefile
>> @@ -102,6 +102,7 @@ xfs-y				+= xfs_log.o \
>>   				   xfs_buf_item_recover.o \
>>   				   xfs_dquot_item_recover.o \
>>   				   xfs_extfree_item.o \
>> +				   xfs_attr_item.o \
>>   				   xfs_icreate_item.o \
>>   				   xfs_inode_item.o \
>>   				   xfs_inode_item_recover.o \
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 2b8e481..e4c1b4b 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -24,6 +24,7 @@
>>   #include "xfs_quota.h"
>>   #include "xfs_trans_space.h"
>>   #include "xfs_trace.h"
>> +#include "xfs_attr_item.h"
>>   
>>   /*
>>    * xfs_attr.c
>> @@ -61,8 +62,8 @@ 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_attr_set_iter(struct xfs_delattr_context *dac,
>> +		      struct xfs_buf **leaf_bp);
>>   
>>   int
>>   xfs_inode_hasattr(
>> @@ -144,7 +145,7 @@ xfs_attr_get(
>>   /*
>>    * Calculate how many blocks we need for the new attribute,
>>    */
>> -STATIC int
>> +int
>>   xfs_attr_calc_size(
>>   	struct xfs_da_args	*args,
>>   	int			*local)
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 603887e..ee79763 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -28,6 +28,11 @@ struct xfs_attr_list_context;
>>    */
>>   #define	ATTR_MAX_VALUELEN	(64*1024)	/* max length of a value */
>>   
>> +static inline bool xfs_hasdelattr(struct xfs_mount *mp)
>> +{
>> +	return false;
>> +}
>> +
>>   /*
>>    * Kernel-internal version of the attrlist cursor.
>>    */
>> @@ -390,6 +395,7 @@ enum xfs_delattr_state {
>>    */
>>   #define XFS_DAC_DEFER_FINISH		0x01 /* finish the transaction */
>>   #define XFS_DAC_LEAF_ADDNAME_INIT	0x02 /* xfs_attr_leaf_addname init*/
>> +#define XFS_DAC_DELAYED_OP_INIT		0x04 /* delayed operations init*/
>>   
>>   /*
>>    * Context used for keeping track of delayed attribute operations
>> @@ -397,6 +403,11 @@ enum xfs_delattr_state {
>>   struct xfs_delattr_context {
>>   	struct xfs_da_args      *da_args;
>>   
>> +	/*
>> +	 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
>> +	 */
>> +	struct xfs_buf		*leaf_bp;
>> +
>>   	/* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */
>>   	struct xfs_bmbt_irec	map;
>>   	xfs_dablk_t		lblkno;
>> @@ -410,6 +421,23 @@ struct xfs_delattr_context {
>>   	enum xfs_delattr_state  dela_state;
>>   };
>>   
>> +/*
>> + * List of attrs to commit later.
>> + */
>> +struct xfs_attr_item {
>> +	struct xfs_delattr_context	xattri_dac;
>> +
>> +	/*
>> +	 * Indicates if the attr operation is a set or a remove
>> +	 * XFS_ATTR_OP_FLAGS_{SET,REMOVE}
>> +	 */
>> +	uint32_t			xattri_op_flags;
>> +
>> +	/* used to log this item to an intent */
>> +	struct list_head		xattri_list;
>> +};
>> +
>> +
>>   /*========================================================================
>>    * Function prototypes for the kernel.
>>    *========================================================================*/
>> @@ -425,11 +453,14 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
>>   int xfs_attr_get(struct xfs_da_args *args);
>>   int xfs_attr_set(struct xfs_da_args *args);
>>   int xfs_attr_set_args(struct xfs_da_args *args);
>> +int xfs_attr_set_iter(struct xfs_delattr_context *dac,
>> +		      struct xfs_buf **leaf_bp);
>>   int xfs_has_attr(struct xfs_da_args *args);
>>   int xfs_attr_remove_args(struct xfs_da_args *args);
>>   int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>   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);
>> +int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
>>   
>>   #endif	/* __XFS_ATTR_H__ */
>> diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
>> index eff4a12..e9caff7 100644
>> --- a/fs/xfs/libxfs/xfs_defer.c
>> +++ b/fs/xfs/libxfs/xfs_defer.c
>> @@ -178,6 +178,7 @@ static const struct xfs_defer_op_type *defer_op_types[] = {
>>   	[XFS_DEFER_OPS_TYPE_RMAP]	= &xfs_rmap_update_defer_type,
>>   	[XFS_DEFER_OPS_TYPE_FREE]	= &xfs_extent_free_defer_type,
>>   	[XFS_DEFER_OPS_TYPE_AGFL_FREE]	= &xfs_agfl_free_defer_type,
>> +	[XFS_DEFER_OPS_TYPE_ATTR]	= &xfs_attr_defer_type,
>>   };
>>   
>>   static void
>> diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
>> index 05472f7..72a5789 100644
>> --- a/fs/xfs/libxfs/xfs_defer.h
>> +++ b/fs/xfs/libxfs/xfs_defer.h
>> @@ -19,6 +19,7 @@ enum xfs_defer_ops_type {
>>   	XFS_DEFER_OPS_TYPE_RMAP,
>>   	XFS_DEFER_OPS_TYPE_FREE,
>>   	XFS_DEFER_OPS_TYPE_AGFL_FREE,
>> +	XFS_DEFER_OPS_TYPE_ATTR,
>>   	XFS_DEFER_OPS_TYPE_MAX,
>>   };
>>   
>> @@ -63,6 +64,8 @@ extern const struct xfs_defer_op_type xfs_refcount_update_defer_type;
>>   extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
>>   extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
>>   extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
>> +extern const struct xfs_defer_op_type xfs_attr_defer_type;
>> +
>>   
>>   /*
>>    * This structure enables a dfops user to detach the chain of deferred
>> diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
>> index 8bd00da..19963b6 100644
>> --- a/fs/xfs/libxfs/xfs_log_format.h
>> +++ b/fs/xfs/libxfs/xfs_log_format.h
>> @@ -117,7 +117,12 @@ struct xfs_unmount_log_format {
>>   #define XLOG_REG_TYPE_CUD_FORMAT	24
>>   #define XLOG_REG_TYPE_BUI_FORMAT	25
>>   #define XLOG_REG_TYPE_BUD_FORMAT	26
>> -#define XLOG_REG_TYPE_MAX		26
>> +#define XLOG_REG_TYPE_ATTRI_FORMAT	27
>> +#define XLOG_REG_TYPE_ATTRD_FORMAT	28
>> +#define XLOG_REG_TYPE_ATTR_NAME	29
>> +#define XLOG_REG_TYPE_ATTR_VALUE	30
>> +#define XLOG_REG_TYPE_MAX		30
>> +
>>   
>>   /*
>>    * Flags to log operation header
>> @@ -240,6 +245,8 @@ typedef struct xfs_trans_header {
>>   #define	XFS_LI_CUD		0x1243
>>   #define	XFS_LI_BUI		0x1244	/* bmbt update intent */
>>   #define	XFS_LI_BUD		0x1245
>> +#define	XFS_LI_ATTRI		0x1246  /* attr set/remove intent*/
>> +#define	XFS_LI_ATTRD		0x1247  /* attr set/remove done */
>>   
>>   #define XFS_LI_TYPE_DESC \
>>   	{ XFS_LI_EFI,		"XFS_LI_EFI" }, \
>> @@ -255,7 +262,9 @@ typedef struct xfs_trans_header {
>>   	{ XFS_LI_CUI,		"XFS_LI_CUI" }, \
>>   	{ XFS_LI_CUD,		"XFS_LI_CUD" }, \
>>   	{ XFS_LI_BUI,		"XFS_LI_BUI" }, \
>> -	{ XFS_LI_BUD,		"XFS_LI_BUD" }
>> +	{ XFS_LI_BUD,		"XFS_LI_BUD" }, \
>> +	{ XFS_LI_ATTRI,		"XFS_LI_ATTRI" }, \
>> +	{ XFS_LI_ATTRD,		"XFS_LI_ATTRD" }
>>   
>>   /*
>>    * Inode Log Item Format definitions.
>> @@ -863,4 +872,35 @@ struct xfs_icreate_log {
>>   	__be32		icl_gen;	/* inode generation number to use */
>>   };
>>   
>> +/*
>> + * Flags for deferred attribute operations.
>> + * Upper bits are flags, lower byte is type code
>> + */
>> +#define XFS_ATTR_OP_FLAGS_SET		1	/* Set the attribute */
>> +#define XFS_ATTR_OP_FLAGS_REMOVE	2	/* Remove the attribute */
>> +#define XFS_ATTR_OP_FLAGS_TYPE_MASK	0x0FF	/* Flags type mask */
>> +
>> +/*
>> + * This is the structure used to lay out an attr log item in the
>> + * log.
>> + */
>> +struct xfs_attri_log_format {
>> +	uint16_t	alfi_type;	/* attri log item type */
>> +	uint16_t	alfi_size;	/* size of this item */
>> +	uint32_t	__pad;		/* pad to 64 bit aligned */
>> +	uint64_t	alfi_id;	/* attri identifier */
>> +	uint64_t	alfi_ino;	/* the inode for this attr operation */
>> +	uint32_t	alfi_op_flags;	/* marks the op as a set or remove */
>> +	uint32_t	alfi_name_len;	/* attr name length */
>> +	uint32_t	alfi_value_len;	/* attr value length */
>> +	uint32_t	alfi_attr_flags;/* attr flags */
>> +};
>> +
>> +struct xfs_attrd_log_format {
>> +	uint16_t	alfd_type;	/* attrd log item type */
>> +	uint16_t	alfd_size;	/* size of this item */
>> +	uint32_t	__pad;		/* pad to 64 bit aligned */
>> +	uint64_t	alfd_alf_id;	/* id of corresponding attri */
>> +};
>> +
>>   #endif /* __XFS_LOG_FORMAT_H__ */
>> diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
>> index 3cca2bf..b6e5514 100644
>> --- a/fs/xfs/libxfs/xfs_log_recover.h
>> +++ b/fs/xfs/libxfs/xfs_log_recover.h
>> @@ -72,6 +72,8 @@ extern const struct xlog_recover_item_ops xlog_rui_item_ops;
>>   extern const struct xlog_recover_item_ops xlog_rud_item_ops;
>>   extern const struct xlog_recover_item_ops xlog_cui_item_ops;
>>   extern const struct xlog_recover_item_ops xlog_cud_item_ops;
>> +extern const struct xlog_recover_item_ops xlog_attri_item_ops;
>> +extern const struct xlog_recover_item_ops xlog_attrd_item_ops;
>>   
>>   /*
>>    * Macros, structures, prototypes for internal log manager use.
>> diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
>> index 53456f3..ac35121 100644
>> --- a/fs/xfs/scrub/common.c
>> +++ b/fs/xfs/scrub/common.c
>> @@ -24,6 +24,8 @@
>>   #include "xfs_rmap_btree.h"
>>   #include "xfs_log.h"
>>   #include "xfs_trans_priv.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_reflink.h"
>>   #include "scrub/scrub.h"
>> diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
>> index 779cb73..79f7bd2 100644
>> --- a/fs/xfs/xfs_acl.c
>> +++ b/fs/xfs/xfs_acl.c
>> @@ -10,6 +10,8 @@
>>   #include "xfs_trans_resv.h"
>>   #include "xfs_mount.h"
>>   #include "xfs_inode.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_trace.h"
>>   #include "xfs_error.h"
>> diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
>> new file mode 100644
>> index 0000000..8c8f72d
>> --- /dev/null
>> +++ b/fs/xfs/xfs_attr_item.c
>> @@ -0,0 +1,828 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) 2021 Oracle.  All Rights Reserved.
>> + * Author: Allison Collins <allison.henderson@oracle.com>
>> + */
>> +
>> +#include "xfs.h"
>> +#include "xfs_fs.h"
>> +#include "xfs_format.h"
>> +#include "xfs_log_format.h"
>> +#include "xfs_trans_resv.h"
>> +#include "xfs_bit.h"
>> +#include "xfs_shared.h"
>> +#include "xfs_mount.h"
>> +#include "xfs_defer.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_log_format.h"
>> +#include "xfs_trans.h"
>> +#include "xfs_bmap.h"
>> +#include "xfs_bmap_btree.h"
>> +#include "xfs_trans_priv.h"
>> +#include "xfs_buf_item.h"
>> +#include "xfs_attr_item.h"
>> +#include "xfs_log.h"
>> +#include "xfs_btree.h"
>> +#include "xfs_rmap.h"
>> +#include "xfs_inode.h"
>> +#include "xfs_icache.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_da_btree.h"
>> +#include "xfs_attr.h"
>> +#include "xfs_shared.h"
>> +#include "xfs_attr_item.h"
>> +#include "xfs_alloc.h"
>> +#include "xfs_bmap.h"
>> +#include "xfs_trace.h"
>> +#include "libxfs/xfs_da_format.h"
>> +#include "xfs_inode.h"
>> +#include "xfs_quota.h"
>> +#include "xfs_trans_space.h"
>> +#include "xfs_log_priv.h"
>> +#include "xfs_log_recover.h"
>> +
>> +static const struct xfs_item_ops xfs_attri_item_ops;
>> +static const struct xfs_item_ops xfs_attrd_item_ops;
>> +
>> +/* iovec length must be 32-bit aligned */
>> +static inline size_t ATTR_NVEC_SIZE(size_t size)
>> +{
>> +	return size == sizeof(int32_t) ? size :
>> +	       sizeof(int32_t) + round_up(size, sizeof(int32_t));
>> +}
>> +
>> +static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip)
>> +{
>> +	return container_of(lip, struct xfs_attri_log_item, attri_item);
>> +}
>> +
>> +STATIC void
>> +xfs_attri_item_free(
>> +	struct xfs_attri_log_item	*attrip)
>> +{
>> +	kmem_free(attrip->attri_item.li_lv_shadow);
>> +	kmem_free(attrip);
>> +}
>> +
>> +/*
>> + * Freeing the attrip requires that we remove it from the AIL if it has already
>> + * been placed there. However, the ATTRI may not yet have been placed in the
>> + * AIL when called by xfs_attri_release() from ATTRD processing due to the
>> + * ordering of committed vs unpin operations in bulk insert operations. Hence
>> + * the reference count to ensure only the last caller frees the ATTRI.
>> + */
>> +STATIC void
>> +xfs_attri_release(
>> +	struct xfs_attri_log_item	*attrip)
>> +{
>> +	ASSERT(atomic_read(&attrip->attri_refcount) > 0);
>> +	if (atomic_dec_and_test(&attrip->attri_refcount)) {
>> +		xfs_trans_ail_delete(&attrip->attri_item,
>> +				     SHUTDOWN_LOG_IO_ERROR);
>> +		xfs_attri_item_free(attrip);
>> +	}
>> +}
>> +
>> +STATIC void
>> +xfs_attri_item_size(
>> +	struct xfs_log_item	*lip,
>> +	int			*nvecs,
>> +	int			*nbytes)
>> +{
>> +	struct xfs_attri_log_item       *attrip = ATTRI_ITEM(lip);
>> +
>> +	*nvecs += 1;
>> +	*nbytes += sizeof(struct xfs_attri_log_format);
>> +
>> +	/* Attr set and remove operations require a name */
>> +	ASSERT(attrip->attri_name_len > 0);
>> +
>> +	*nvecs += 1;
>> +	*nbytes += ATTR_NVEC_SIZE(attrip->attri_name_len);
>> +
>> +	/*
>> +	 * Set ops can accept a value of 0 len to clear an attr value.  Remove
>> +	 * ops do not need a value at all.  So only account for the value
>> +	 * when it is needed.
>> +	 */
>> +	if (attrip->attri_value_len > 0) {
>> +		*nvecs += 1;
>> +		*nbytes += ATTR_NVEC_SIZE(attrip->attri_value_len);
>> +	}
>> +}
>> +
>> +/*
>> + * This is called to fill in the log iovecs for the given attri log
>> + * item. We use  1 iovec for the attri_format_item, 1 for the name, and
>> + * another for the value if it is present
>> + */
>> +STATIC void
>> +xfs_attri_item_format(
>> +	struct xfs_log_item	*lip,
>> +	struct xfs_log_vec	*lv)
>> +{
>> +	struct xfs_attri_log_item	*attrip = ATTRI_ITEM(lip);
>> +	struct xfs_log_iovec		*vecp = NULL;
>> +
>> +	attrip->attri_format.alfi_type = XFS_LI_ATTRI;
>> +	attrip->attri_format.alfi_size = 1;
>> +
>> +	/*
>> +	 * This size accounting must be done before copying the attrip into the
>> +	 * iovec.  If we do it after, the wrong size will be recorded to the log
>> +	 * and we trip across assertion checks for bad region sizes later during
>> +	 * the log recovery.
>> +	 */
>> +
>> +	ASSERT(attrip->attri_name_len > 0);
>> +	attrip->attri_format.alfi_size++;
>> +
>> +	if (attrip->attri_value_len > 0)
>> +		attrip->attri_format.alfi_size++;
>> +
>> +	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT,
>> +			&attrip->attri_format,
>> +			sizeof(struct xfs_attri_log_format));
>> +	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME,
>> +			attrip->attri_name,
>> +			ATTR_NVEC_SIZE(attrip->attri_name_len));
>> +	if (attrip->attri_value_len > 0)
>> +		xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE,
>> +				attrip->attri_value,
>> +				ATTR_NVEC_SIZE(attrip->attri_value_len));
>> +}
>> +
>> +/*
>> + * The unpin operation is the last place an ATTRI is manipulated in the log. It
>> + * is either inserted in the AIL or aborted in the event of a log I/O error. In
>> + * either case, the ATTRI transaction has been successfully committed to make
>> + * it this far. Therefore, we expect whoever committed the ATTRI to either
>> + * construct and commit the ATTRD or drop the ATTRD's reference in the event of
>> + * error. Simply drop the log's ATTRI reference now that the log is done with
>> + * it.
>> + */
>> +STATIC void
>> +xfs_attri_item_unpin(
>> +	struct xfs_log_item	*lip,
>> +	int			remove)
>> +{
>> +	xfs_attri_release(ATTRI_ITEM(lip));
>> +}
>> +
>> +
>> +STATIC void
>> +xfs_attri_item_release(
>> +	struct xfs_log_item	*lip)
>> +{
>> +	xfs_attri_release(ATTRI_ITEM(lip));
>> +}
>> +
>> +/*
>> + * Allocate and initialize an attri item.  Caller may allocate an additional
>> + * trailing buffer of the specified size
>> + */
>> +STATIC struct xfs_attri_log_item *
>> +xfs_attri_init(
>> +	struct xfs_mount		*mp,
>> +	int				buffer_size)
>> +
>> +{
>> +	struct xfs_attri_log_item	*attrip;
>> +	uint				size;
>> +
>> +	size = sizeof(struct xfs_attri_log_item) + buffer_size;
>> +	attrip = kmem_alloc_large(size, KM_ZERO);
>> +	if (attrip == NULL)
>> +		return NULL;
>> +
>> +	xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI,
>> +			  &xfs_attri_item_ops);
>> +	attrip->attri_format.alfi_id = (uintptr_t)(void *)attrip;
>> +	atomic_set(&attrip->attri_refcount, 2);
>> +
>> +	return attrip;
>> +}
>> +
>> +/*
>> + * Copy an attr format buffer from the given buf, and into the destination attr
>> + * format structure.
>> + */
>> +STATIC int
>> +xfs_attri_copy_format(
>> +	struct xfs_log_iovec		*buf,
>> +	struct xfs_attri_log_format	*dst_attr_fmt)
>> +{
>> +	struct xfs_attri_log_format	*src_attr_fmt = buf->i_addr;
>> +	uint				len;
>> +
>> +	len = sizeof(struct xfs_attri_log_format);
>> +	if (buf->i_len != len)
>> +		return -EFSCORRUPTED;
>> +
>> +	memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len);
>> +	return 0;
>> +}
>> +
>> +static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip)
>> +{
>> +	return container_of(lip, struct xfs_attrd_log_item, attrd_item);
>> +}
>> +
>> +STATIC void
>> +xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp)
>> +{
>> +	kmem_free(attrdp->attrd_item.li_lv_shadow);
>> +	kmem_free(attrdp);
>> +}
>> +
>> +STATIC void
>> +xfs_attrd_item_size(
>> +	struct xfs_log_item		*lip,
>> +	int				*nvecs,
>> +	int				*nbytes)
>> +{
>> +	*nvecs += 1;
>> +	*nbytes += sizeof(struct xfs_attrd_log_format);
>> +}
>> +
>> +/*
>> + * This is called to fill in the log iovecs for the given attrd log item. We use
>> + * only 1 iovec for the attrd_format, and we point that at the attr_log_format
>> + * structure embedded in the attrd item.
>> + */
>> +STATIC void
>> +xfs_attrd_item_format(
>> +	struct xfs_log_item	*lip,
>> +	struct xfs_log_vec	*lv)
>> +{
>> +	struct xfs_attrd_log_item	*attrdp = ATTRD_ITEM(lip);
>> +	struct xfs_log_iovec		*vecp = NULL;
>> +
>> +	attrdp->attrd_format.alfd_type = XFS_LI_ATTRD;
>> +	attrdp->attrd_format.alfd_size = 1;
>> +
>> +	xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRD_FORMAT,
>> +			&attrdp->attrd_format,
>> +			sizeof(struct xfs_attrd_log_format));
>> +}
>> +
>> +/*
>> + * The ATTRD is either committed or aborted if the transaction is cancelled. If
>> + * the transaction is cancelled, drop our reference to the ATTRI and free the
>> + * ATTRD.
>> + */
>> +STATIC void
>> +xfs_attrd_item_release(
>> +	struct xfs_log_item		*lip)
>> +{
>> +	struct xfs_attrd_log_item	*attrdp = ATTRD_ITEM(lip);
>> +
>> +	xfs_attri_release(attrdp->attrd_attrip);
>> +	xfs_attrd_item_free(attrdp);
>> +}
>> +
>> +/*
>> + * Performs one step of an attribute update intent and marks the attrd item
>> + * dirty..  An attr operation may be a set or a remove.  Note that the
>> + * transaction is marked dirty regardless of whether the operation succeeds or
>> + * fails to support the ATTRI/ATTRD lifecycle rules.
>> + */
>> +int
>> +xfs_trans_attr(
> 
> xfs_attri_finish_update() ?
> 
>> +	struct xfs_delattr_context	*dac,
>> +	struct xfs_attrd_log_item	*attrdp,
>> +	struct xfs_buf			**leaf_bp,
>> +	uint32_t			op_flags)
>> +{
>> +	struct xfs_da_args		*args = dac->da_args;
>> +	int				error;
>> +
>> +	error = xfs_qm_dqattach_locked(args->dp, 0);
>> +	if (error)
>> +		return error;
>> +
>> +	switch (op_flags) {
>> +	case XFS_ATTR_OP_FLAGS_SET:
>> +		args->op_flags |= XFS_DA_OP_ADDNAME;
>> +		error = xfs_attr_set_iter(dac, leaf_bp);
>> +		break;
>> +	case XFS_ATTR_OP_FLAGS_REMOVE:
>> +		ASSERT(XFS_IFORK_Q(args->dp));
>> +		error = xfs_attr_remove_iter(dac);
>> +		break;
>> +	default:
>> +		error = -EFSCORRUPTED;
>> +		break;
>> +	}
>> +
>> +	/*
>> +	 * Mark the transaction dirty, even on error. This ensures the
>> +	 * transaction is aborted, which:
>> +	 *
>> +	 * 1.) releases the ATTRI and frees the ATTRD
>> +	 * 2.) shuts down the filesystem
>> +	 */
>> +	args->trans->t_flags |= XFS_TRANS_DIRTY;
>> +
>> +	/*
>> +	 * attr intent/done items are null when delayed attributes are disabled
>> +	 */
>> +	if (attrdp)
>> +		set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
>> +
>> +	return error;
>> +}
>> +
>> +/* Log an attr to the intent item. */
>> +STATIC void
>> +xfs_attr_log_item(
>> +	struct xfs_trans		*tp,
>> +	struct xfs_attri_log_item	*attrip,
>> +	struct xfs_attr_item		*attr)
>> +{
>> +	struct xfs_attri_log_format	*attrp;
>> +
>> +	tp->t_flags |= XFS_TRANS_DIRTY;
>> +	set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags);
>> +
>> +	/*
>> +	 * At this point the xfs_attr_item has been constructed, and we've
>> +	 * created the log intent. Fill in the attri log item and log format
>> +	 * structure with fields from this xfs_attr_item
>> +	 */
>> +	attrp = &attrip->attri_format;
>> +	attrp->alfi_ino = attr->xattri_dac.da_args->dp->i_ino;
>> +	attrp->alfi_op_flags = attr->xattri_op_flags;
>> +	attrp->alfi_value_len = attr->xattri_dac.da_args->valuelen;
>> +	attrp->alfi_name_len = attr->xattri_dac.da_args->namelen;
>> +	attrp->alfi_attr_flags = attr->xattri_dac.da_args->attr_filter;
>> +
>> +	attrip->attri_name = (void *)attr->xattri_dac.da_args->name;
>> +	attrip->attri_value = attr->xattri_dac.da_args->value;
>> +	attrip->attri_name_len = attr->xattri_dac.da_args->namelen;
>> +	attrip->attri_value_len = attr->xattri_dac.da_args->valuelen;
>> +}
>> +
>> +/* Get an ATTRI. */
>> +static struct xfs_log_item *
>> +xfs_attr_create_intent(
>> +	struct xfs_trans		*tp,
>> +	struct list_head		*items,
>> +	unsigned int			count,
>> +	bool				sort)
>> +{
>> +	struct xfs_mount		*mp = tp->t_mountp;
>> +	struct xfs_attri_log_item	*attrip;
>> +	struct xfs_attr_item		*attr;
>> +
>> +	ASSERT(count == 1);
>> +
>> +	if (!xfs_hasdelattr(mp))
>> +		return NULL;
>> +
>> +	attrip = xfs_attri_init(mp, 0);
>> +	if (attrip == NULL)
>> +		return NULL;
>> +
>> +	xfs_trans_add_item(tp, &attrip->attri_item);
>> +	list_for_each_entry(attr, items, xattri_list)
>> +		xfs_attr_log_item(tp, attrip, attr);
>> +	return &attrip->attri_item;
>> +}
>> +
>> +/* Process an attr. */
>> +STATIC int
>> +xfs_attr_finish_item(
>> +	struct xfs_trans		*tp,
>> +	struct xfs_log_item		*done,
>> +	struct list_head		*item,
>> +	struct xfs_btree_cur		**state)
>> +{
>> +	struct xfs_attr_item		*attr;
>> +	struct xfs_attrd_log_item	*done_item = NULL;
>> +	int				error;
>> +	struct xfs_delattr_context	*dac;
>> +
>> +	attr = container_of(item, struct xfs_attr_item, xattri_list);
>> +	dac = &attr->xattri_dac;
>> +	if (done)
>> +		done_item = ATTRD_ITEM(done);
>> +
>> +	/*
>> +	 * Corner case that can happen during a recovery.  Because the first
>> +	 * iteration of a multi part delay op happens in xfs_attri_item_recover
>> +	 * to maintain the order of the log replay items.  But the new
>> +	 * transactions do not automatically rejoin during a recovery as they do
>> +	 * in a standard delay op, so we need to catch this here and rejoin the
>> +	 * leaf to the new transaction
>> +	 */
>> +	if (attr->xattri_dac.leaf_bp &&
>> +	    attr->xattri_dac.leaf_bp->b_transp != tp) {
>> +		xfs_trans_bjoin(tp, attr->xattri_dac.leaf_bp);
>> +		xfs_trans_bhold(tp, attr->xattri_dac.leaf_bp);
>> +	}
>> +
>> +	/*
>> +	 * Always reset trans after EAGAIN cycle
>> +	 * since the transaction is new
>> +	 */
>> +	dac->da_args->trans = tp;
>> +
>> +	error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
>> +			       attr->xattri_op_flags);
>> +	if (error != -EAGAIN)
>> +		kmem_free(attr);
>> +
>> +	return error;
>> +}
>> +
>> +/* Abort all pending ATTRs. */
>> +STATIC void
>> +xfs_attr_abort_intent(
>> +	struct xfs_log_item		*intent)
>> +{
>> +	xfs_attri_release(ATTRI_ITEM(intent));
>> +}
>> +
>> +/* Cancel an attr */
>> +STATIC void
>> +xfs_attr_cancel_item(
>> +	struct list_head		*item)
>> +{
>> +	struct xfs_attr_item		*attr;
>> +
>> +	attr = container_of(item, struct xfs_attr_item, xattri_list);
>> +	kmem_free(attr);
>> +}
>> +
>> +STATIC xfs_lsn_t
>> +xfs_attri_item_committed(
>> +	struct xfs_log_item		*lip,
>> +	xfs_lsn_t			lsn)
>> +{
>> +	struct xfs_attri_log_item	*attrip;
>> +	/*
>> +	 * The attrip refers to xfs_attr_item memory to log the name and value
>> +	 * with the intent item. This already occurred when the intent was
>> +	 * committed so these fields are no longer accessed. Clear them out of
>   +	 * caution since we're about to free the xfs_attr_item.
>> +	 */
>> +	attrip = ATTRI_ITEM(lip);
>> +	attrip->attri_name = NULL;
>> +	attrip->attri_value = NULL;
>> +
>> +	/*
>> +	 * The ATTRI is logged only once and cannot be moved in the log, so
>> +	 * simply return the lsn at which it's been logged.
>> +	 */
>> +	return lsn;
>> +}
>> +
>> +STATIC bool
>> +xfs_attri_item_match(
>> +	struct xfs_log_item	*lip,
>> +	uint64_t		intent_id)
>> +{
>> +	return ATTRI_ITEM(lip)->attri_format.alfi_id == intent_id;
>> +}
>> +
>> +/*
>> + * This routine is called to allocate an "attr free done" log item.
>> + */
>> +struct xfs_attrd_log_item *
>> +xfs_trans_get_attrd(struct xfs_trans		*tp,
>> +		  struct xfs_attri_log_item	*attrip)
>> +{
>> +	struct xfs_attrd_log_item		*attrdp;
>> +	uint					size;
>> +
>> +	ASSERT(tp != NULL);
>> +
>> +	size = sizeof(struct xfs_attrd_log_item);
>> +	attrdp = kmem_zalloc(size, 0);
>> +
>> +	xfs_log_item_init(tp->t_mountp, &attrdp->attrd_item, XFS_LI_ATTRD,
>> +			  &xfs_attrd_item_ops);
>> +	attrdp->attrd_attrip = attrip;
>> +	attrdp->attrd_format.alfd_alf_id = attrip->attri_format.alfi_id;
>> +
>> +	xfs_trans_add_item(tp, &attrdp->attrd_item);
>> +	return attrdp;
>> +}
>> +
>> +static const struct xfs_item_ops xfs_attrd_item_ops = {
>> +	.flags		= XFS_ITEM_RELEASE_WHEN_COMMITTED,
>> +	.iop_size	= xfs_attrd_item_size,
>> +	.iop_format	= xfs_attrd_item_format,
>> +	.iop_release    = xfs_attrd_item_release,
>> +};
>> +
>> +
>> +/* Get an ATTRD so we can process all the attrs. */
>> +static struct xfs_log_item *
>> +xfs_attr_create_done(
>> +	struct xfs_trans		*tp,
>> +	struct xfs_log_item		*intent,
>> +	unsigned int			count)
>> +{
>> +	if (!intent)
>> +		return NULL;
>> +
>> +	return &xfs_trans_get_attrd(tp, ATTRI_ITEM(intent))->attrd_item;
>> +}
>> +
>> +const struct xfs_defer_op_type xfs_attr_defer_type = {
>> +	.max_items	= 1,
>> +	.create_intent	= xfs_attr_create_intent,
>> +	.abort_intent	= xfs_attr_abort_intent,
>> +	.create_done	= xfs_attr_create_done,
>> +	.finish_item	= xfs_attr_finish_item,
>> +	.cancel_item	= xfs_attr_cancel_item,
>> +};
>> +
>> +/*
>> + * Process an attr intent item that was recovered from the log.  We need to
>> + * delete the attr that it describes.
>> + */
>> +STATIC int
>> +xfs_attri_item_recover(
>> +	struct xfs_log_item		*lip,
>> +	struct list_head		*capture_list)
>> +{
>> +	struct xfs_attri_log_item	*attrip = ATTRI_ITEM(lip);
>> +	struct xfs_attr_item		*new_attr;
>> +	struct xfs_mount		*mp = lip->li_mountp;
>> +	struct xfs_inode		*ip;
>> +	struct xfs_da_args		args;
>> +	struct xfs_da_args		*new_args;
>> +	struct xfs_trans_res		tres;
>> +	bool				rsvd;
>> +	struct xfs_attri_log_format	*attrp;
>> +	int				error;
>> +	int				total;
>> +	int				local;
>> +	struct xfs_attrd_log_item	*done_item = NULL;
>> +	struct xfs_attr_item		attr = {
>> +		.xattri_op_flags	= attrip->attri_format.alfi_op_flags,
>> +		.xattri_dac.da_args	= &args,
>> +	};
>> +
>> +	/*
>> +	 * First check the validity of the attr described by the ATTRI.  If any
>> +	 * are bad, then assume that all are bad and just toss the ATTRI.
>> +	 */
>> +	attrp = &attrip->attri_format;
>> +	if (!(attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET ||
>> +	      attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE) ||
>> +	    (attrp->alfi_value_len > XATTR_SIZE_MAX) ||
>> +	    (attrp->alfi_name_len > XATTR_NAME_MAX) ||
>> +	    (attrp->alfi_name_len == 0) ||
>> +	    xfs_verify_ino(mp, attrp->alfi_ino) == false ||
> 
> Please put this validation logic in a separate predicate.
> 
> It probably ought to ensure that there aren't illegal characters in the
> attr name too.
> 
>> +	    !xfs_hasdelattr(mp)) {
> 
> Weird nit: If you mount with -o delattr, crash, and remount without that
> mount option, we'll report that as a corruption error.
> 
> Not sure what we want to do about that, it's a rough edge but it's also
> an experimental feature.
> 
>> +		return -EFSCORRUPTED;
>> +	}
>> +
>> +	error = xfs_iget(mp, 0, attrp->alfi_ino, 0, 0, &ip);
>> +	if (error)
>> +		return error;
>> +
>> +	if (VFS_I(ip)->i_nlink == 0)
>> +		xfs_iflags_set(ip, XFS_IRECOVERY);
> 
>> +
>> +	memset(&args, 0, sizeof(struct xfs_da_args));
>> +	args.dp = ip;
>> +	args.geo = mp->m_attr_geo;
>> +	args.op_flags = attrp->alfi_op_flags;
>> +	args.whichfork = XFS_ATTR_FORK;
>> +	args.name = attrip->attri_name;
>> +	args.namelen = attrp->alfi_name_len;
>> +	args.hashval = xfs_da_hashname(args.name, args.namelen);
>> +	args.attr_filter = attrp->alfi_attr_flags;
>> +
>> +	if (attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET) {
>> +		args.value = attrip->attri_value;
>> +		args.valuelen = attrp->alfi_value_len;
>> +		args.total = xfs_attr_calc_size(&args, &local);
>> +
>> +		tres.tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
>> +				 M_RES(mp)->tr_attrsetrt.tr_logres *
>> +					args.total;
>> +		tres.tr_logcount = XFS_ATTRSET_LOG_COUNT;
>> +		tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
>> +		total = args.total;
>> +	} else {
>> +		tres = M_RES(mp)->tr_attrrm;
>> +		total = XFS_ATTRRM_SPACE_RES(mp);
>> +	}
> 
> Isn't there already code in xfs_attr.c that does this?
> 
>> +	error = xfs_trans_alloc(mp, &tres, total, 0,
>> +				rsvd ? XFS_TRANS_RESERVE : 0, &args.trans);
>> +	if (error)
>> +		return error;
>> +
>> +	done_item = xfs_trans_get_attrd(args.trans, attrip);
>> +
>> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
>> +	xfs_trans_ijoin(args.trans, ip, 0);
>> +
>> +	error = xfs_trans_attr(&attr.xattri_dac, done_item,
>> +			       &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
>> +	if (error == -EAGAIN) {
>> +		/*
>> +		 * There's more work to do, so make a new xfs_attr_item and add
>> +		 * it to this transaction.  We dont use xfs_attr_item_init here
>> +		 * because we need the info stored in the current attr to
>> +		 * continue with this multi-part operation.  So, alloc space
>> +		 * for it and the args and copy everything there.
>> +		 */
>> +		new_attr = kmem_zalloc(sizeof(struct xfs_attr_item) +
>> +				       sizeof(struct xfs_da_args), KM_NOFS);
>> +		new_args = (struct xfs_da_args *)((char *)new_attr +
>> +			   sizeof(struct xfs_attr_item));
>> +
>> +		memcpy(new_args, &args, sizeof(struct xfs_da_args));
>> +		memcpy(new_attr, &attr, sizeof(struct xfs_attr_item));
>> +
>> +		new_attr->xattri_dac.da_args = new_args;
>> +		memset(&new_attr->xattri_list, 0, sizeof(struct list_head));
>> +
>> +		xfs_defer_add(args.trans, XFS_DEFER_OPS_TYPE_ATTR,
>> +			      &new_attr->xattri_list);
>> +
>> +		/* Do not send -EAGAIN back to caller */
>> +		error = 0;
>> +	} else if (error) {
>> +		xfs_trans_cancel(args.trans);
>> +		goto out;
>> +	}
>> +
>> +	xfs_defer_ops_capture_and_commit(args.trans, ip, capture_list);
>> +
>> +out:
>> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
>> +	xfs_irele(ip);
>> +	return error;
>> +}
>> +
>> +/* Relog an intent item to push the log tail forward. */
>> +static struct xfs_log_item *
>> +xfs_attri_item_relog(
>> +	struct xfs_log_item		*intent,
>> +	struct xfs_trans		*tp)
>> +{
>> +	struct xfs_attrd_log_item	*attrdp;
>> +	struct xfs_attri_log_item	*old_attrip;
>> +	struct xfs_attri_log_item	*new_attrip;
>> +	struct xfs_attri_log_format	*new_attrp;
>> +	struct xfs_attri_log_format	*old_attrp;
>> +	int				buffer_size;
>> +
>> +	old_attrip = ATTRI_ITEM(intent);
>> +	old_attrp = &old_attrip->attri_format;
>> +	buffer_size = old_attrp->alfi_value_len + old_attrp->alfi_name_len;
>> +
>> +	tp->t_flags |= XFS_TRANS_DIRTY;
>> +	attrdp = xfs_trans_get_attrd(tp, old_attrip);
>> +	set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
>> +
>> +	new_attrip = xfs_attri_init(tp->t_mountp, buffer_size);
>> +	new_attrp = &new_attrip->attri_format;
>> +
>> +	new_attrp->alfi_ino = old_attrp->alfi_ino;
>> +	new_attrp->alfi_op_flags = old_attrp->alfi_op_flags;
>> +	new_attrp->alfi_value_len = old_attrp->alfi_value_len;
>> +	new_attrp->alfi_name_len = old_attrp->alfi_name_len;
>> +	new_attrp->alfi_attr_flags = old_attrp->alfi_attr_flags;
>> +
>> +	new_attrip->attri_name_len = old_attrip->attri_name_len;
>> +	new_attrip->attri_name = ((char *)new_attrip) +
>> +				 sizeof(struct xfs_attri_log_item);
>> +	memcpy(new_attrip->attri_name, old_attrip->attri_name,
>> +		new_attrip->attri_name_len);
>> +
>> +	new_attrip->attri_value_len = old_attrip->attri_value_len;
>> +	if (new_attrip->attri_value_len > 0) {
>> +		new_attrip->attri_value = new_attrip->attri_name +
>> +					  new_attrip->attri_name_len;
>> +
>> +		memcpy(new_attrip->attri_value, old_attrip->attri_value,
>> +		       new_attrip->attri_value_len);
>> +	}
>> +
>> +	xfs_trans_add_item(tp, &new_attrip->attri_item);
>> +	set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags);
>> +
>> +	return &new_attrip->attri_item;
>> +}
>> +
>> +static const struct xfs_item_ops xfs_attri_item_ops = {
>> +	.iop_size	= xfs_attri_item_size,
>> +	.iop_format	= xfs_attri_item_format,
>> +	.iop_unpin	= xfs_attri_item_unpin,
>> +	.iop_committed	= xfs_attri_item_committed,
>> +	.iop_release    = xfs_attri_item_release,
>> +	.iop_recover	= xfs_attri_item_recover,
>> +	.iop_match	= xfs_attri_item_match,
>> +	.iop_relog	= xfs_attri_item_relog,
>> +};
>> +
>> +
>> +
>> +STATIC int
>> +xlog_recover_attri_commit_pass2(
>> +	struct xlog                     *log,
>> +	struct list_head		*buffer_list,
>> +	struct xlog_recover_item        *item,
>> +	xfs_lsn_t                       lsn)
>> +{
>> +	int                             error;
>> +	struct xfs_mount                *mp = log->l_mp;
>> +	struct xfs_attri_log_item       *attrip;
>> +	struct xfs_attri_log_format     *attri_formatp;
>> +	char				*name = NULL;
>> +	char				*value = NULL;
>> +	int				region = 0;
>> +	int				buffer_size;
>> +
>> +	attri_formatp = item->ri_buf[region].i_addr;
>> +
>> +	/* Validate xfs_attri_log_format */
>> +	if (attri_formatp->__pad != 0 || attri_formatp->alfi_name_len == 0 ||
>> +	    (attri_formatp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE &&
>> +	    attri_formatp->alfi_value_len != 0))
>> +		return -EFSCORRUPTED;
> 
> Might be worth an XFS_ERROR_REPORT here since this is evidence of incore
> memory corruption, right?
> 
>> +
>> +	buffer_size = attri_formatp->alfi_name_len +
>> +		      attri_formatp->alfi_value_len;
>> +
>> +	attrip = xfs_attri_init(mp, buffer_size);
>> +	if (attrip == NULL)
>> +		return -ENOMEM;
>> +
>> +	error = xfs_attri_copy_format(&item->ri_buf[region],
>> +				      &attrip->attri_format);
>> +	if (error) {
>> +		xfs_attri_item_free(attrip);
>> +		return error;
>> +	}
>> +
>> +	attrip->attri_name_len = attri_formatp->alfi_name_len;
>> +	attrip->attri_value_len = attri_formatp->alfi_value_len;
>> +	region++;
>> +	name = ((char *)attrip) + sizeof(struct xfs_attri_log_item);
>> +	memcpy(name, item->ri_buf[region].i_addr, attrip->attri_name_len);
>> +	attrip->attri_name = name;
>> +
>> +	if (attrip->attri_value_len > 0) {
>> +		region++;
>> +		value = ((char *)attrip) + sizeof(struct xfs_attri_log_item) +
>> +			attrip->attri_name_len;
>> +		memcpy(value, item->ri_buf[region].i_addr,
>> +			attrip->attri_value_len);
>> +		attrip->attri_value = value;
>> +	}
>> +
>> +	/*
>> +	 * The ATTRI has two references. One for the ATTRD and one for ATTRI to
>> +	 * ensure it makes it into the AIL. Insert the ATTRI into the AIL
>> +	 * directly and drop the ATTRI reference. Note that
>> +	 * xfs_trans_ail_update() drops the AIL lock.
>> +	 */
>> +	xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn);
>> +	xfs_attri_release(attrip);
>> +	return 0;
>> +}
>> +
>> +const struct xlog_recover_item_ops xlog_attri_item_ops = {
>> +	.item_type	= XFS_LI_ATTRI,
>> +	.commit_pass2	= xlog_recover_attri_commit_pass2,
>> +};
>> +
>> +/*
>> + * This routine is called when an ATTRD format structure is found in a committed
>> + * transaction in the log. Its purpose is to cancel the corresponding ATTRI if
>> + * it was still in the log. To do this it searches the AIL for the ATTRI with
>> + * an id equal to that in the ATTRD format structure. If we find it we drop
>> + * the ATTRD reference, which removes the ATTRI from the AIL and frees it.
>> + */
>> +STATIC int
>> +xlog_recover_attrd_commit_pass2(
>> +	struct xlog			*log,
>> +	struct list_head		*buffer_list,
>> +	struct xlog_recover_item	*item,
>> +	xfs_lsn_t			lsn)
>> +{
>> +	struct xfs_attrd_log_format	*attrd_formatp;
>> +
>> +	attrd_formatp = item->ri_buf[0].i_addr;
>> +	ASSERT((item->ri_buf[0].i_len ==
>> +				(sizeof(struct xfs_attrd_log_format))));
>> +
>> +	xlog_recover_release_intent(log, XFS_LI_ATTRI,
>> +				    attrd_formatp->alfd_alf_id);
>> +	return 0;
>> +}
>> +
>> +const struct xlog_recover_item_ops xlog_attrd_item_ops = {
>> +	.item_type	= XFS_LI_ATTRD,
>> +	.commit_pass2	= xlog_recover_attrd_commit_pass2,
>> +};
>> diff --git a/fs/xfs/xfs_attr_item.h b/fs/xfs/xfs_attr_item.h
>> new file mode 100644
>> index 0000000..27c6bae
>> --- /dev/null
>> +++ b/fs/xfs/xfs_attr_item.h
>> @@ -0,0 +1,52 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later
>> + *
>> + * Copyright (C) 2019 Oracle.  All Rights Reserved.
>> + * Author: Allison Collins <allison.henderson@oracle.com>
>> + */
>> +#ifndef	__XFS_ATTR_ITEM_H__
>> +#define	__XFS_ATTR_ITEM_H__
>> +
>> +/* kernel only ATTRI/ATTRD definitions */
>> +
>> +struct xfs_mount;
>> +struct kmem_zone;
>> +
>> +/*
>> + * Define ATTR flag bits. Manipulated by set/clear/test_bit operators.
>> + */
>> +#define	XFS_ATTRI_RECOVERED	1
>> +
>> +
>> +/*
>> + * This is the "attr intention" log item.  It is used to log the fact that some
> 
> Well, it's the incore state tracking for attr intent log items... :)
> 
> Aside from the nits I 've pointed out so far, this looks more or less ok
> to me.
> 
> --------------
> 
> Ok, so here we are at the end.  Looking at my own atomic extent swap
> series, I broke this up into two pieces -- one to create the barebones
> log item (with dummy implementations); and a second one to create the
> defer ops code and connect it to the log item.
> 
> Oh, heh.  The first patch is 16K and the second one is 47K.
> 
> That might be a good way to break *this* patch into smaller parts, if
> nothing else -- concentrate on getting the log parts working, then
> connect the new state machine to defer ops and log items.
> 
> That said, as this series gets longer and longer I find it really more
> difficult to go through the whole series one by one vs. just diffing the
> whole branch and reviewing that.
> 
> <shrug> I don't really have a definitive answer for which is better.
> The xattr code is very complex, and I struggle even combining both of my
> usual strategies and attacking review from both ends.
> 
> By the way, have you been stress testing the xattr code with all this
> stuff applied?  At some point it becomes easier to pull this in and fix
> up the breakage than it is to review 22 slice-n-dice patches every cycle.
> 
> --D
> 
>> + * attribute operations need to be processed.  An operation is currently either
>> + * a set or remove.  Set or remove operations are described by the xfs_attr_item
>> + * which may be logged to this intent.
>> + *
>> + * During a normal attr operation, name and value point to the name and value
>> + * feilds of the calling functions xfs_da_args.  During a recovery, the name
>> + * and value buffers are copied from the log, and stored in a trailing buffer
>> + * attached to the xfs_attr_item until they are committed.  They are freed when
>> + * the xfs_attr_item itself is freed when the work is done.
>> + */
>> +struct xfs_attri_log_item {
>> +	struct xfs_log_item		attri_item;
>> +	atomic_t			attri_refcount;
>> +	int				attri_name_len;
>> +	int				attri_value_len;
>> +	void				*attri_name;
>> +	void				*attri_value;
>> +	struct xfs_attri_log_format	attri_format;
>> +};
>> +
>> +/*
>> + * This is the "attr done" log item.  It is used to log the fact that some attrs
>> + * earlier mentioned in an attri item have been freed.
>> + */
>> +struct xfs_attrd_log_item {
>> +	struct xfs_attri_log_item	*attrd_attrip;
>> +	struct xfs_log_item		attrd_item;
>> +	struct xfs_attrd_log_format	attrd_format;
>> +};
>> +
>> +#endif	/* __XFS_ATTR_ITEM_H__ */
>> diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
>> index 8f8837f..d7787a5 100644
>> --- a/fs/xfs/xfs_attr_list.c
>> +++ b/fs/xfs/xfs_attr_list.c
>> @@ -15,6 +15,7 @@
>>   #include "xfs_inode.h"
>>   #include "xfs_trans.h"
>>   #include "xfs_bmap.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_attr_sf.h"
>>   #include "xfs_attr_leaf.h"
>> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
>> index 248083e..6682936 100644
>> --- a/fs/xfs/xfs_ioctl.c
>> +++ b/fs/xfs/xfs_ioctl.c
>> @@ -15,6 +15,8 @@
>>   #include "xfs_iwalk.h"
>>   #include "xfs_itable.h"
>>   #include "xfs_error.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_bmap.h"
>>   #include "xfs_bmap_util.h"
>> diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
>> index c1771e7..62e1534 100644
>> --- a/fs/xfs/xfs_ioctl32.c
>> +++ b/fs/xfs/xfs_ioctl32.c
>> @@ -17,6 +17,8 @@
>>   #include "xfs_itable.h"
>>   #include "xfs_fsops.h"
>>   #include "xfs_rtalloc.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_ioctl.h"
>>   #include "xfs_ioctl32.h"
>> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
>> index 00369502f..ce04721 100644
>> --- a/fs/xfs/xfs_iops.c
>> +++ b/fs/xfs/xfs_iops.c
>> @@ -13,6 +13,8 @@
>>   #include "xfs_inode.h"
>>   #include "xfs_acl.h"
>>   #include "xfs_quota.h"
>> +#include "xfs_da_format.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_trans.h"
>>   #include "xfs_trace.h"
>> diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
>> index 0604183..290e57b 100644
>> --- a/fs/xfs/xfs_log.c
>> +++ b/fs/xfs/xfs_log.c
>> @@ -2070,6 +2070,10 @@ xlog_print_tic_res(
>>   	    REG_TYPE_STR(CUD_FORMAT, "cud_format"),
>>   	    REG_TYPE_STR(BUI_FORMAT, "bui_format"),
>>   	    REG_TYPE_STR(BUD_FORMAT, "bud_format"),
>> +	    REG_TYPE_STR(ATTRI_FORMAT, "attri_format"),
>> +	    REG_TYPE_STR(ATTRD_FORMAT, "attrd_format"),
>> +	    REG_TYPE_STR(ATTR_NAME, "attr_name"),
>> +	    REG_TYPE_STR(ATTR_VALUE, "attr_value"),
>>   	};
>>   	BUILD_BUG_ON(ARRAY_SIZE(res_type_str) != XLOG_REG_TYPE_MAX + 1);
>>   #undef REG_TYPE_STR
>> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
>> index 295a5c6..c0821b6 100644
>> --- a/fs/xfs/xfs_log_recover.c
>> +++ b/fs/xfs/xfs_log_recover.c
>> @@ -1775,6 +1775,8 @@ static const struct xlog_recover_item_ops *xlog_recover_item_ops[] = {
>>   	&xlog_cud_item_ops,
>>   	&xlog_bui_item_ops,
>>   	&xlog_bud_item_ops,
>> +	&xlog_attri_item_ops,
>> +	&xlog_attrd_item_ops,
>>   };
>>   
>>   static const struct xlog_recover_item_ops *
>> diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
>> index 0aa87c21..bc9c25e 100644
>> --- a/fs/xfs/xfs_ondisk.h
>> +++ b/fs/xfs/xfs_ondisk.h
>> @@ -132,6 +132,8 @@ xfs_check_ondisk_structs(void)
>>   	XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format,	56);
>>   	XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat,	20);
>>   	XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header,		16);
>> +	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
>> +	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
>>   
>>   	/*
>>   	 * The v5 superblock format extended several v4 header structures with
>> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
>> index bca48b3..9b0c790 100644
>> --- a/fs/xfs/xfs_xattr.c
>> +++ b/fs/xfs/xfs_xattr.c
>> @@ -10,6 +10,7 @@
>>   #include "xfs_log_format.h"
>>   #include "xfs_da_format.h"
>>   #include "xfs_inode.h"
>> +#include "xfs_da_btree.h"
>>   #include "xfs_attr.h"
>>   #include "xfs_acl.h"
>>   #include "xfs_da_btree.h"
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs
  2021-02-26  5:02   ` Darrick J. Wong
@ 2021-02-27  0:56     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:56 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 10:02 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:43AM -0700, Allison Henderson wrote:
>> This is a clean up patch that skips the flip flag logic for delayed attr
>> renames.  Since the log replay keeps the inode locked, we do not need to
>> worry about race windows with attr lookups.  So we can skip over
>> flipping the flag and the extra transaction roll for it
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> I wonder, have you done much performance analysis of the old vs. new
> xattr code paths?  Does skipping the extra step + roll make attr
> operations faster?
I dont have any analysis right now, but maybe I could put some together. 
  I'm sure there's some impact, but not sure how much.  If it does, I 
suspect it will become of more interest when we bring in pptrs since the 
code path with be in heavier use then.

> 
> This looks pretty straightforward though:
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Thank you!

Allison

> 
> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c      | 51 +++++++++++++++++++++++++------------------
>>   fs/xfs/libxfs/xfs_attr_leaf.c |  3 ++-
>>   2 files changed, 32 insertions(+), 22 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index e4c1b4b..666cc69 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -337,6 +337,7 @@ xfs_attr_set_iter(
>>   	struct xfs_da_state		*state = NULL;
>>   	int				forkoff, error = 0;
>>   	int				retval = 0;
>> +	struct xfs_mount		*mp = args->dp->i_mount;
>>   
>>   	/* State machine switch */
>>   	switch (dac->dela_state) {
>> @@ -470,16 +471,21 @@ xfs_attr_set_iter(
>>   		 * "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.
>> -		 */
>> -		dac->dela_state = XFS_DAS_FLIP_LFLAG;
>> -		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>> -		return -EAGAIN;
>> +		if (!xfs_hasdelattr(mp)) {
>> +			error = xfs_attr3_leaf_flipflags(args);
>> +			if (error)
>> +				return error;
>> +			/*
>> +			 * Commit the flag value change and start the next trans
>> +			 * in series.
>> +			 */
>> +			dac->dela_state = XFS_DAS_FLIP_LFLAG;
>> +			trace_xfs_attr_set_iter_return(dac->dela_state,
>> +						       args->dp);
>> +			return -EAGAIN;
>> +		}
>> +
>> +		/* fallthrough */
>>   	case XFS_DAS_FLIP_LFLAG:
>>   		/*
>>   		 * Dismantle the "old" attribute/value pair by removing a
>> @@ -588,17 +594,21 @@ xfs_attr_set_iter(
>>   		 * 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
>> -		 */
>> -		dac->dela_state = XFS_DAS_FLIP_NFLAG;
>> -		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>> -		return -EAGAIN;
>> +		if (!xfs_hasdelattr(mp)) {
>> +			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;
>> +			trace_xfs_attr_set_iter_return(dac->dela_state,
>> +						       args->dp);
>> +			return -EAGAIN;
>> +		}
>>   
>> +		/* fallthrough */
>>   	case XFS_DAS_FLIP_NFLAG:
>>   		/*
>>   		 * Dismantle the "old" attribute/value pair by removing a
>> @@ -1277,7 +1287,6 @@ int xfs_attr_node_addname_work(
>>   	 * 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.
>>   	 */
>> -	args->attr_filter |= XFS_ATTR_INCOMPLETE;
>>   	state = xfs_da_state_alloc(args);
>>   	state->inleaf = 0;
>>   	error = xfs_da3_node_lookup_int(state, &retval);
>> diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
>> index 3780141..ec707bd 100644
>> --- a/fs/xfs/libxfs/xfs_attr_leaf.c
>> +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
>> @@ -1486,7 +1486,8 @@ xfs_attr3_leaf_add_work(
>>   	if (tmp)
>>   		entry->flags |= XFS_ATTR_LOCAL;
>>   	if (args->op_flags & XFS_DA_OP_RENAME) {
>> -		entry->flags |= XFS_ATTR_INCOMPLETE;
>> +		if (!xfs_hasdelattr(mp))
>> +			entry->flags |= XFS_ATTR_INCOMPLETE;
>>   		if ((args->blkno2 == args->blkno) &&
>>   		    (args->index2 <= args->index)) {
>>   			args->index2++;
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred
  2021-02-26  5:00   ` Darrick J. Wong
@ 2021-02-27  0:57     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 10:00 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:44AM -0700, Allison Henderson wrote:
>> From: Allison Collins <allison.henderson@oracle.com>
>>
>> These routines to set up and start a new deferred attribute operations.
> 
> "These routine set up and queue a new deferred attribute operation..."?
Sure, will fix
> 
>> These functions are meant to be called by any routine needing to
>> initiate a deferred attribute operation as opposed to the existing
>> inline operations. New helper function xfs_attr_item_init also added.
>>
>> Finally enable delayed attributes in xfs_attr_set and xfs_attr_remove.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> Other than that it seems fine to me,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Thank you!
Allison

> 
> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++--
>>   fs/xfs/libxfs/xfs_attr.h |  2 ++
>>   2 files changed, 58 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 666cc69..cec861e 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -25,6 +25,7 @@
>>   #include "xfs_trans_space.h"
>>   #include "xfs_trace.h"
>>   #include "xfs_attr_item.h"
>> +#include "xfs_attr.h"
>>   
>>   /*
>>    * xfs_attr.c
>> @@ -838,9 +839,10 @@ xfs_attr_set(
>>   		if (error != -ENOATTR && error != -EEXIST)
>>   			goto out_trans_cancel;
>>   
>> -		error = xfs_attr_set_args(args);
>> +		error = xfs_attr_set_deferred(args);
>>   		if (error)
>>   			goto out_trans_cancel;
>> +
>>   		/* shortform attribute has already been committed */
>>   		if (!args->trans)
>>   			goto out_unlock;
>> @@ -849,7 +851,7 @@ xfs_attr_set(
>>   		if (error != -EEXIST)
>>   			goto out_trans_cancel;
>>   
>> -		error = xfs_attr_remove_args(args);
>> +		error = xfs_attr_remove_deferred(args);
>>   		if (error)
>>   			goto out_trans_cancel;
>>   	}
>> @@ -879,6 +881,58 @@ xfs_attr_set(
>>   	goto out_unlock;
>>   }
>>   
>> +STATIC int
>> +xfs_attr_item_init(
>> +	struct xfs_da_args	*args,
>> +	unsigned int		op_flags,	/* op flag (set or remove) */
>> +	struct xfs_attr_item	**attr)		/* new xfs_attr_item */
>> +{
>> +
>> +	struct xfs_attr_item	*new;
>> +
>> +	new = kmem_zalloc(sizeof(struct xfs_attr_item), KM_NOFS);
>> +	new->xattri_op_flags = op_flags;
>> +	new->xattri_dac.da_args = args;
>> +
>> +	*attr = new;
>> +	return 0;
>> +}
>> +
>> +/* Sets an attribute for an inode as a deferred operation */
>> +int
>> +xfs_attr_set_deferred(
>> +	struct xfs_da_args	*args)
>> +{
>> +	struct xfs_attr_item	*new;
>> +	int			error = 0;
>> +
>> +	error = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_SET, &new);
>> +	if (error)
>> +		return error;
>> +
>> +	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
>> +
>> +	return 0;
>> +}
>> +
>> +/* Removes an attribute for an inode as a deferred operation */
>> +int
>> +xfs_attr_remove_deferred(
>> +	struct xfs_da_args	*args)
>> +{
>> +
>> +	struct xfs_attr_item	*new;
>> +	int			error;
>> +
>> +	error  = xfs_attr_item_init(args, XFS_ATTR_OP_FLAGS_REMOVE, &new);
>> +	if (error)
>> +		return error;
>> +
>> +	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
>> +
>> +	return 0;
>> +}
>> +
>>   /*========================================================================
>>    * External routines when attribute list is inside the inode
>>    *========================================================================*/
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index ee79763..4abf02c 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -462,5 +462,7 @@ 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);
>>   int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
>> +int xfs_attr_set_deferred(struct xfs_da_args *args);
>> +int xfs_attr_remove_deferred(struct xfs_da_args *args);
>>   
>>   #endif	/* __XFS_ATTR_H__ */
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args
  2021-02-26  4:58   ` Darrick J. Wong
@ 2021-02-27  0:57     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 9:58 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:45AM -0700, Allison Henderson wrote:
>> Remove xfs_attr_set_args, xfs_attr_remove_args, and xfs_attr_trans_roll.
>> These high level loops are now driven by the delayed operations code,
>> and can be removed.
>>
>> Additionally collapse in the leaf_bp parameter of xfs_attr_set_iter
>> since we only have one caller that passes dac->leaf_bp
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> Looks good to me,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> 
Great, thanks!
Allison

> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c        | 97 +++--------------------------------------
>>   fs/xfs/libxfs/xfs_attr.h        | 10 ++---
>>   fs/xfs/libxfs/xfs_attr_remote.c |  1 -
>>   fs/xfs/xfs_attr_item.c          |  8 ++--
>>   4 files changed, 11 insertions(+), 105 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index cec861e..8b62447 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -63,8 +63,6 @@ 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);
>> -int xfs_attr_set_iter(struct xfs_delattr_context *dac,
>> -		      struct xfs_buf **leaf_bp);
>>   
>>   int
>>   xfs_inode_hasattr(
>> @@ -223,67 +221,13 @@ 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.
>> - */
>> -STATIC 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);
>> -		if (error)
>> -			return error;
>> -	} else
>> -		error = xfs_trans_roll_inode(&args->trans, args->dp);
>> -
>> -	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_delattr_context	*dac,
>> -	struct xfs_buf			**leaf_bp)
>> +	struct xfs_delattr_context	*dac)
>>   {
>>   	struct xfs_da_args		*args = dac->da_args;
>>   	struct xfs_inode		*dp = args->dp;
>> +	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
>>   	int				error = 0;
>>   
>>   	/*
>> @@ -316,7 +260,6 @@ xfs_attr_set_fmt(
>>   	 * add.
>>   	 */
>>   	trace_xfs_attr_set_fmt_return(XFS_DAS_UNINIT, args->dp);
>> -	dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	return -EAGAIN;
>>   }
>>   
>> @@ -329,10 +272,10 @@ xfs_attr_set_fmt(
>>    */
>>   int
>>   xfs_attr_set_iter(
>> -	struct xfs_delattr_context	*dac,
>> -	struct xfs_buf			**leaf_bp)
>> +	struct xfs_delattr_context	*dac)
>>   {
>>   	struct xfs_da_args              *args = dac->da_args;
>> +	struct xfs_buf			**leaf_bp = &dac->leaf_bp;
>>   	struct xfs_inode		*dp = args->dp;
>>   	struct xfs_buf			*bp = NULL;
>>   	struct xfs_da_state		*state = NULL;
>> @@ -344,7 +287,7 @@ xfs_attr_set_iter(
>>   	switch (dac->dela_state) {
>>   	case XFS_DAS_UNINIT:
>>   		if (xfs_attr_is_shortform(dp))
>> -			return xfs_attr_set_fmt(dac, leaf_bp);
>> +			return xfs_attr_set_fmt(dac);
>>   
>>   		/*
>>   		 * After a shortform to leaf conversion, we need to hold the
>> @@ -381,7 +324,6 @@ xfs_attr_set_iter(
>>   				 * be a node, so we'll fall down into the node
>>   				 * handling code below
>>   				 */
>> -				dac->flags |= XFS_DAC_DEFER_FINISH;
>>   				trace_xfs_attr_set_iter_return(
>>   					dac->dela_state, args->dp);
>>   				return -EAGAIN;
>> @@ -687,32 +629,6 @@ xfs_has_attr(
>>   
>>   /*
>>    * Remove the attribute specified in @args.
>> - */
>> -int
>> -xfs_attr_remove_args(
>> -	struct xfs_da_args	*args)
>> -{
>> -	int				error;
>> -	struct xfs_delattr_context	dac = {
>> -		.da_args	= 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;
>> -}
>> -
>> -/*
>> - * Remove the attribute specified in @args.
>>    *
>>    * This function may return -EAGAIN to signal that the transaction needs to be
>>    * rolled.  Callers should continue calling this function until they receive a
>> @@ -1297,7 +1213,6 @@ xfs_attr_node_addname(
>>   			 * this. dela_state is still unset by this function at
>>   			 * this point.
>>   			 */
>> -			dac->flags |= XFS_DAC_DEFER_FINISH;
>>   			trace_xfs_attr_node_addname_return(
>>   					dac->dela_state, args->dp);
>>   			return -EAGAIN;
>> @@ -1312,7 +1227,6 @@ xfs_attr_node_addname(
>>   		error = xfs_da3_split(state);
>>   		if (error)
>>   			goto out;
>> -		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	} else {
>>   		/*
>>   		 * Addition succeeded, update Btree hashvals.
>> @@ -1599,7 +1513,6 @@ xfs_attr_node_removename_iter(
>>   			if (error)
>>   				goto out;
>>   
>> -			dac->flags |= XFS_DAC_DEFER_FINISH;
>>   			dac->dela_state = XFS_DAS_RM_SHRINK;
>>   			trace_xfs_attr_node_removename_iter_return(
>>   					dac->dela_state, args->dp);
>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>> index 4abf02c..f82c0b1 100644
>> --- a/fs/xfs/libxfs/xfs_attr.h
>> +++ b/fs/xfs/libxfs/xfs_attr.h
>> @@ -393,9 +393,8 @@ enum xfs_delattr_state {
>>   /*
>>    * 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*/
>> -#define XFS_DAC_DELAYED_OP_INIT		0x04 /* delayed operations init*/
>> +#define XFS_DAC_LEAF_ADDNAME_INIT	0x01 /* xfs_attr_leaf_addname init*/
>> +#define XFS_DAC_DELAYED_OP_INIT		0x02 /* delayed operations init*/
>>   
>>   /*
>>    * Context used for keeping track of delayed attribute operations
>> @@ -452,11 +451,8 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
>>   int xfs_attr_get_ilocked(struct xfs_da_args *args);
>>   int xfs_attr_get(struct xfs_da_args *args);
>>   int xfs_attr_set(struct xfs_da_args *args);
>> -int xfs_attr_set_args(struct xfs_da_args *args);
>> -int xfs_attr_set_iter(struct xfs_delattr_context *dac,
>> -		      struct xfs_buf **leaf_bp);
>> +int xfs_attr_set_iter(struct xfs_delattr_context *dac);
>>   int xfs_has_attr(struct xfs_da_args *args);
>> -int xfs_attr_remove_args(struct xfs_da_args *args);
>>   int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>   bool xfs_attr_namecheck(const void *name, size_t length);
>>   void xfs_delattr_context_init(struct xfs_delattr_context *dac,
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index b6554a3..78bb552 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>> @@ -762,7 +762,6 @@ xfs_attr_rmtval_remove(
>>   	 * by the parent
>>   	 */
>>   	if (!done) {
>> -		dac->flags |= XFS_DAC_DEFER_FINISH;
>>   		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
>>   		return -EAGAIN;
>>   	}
>> diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
>> index 8c8f72d..13b289b 100644
>> --- a/fs/xfs/xfs_attr_item.c
>> +++ b/fs/xfs/xfs_attr_item.c
>> @@ -291,7 +291,6 @@ int
>>   xfs_trans_attr(
>>   	struct xfs_delattr_context	*dac,
>>   	struct xfs_attrd_log_item	*attrdp,
>> -	struct xfs_buf			**leaf_bp,
>>   	uint32_t			op_flags)
>>   {
>>   	struct xfs_da_args		*args = dac->da_args;
>> @@ -304,7 +303,7 @@ xfs_trans_attr(
>>   	switch (op_flags) {
>>   	case XFS_ATTR_OP_FLAGS_SET:
>>   		args->op_flags |= XFS_DA_OP_ADDNAME;
>> -		error = xfs_attr_set_iter(dac, leaf_bp);
>> +		error = xfs_attr_set_iter(dac);
>>   		break;
>>   	case XFS_ATTR_OP_FLAGS_REMOVE:
>>   		ASSERT(XFS_IFORK_Q(args->dp));
>> @@ -428,8 +427,7 @@ xfs_attr_finish_item(
>>   	 */
>>   	dac->da_args->trans = tp;
>>   
>> -	error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
>> -			       attr->xattri_op_flags);
>> +	error = xfs_trans_attr(dac, done_item, attr->xattri_op_flags);
>>   	if (error != -EAGAIN)
>>   		kmem_free(attr);
>>   
>> @@ -625,7 +623,7 @@ xfs_attri_item_recover(
>>   	xfs_trans_ijoin(args.trans, ip, 0);
>>   
>>   	error = xfs_trans_attr(&attr.xattri_dac, done_item,
>> -			       &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
>> +			       attrp->alfi_op_flags);
>>   	if (error == -EAGAIN) {
>>   		/*
>>   		 * There's more work to do, so make a new xfs_attr_item and add
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans
  2021-02-26  5:06   ` Darrick J. Wong
@ 2021-02-27  0:57     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 10:06 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:41AM -0700, Allison Henderson wrote:
>> Because xattrs can be over a page in size, we need to handle possible
>> krealloc errors to avoid warnings
>>
>> The warning:
>>     WARNING: CPU: 1 PID: 20255 at mm/page_alloc.c:3446
>>                   get_page_from_freelist+0x100b/0x1690
>>
>> is caused when sizes larger that a page are allocated with the
>> __GFP_NOFAIL flag option.  We encounter this error now because attr
>> values can be up to 64k in size.  So we cannot use __GFP_NOFAIL, and
>> we need to handle the error code if the allocation fails.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/xfs_log_recover.c | 5 ++++-
>>   1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
>> index 97f3130..295a5c6 100644
>> --- a/fs/xfs/xfs_log_recover.c
>> +++ b/fs/xfs/xfs_log_recover.c
>> @@ -2061,7 +2061,10 @@ xlog_recover_add_to_cont_trans(
>>   	old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
>>   	old_len = item->ri_buf[item->ri_cnt-1].i_len;
>>   
>> -	ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL | __GFP_NOFAIL);
>> +	ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL);
>> +	if (ptr == NULL)
>> +		return -ENOMEM;
> 
> Given that we update i_addr anyway, perhaps this should fall back to
> kmem_alloc_large+memcpy to avoid introducing another failure point?
Sure, I can add that in. Thx!

Allison
> 
> --D
> 
>> +
>>   	memcpy(&ptr[old_len], dp, len);
>>   	item->ri_buf[item->ri_cnt-1].i_len += len;
>>   	item->ri_buf[item->ri_cnt-1].i_addr = ptr;
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 13/22] xfs: Add state machine tracepoints
  2021-02-26  5:06   ` Darrick J. Wong
@ 2021-02-27  0:57     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-02-27  0:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/25/21 10:06 PM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:39AM -0700, Allison Henderson wrote:
>> This is a quick patch to add a new tracepoint: xfs_das_state_return.  We
>> use this to track when ever a new state is set or -EAGAIN is returned
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> Looks good!
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Cool, thank you!
Allison

> 
> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c        | 31 ++++++++++++++++++++++++++++++-
>>   fs/xfs/libxfs/xfs_attr_remote.c |  1 +
>>   fs/xfs/xfs_trace.h              | 25 +++++++++++++++++++++++++
>>   3 files changed, 56 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index c7b86d5..ba21475 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -313,6 +313,7 @@ xfs_attr_set_fmt(
>>   	 * the attr fork to leaf format and will restart with the leaf
>>   	 * add.
>>   	 */
>> +	trace_xfs_attr_set_fmt_return(XFS_DAS_UNINIT, args->dp);
>>   	dac->flags |= XFS_DAC_DEFER_FINISH;
>>   	return -EAGAIN;
>>   }
>> @@ -378,6 +379,8 @@ xfs_attr_set_iter(
>>   				 * handling code below
>>   				 */
>>   				dac->flags |= XFS_DAC_DEFER_FINISH;
>> +				trace_xfs_attr_set_iter_return(
>> +					dac->dela_state, args->dp);
>>   				return -EAGAIN;
>>   			}
>>   			else if (error)
>> @@ -400,10 +403,13 @@ xfs_attr_set_iter(
>>   				return error;
>>   
>>   			dac->dela_state = XFS_DAS_FOUND_NBLK;
>> +			trace_xfs_attr_set_iter_return(dac->dela_state,
>> +						       args->dp);
>>   			return -EAGAIN;
>>   		}
>>   
>>   		dac->dela_state = XFS_DAS_FOUND_LBLK;
>> +		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>>   		return -EAGAIN;
>>   
>>           case XFS_DAS_FOUND_LBLK:
>> @@ -433,6 +439,8 @@ xfs_attr_set_iter(
>>   			if (error)
>>   				return error;
>>   
>> +			trace_xfs_attr_set_iter_return(dac->dela_state,
>> +						       args->dp);
>>   			return -EAGAIN;
>>   		}
>>   
>> @@ -469,6 +477,7 @@ xfs_attr_set_iter(
>>   		 * series.
>>   		 */
>>   		dac->dela_state = XFS_DAS_FLIP_LFLAG;
>> +		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>>   		return -EAGAIN;
>>   	case XFS_DAS_FLIP_LFLAG:
>>   		/*
>> @@ -488,6 +497,9 @@ xfs_attr_set_iter(
>>   	case XFS_DAS_RM_LBLK:
>>   		if (args->rmtblkno) {
>>   			error = __xfs_attr_rmtval_remove(dac);
>> +			if (error == -EAGAIN)
>> +				trace_xfs_attr_set_iter_return(
>> +					dac->dela_state, args->dp);
>>   			if (error)
>>   				return error;
>>   		}
>> @@ -545,6 +557,8 @@ xfs_attr_set_iter(
>>   				if (error)
>>   					return error;
>>   
>> +				trace_xfs_attr_set_iter_return(
>> +					dac->dela_state, args->dp);
>>   				return -EAGAIN;
>>   			}
>>   
>> @@ -581,6 +595,7 @@ xfs_attr_set_iter(
>>   		 * series
>>   		 */
>>   		dac->dela_state = XFS_DAS_FLIP_NFLAG;
>> +		trace_xfs_attr_set_iter_return(dac->dela_state, args->dp);
>>   		return -EAGAIN;
>>   
>>   	case XFS_DAS_FLIP_NFLAG:
>> @@ -601,6 +616,10 @@ xfs_attr_set_iter(
>>   	case XFS_DAS_RM_NBLK:
>>   		if (args->rmtblkno) {
>>   			error = __xfs_attr_rmtval_remove(dac);
>> +			if (error == -EAGAIN)
>> +				trace_xfs_attr_set_iter_return(
>> +					dac->dela_state, args->dp);
>> +
>>   			if (error)
>>   				return error;
>>   		}
>> @@ -1214,6 +1233,8 @@ xfs_attr_node_addname(
>>   			 * this point.
>>   			 */
>>   			dac->flags |= XFS_DAC_DEFER_FINISH;
>> +			trace_xfs_attr_node_addname_return(
>> +					dac->dela_state, args->dp);
>>   			return -EAGAIN;
>>   		}
>>   
>> @@ -1394,6 +1415,9 @@ xfs_attr_node_remove_rmt (
>>   	 * May return -EAGAIN to request that the caller recall this function
>>   	 */
>>   	error = __xfs_attr_rmtval_remove(dac);
>> +	if (error == -EAGAIN)
>> +		trace_xfs_attr_node_remove_rmt_return(dac->dela_state,
>> +						      dac->da_args->dp);
>>   	if (error)
>>   		return error;
>>   
>> @@ -1513,6 +1537,8 @@ xfs_attr_node_removename_iter(
>>   
>>   			dac->flags |= XFS_DAC_DEFER_FINISH;
>>   			dac->dela_state = XFS_DAS_RM_SHRINK;
>> +			trace_xfs_attr_node_removename_iter_return(
>> +					dac->dela_state, args->dp);
>>   			return -EAGAIN;
>>   		}
>>   
>> @@ -1531,8 +1557,11 @@ xfs_attr_node_removename_iter(
>>   		goto out;
>>   	}
>>   
>> -	if (error == -EAGAIN)
>> +	if (error == -EAGAIN) {
>> +		trace_xfs_attr_node_removename_iter_return(
>> +					dac->dela_state, args->dp);
>>   		return error;
>> +	}
>>   out:
>>   	if (state)
>>   		xfs_da_state_free(state);
>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>> index 6af86bf..b242e1a 100644
>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>> @@ -763,6 +763,7 @@ __xfs_attr_rmtval_remove(
>>   	 */
>>   	if (!done) {
>>   		dac->flags |= XFS_DAC_DEFER_FINISH;
>> +		trace_xfs_attr_rmtval_remove_return(dac->dela_state, args->dp);
>>   		return -EAGAIN;
>>   	}
>>   
>> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
>> index 363e1bf..7993f55 100644
>> --- a/fs/xfs/xfs_trace.h
>> +++ b/fs/xfs/xfs_trace.h
>> @@ -3927,6 +3927,31 @@ DEFINE_EVENT(xfs_eofblocks_class, name,	\
>>   DEFINE_EOFBLOCKS_EVENT(xfs_ioc_free_eofblocks);
>>   DEFINE_EOFBLOCKS_EVENT(xfs_blockgc_free_space);
>>   
>> +DECLARE_EVENT_CLASS(xfs_das_state_class,
>> +	TP_PROTO(int das, struct xfs_inode *ip),
>> +	TP_ARGS(das, ip),
>> +	TP_STRUCT__entry(
>> +		__field(int, das)
>> +		__field(xfs_ino_t, ino)
>> +	),
>> +	TP_fast_assign(
>> +		__entry->das = das;
>> +		__entry->ino = ip->i_ino;
>> +	),
>> +	TP_printk("state change %d ino 0x%llx",
>> +		  __entry->das, __entry->ino)
>> +)
>> +
>> +#define DEFINE_DAS_STATE_EVENT(name) \
>> +DEFINE_EVENT(xfs_das_state_class, name, \
>> +	TP_PROTO(int das, struct xfs_inode *ip), \
>> +	TP_ARGS(das, ip))
>> +DEFINE_DAS_STATE_EVENT(xfs_attr_set_fmt_return);
>> +DEFINE_DAS_STATE_EVENT(xfs_attr_set_iter_return);
>> +DEFINE_DAS_STATE_EVENT(xfs_attr_node_addname_return);
>> +DEFINE_DAS_STATE_EVENT(xfs_attr_node_removename_iter_return);
>> +DEFINE_DAS_STATE_EVENT(xfs_attr_node_remove_rmt_return);
>> +DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_remove_return);
>>   #endif /* _TRACE_XFS_H */
>>   
>>   #undef TRACE_INCLUDE_PATH
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-25 22:28         ` Allison Henderson
@ 2021-02-28 15:39           ` Brian Foster
  2021-03-02  8:26             ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Brian Foster @ 2021-02-28 15:39 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 25, 2021 at 03:28:00PM -0700, Allison Henderson wrote:
> 
> 
> On 2/25/21 7:22 AM, Brian Foster wrote:
> > On Thu, Feb 25, 2021 at 12:01:10AM -0700, Allison Henderson wrote:
> > > 
> > > 
> > > On 2/24/21 11:45 AM, Brian Foster wrote:
> > > > On Thu, Feb 18, 2021 at 09:53:37AM -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 has become xfs_attr_remove_iter, which
> > > > > uses a sort of state machine like switch to keep track of where it was
> > > > > when EAGAIN was returned. xfs_attr_node_removename has also been
> > > > > modified to use the switch, and a new version of xfs_attr_remove_args
> > > > > consists of a simple loop to refresh the transaction until the operation
> > > > > is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
> > > > >    fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
> > > > >    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, 294 insertions(+), 83 deletions(-)
> > > > > 
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > > > index 56d4b56..d46b92a 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > ...
> > > > > @@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
> > > > >     *
> > > > >     * This routine will find the blocks of the name to remove, remove them and
> > > > >     * shrink the tree if needed.
> > > > > + *
> > > > > + * This routine is meant to function as either an inline or delayed operation,
> > > > > + * and may return -EAGAIN when the transaction needs to be rolled.  Calling
> > > > > + * functions will need to handle this, and recall the function until a
> > > > > + * successful error code is returned.
> > > > >     */
> > > > >    STATIC int
> > > > > -xfs_attr_node_removename(
> > > > > -	struct xfs_da_args	*args)
> > > > > +xfs_attr_node_removename_iter(
> > > > > +	struct xfs_delattr_context	*dac)
> > > > >    {
> > > > > -	struct xfs_da_state	*state = NULL;
> > > > > -	int			retval, error;
> > > > > -	struct xfs_inode	*dp = args->dp;
> > > > > +	struct xfs_da_args		*args = dac->da_args;
> > > > > +	struct xfs_da_state		*state = NULL;
> > > > > +	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;
> > > > > -
> > > > > -	error = xfs_attr_node_remove_step(args, state);
> > > > > -	if (error)
> > > > > -		goto out;
> > > > > -
> > > > > -	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 (!dac->da_state) {
> > > > > +		error = xfs_attr_node_removename_setup(dac);
> > > > >    		if (error)
> > > > >    			goto out;
> > > > > +	}
> > > > > +	state = dac->da_state;
> > > > > +
> > > > > +	switch (dac->dela_state) {
> > > > > +	case XFS_DAS_UNINIT:
> > > > >    		/*
> > > > > -		 * Commit the Btree join operation and start a new trans.
> > > > > +		 * repeatedly remove remote blocks, remove the entry and join.
> > > > > +		 * returns -EAGAIN or 0 for completion of the step.
> > > > >    		 */
> > > > > -		error = xfs_trans_roll_inode(&args->trans, dp);
> > > > > +		error = xfs_attr_node_remove_step(dac);
> > > > >    		if (error)
> > > > > -			goto out;
> > > > > -	}
> > > > > +			break;
> > > > 
> > > > Hmm.. so re: my comment further down on xfs_attr_rmtval_remove(),
> > > > wouldn't that change semantics here? I.e., once remote blocks are
> > > > removed this would previously carry on with a clean transaction. Now it
> > > > looks like we'd carry on with the dirty transaction that removed the
> > > > last remote extent. This suggests that perhaps we should return once
> > > > more and fall into a new state to remove the name..?
> > > I suspect the diff might be making this a bit difficult to see.  The roll
> > > that you see being removed here belongs to the transaction we hoisted up  in
> > > patch 3 which happens after the clean up below, and we have the
> > > corresponding EAGAIN fot that one.  I think the diff gets things a little
> > > interlaced here because the switch adds another level of indentation.
> > > 
> > 
> > Hmm.. the roll in patch 3 appears to be related to the _cleanup()
> > helper. What I'm referring to here is the state of the transaction after
> > the final remote block is removed from the attr. I'm not sure we're
> > talking about the same thing here..
> > 
> > > some times i do like to I use a graphical diffviewer like diffuse when
> > > patches get weird like this.  Something like this:
> > > 
> > > git config --global diff.tool  diffuse
> > > git difftool 3c53e49 e201c09
> > > 
> > > You'd need to download the branch and also the diffuse tool, but sometimes i
> > > think it makes some of these diffs a bit easier to see
> > > 
> > 
> > I think it's easier just to refer to the code directly. The current
> > upstream code flows down into:
> > 
> > ...
> > xfs_attr_node_removename()
> >   xfs_attr_node_remove_rmt()
> >    xfs_attr_rmtval_remove()
> > 
> > ... which then implements the following loop:
> > 
> >          do {
> >                  retval = __xfs_attr_rmtval_remove(args);
> >                  if (retval && retval != -EAGAIN)
> >                          return retval;
> > 
> >                  /*
> >                   * Close out trans and start the next one in the chain.
> >                   */
> >                  error = xfs_trans_roll_inode(&args->trans, args->dp);
> >                  if (error)
> >                          return error;
> >          } while (retval == -EAGAIN);
> > 
> > This rolls the transaction when retval == -EAGAIN or retval == 0, thus
> > always returns with a clean transaction after the remote block removal
> > completes.
> > 
> > The code as of this patch does:
> > 
> > ...
> > xfs_attr_node_removename_iter()
> >   xfs_attr_node_remove_step()
> >    xfs_attr_node_remove_rmt()
> >     __xfs_attr_rmtval_remove()
> > 
> > ... which either returns -EAGAIN (since the roll is now implemented at
> > the very top) or 0 when done == true. The transaction might be dirty in
> > the latter case, but xfs_attr_node_removename_iter() moves right on to
> > xfs_attr_node_remove_cleanup() which can now do more work in that same
> > transaction. Am I following that correctly?
> > 
> > > Also, it would be
> > > > nice to remove the several seemingly unnecessary layers of indirection
> > > > here. For example, something like the following (also considering my
> > > > comment above wrt to xfs_attr_remove_iter() and UNINIT):
> > > > 
> > > > 	case UNINIT:
> > > > 		...
> > > > 		/* fallthrough */
> > > > 	case RMTBLK:
> > > > 		if (args->rmtblkno > 0) {
> > > > 			dac->dela_state = RMTBLK;
> > > > 			error = __xfs_attr_rmtval_remove(dac);
> > > > 			if (error)
> > > > 				break;
> > > > 
> > > > 			ASSERT(args->rmtblkno == 0);
> > > > 			xfs_attr_refillstate(state);
> > > > 			dac->flags |= XFS_DAC_DEFER_FINISH;
> > > > 			dac->dela_state = RMNAME;
> > > > 			return -EAGAIN;
> > > > 		}
> > > Ok, this looks to me like we've hoisted both xfs_attr_node_remove_rmt and
> > > xfs_attr_node_remove_step into this scope, but I still think this adds an
> > > extra roll where non previously was.  With out that extra EAGAIN, I think we
> > > are fine to have all that just under the UNINIT case.  I also think it's
> > > also worth noteing here that this is kind of a reverse of patch 1, which I
> > > think we put in for reasons of trying to modularize the higher level
> > > functions as much as possible.
> > > 
> > > I suspect some of where you were going with this may have been influenced by
> > > the earlier diff confusion too.  Maybe take a second look there before we go
> > > too much down this change....
> > > 
> > 
> > I can certainly be getting lost somewhere in all the refactoring. If so,
> > can you point out where in the flow described above?
> Ok, I think see it.  So basically I think this means we cant have the
> helpers because it's ambiguos as to if the transaction is dirty or not.  I
> dont see that there's anything in the review history where we rationalized
> that away, so I think we just overlooked it.  So I think what this means is
> that we need to reverse apply commit 72b97ea40d (which is where we added
> xfs_attr_node_remove_rmt), then drop patch 1 which leaves no need for patch
> 3, since the transaction will have not moved.  Then add state RMTBLK?  I
> think that arrives at what you have here.
> 

It's not clear to me if anything needs to change before this patch or
the changes can just fold into this patch itself. You probably have a
better sense of that than I do atm. From my perspective, I think we want
that transaction to roll after the final remote extent removal unless we
had some reason to explicitly change existing behavior. This used to be
handled by the old loop that rolled the transaction down in the remote
block removal code. ISTM that the proper way to maintain the same
behavior in the new state machine code is to unconditionally fall out of
a RMTBLKREMOVE state with an -EAGAIN from _iter().

IOW, __xfs_attr_rmtval_remove() returns -EAGAIN when it has more work to
do. _iter() returns -EAGAIN when __xfs_attr_rmtval_remove() was called,
finished its work, but we need to roll the transaction before the next
step of the operation..

Brian

> Allison
> 
> > 
> > Brian
> > 
> > > 
> > > > 		/* fallthrough */
> > > > 	case RMNAME:
> > > > 		...
> > > > 	...
> > > > 
> > > > > -	/*
> > > > > -	 * 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);
> > > > > +		retval = xfs_attr_node_remove_cleanup(args, state);
> > > > ...
> > > I think the overlooked EAGAIN was in this area that got clipped out.....
> > > 
> > > > > diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
> > > > > index 48d8e9c..f09820c 100644
> > > > > --- a/fs/xfs/libxfs/xfs_attr_remote.c
> > > > > +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> > > > ...
> > > > > @@ -685,31 +687,29 @@ c(
> > > > >    	 * 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;
> > > > 
> > > > Previously this would roll once and exit the loop on retval == 0. Now it
> > > > looks like we break out of the loop immediately. Why the change?
> > > 
> > > Gosh, I think sometime in reviewing v9, we had come up with a
> > > "xfs_attr_roll_again" helper that took the error code as a paramater and
> > > decided whether or not to roll.  And then in v10 i think people thought that
> > > was weird and we turned it into xfs_attr_trans_roll.  I think I likley
> > > forgot to restore the orginal retval handling here.  This whole function
> > > disappears in the next patch, but the original error handling should be
> > > restored to keep things consistent. Thx for the catch!
> > > 
> > > 
> > > Thx for the reviews!!  I know it's complicated!  I've chased my tail many
> > > times with it myself :-)
> > > 
> > > Allison
> > > 
> > > 
> > > 
> > > 
> > > > 
> > > > Brian
> > > > 
> > > > > -		/*
> > > > > -		 * 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 dont 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 reminicent 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] 87+ messages in thread

* Re: [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-02-27  0:54     ` Allison Henderson
@ 2021-03-01 18:00       ` Darrick J. Wong
  2021-03-02  8:26         ` Allison Henderson
  0 siblings, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-03-01 18:00 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Fri, Feb 26, 2021 at 05:54:51PM -0700, Allison Henderson wrote:
> 
> 
> On 2/25/21 9:02 PM, Darrick J. Wong wrote:
> > On Thu, Feb 18, 2021 at 09:53:32AM -0700, 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>
> > > ---
> > >   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 205ad26..bee8d3fb 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_work(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);
> > > @@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
> > >   			return error;
> > >   	}
> > > +	error = xfs_attr_node_addname_work(args);
> > > +out:
> > > +	if (state)
> > > +		xfs_da_state_free(state);
> > > +	if (error)
> > > +		return error;
> > > +	return retval;
> > > +}
> > > +
> > > +
> > > +STATIC
> > > +int xfs_attr_node_addname_work(
> > 
> > What, erm, work does this function do?  Since it survives to the end of
> > the patchset, I think this needs a better name (or at least needs a
> > comment about what it's actually supposed to do).
> To directly answer the question: it's here to help xfs_attr_set_iter not be
> any bigger than it has to. I think we likely struggled with the name because
> it's almost like it's just the "remainder" of the operation that doesnt need
> state management
> 
> > 
> > AFAICT you're splitting node_addname() into two functions because we're
> > at a transaction roll point, and this "_work" function exists to remove
> > the copy of the xattr key that has the "INCOMPLETE" bit set (aka the old
> > one), right?
> Thats about right. Maybe just a quick comment?
> /*
>  * Removes the old xattr key marked with the INCOMPLETE bit
>  */
> 
> I suppose we could consider something like
> "xfs_attr_node_addname_remv_incomplete"?  Or xfs_attr_node_addname_cleanup?
> Trying to cram it into the name maybe getting a bit wordy too.

xfs_attr_node_addname_clear_incomplete?

--D

> 
> Allison
> > 
> > --D
> > 
> > > +	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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname
  2021-02-18 16:53 ` [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname Allison Henderson
  2021-02-24 18:42   ` Brian Foster
@ 2021-03-01 18:05   ` Darrick J. Wong
  2021-03-02  8:26     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-03-01 18:05 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:34AM -0700, 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>

Simple enough transplant,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c | 165 ++++++++++++++++++++++++-----------------------
>  1 file changed, 83 insertions(+), 82 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 4333b61..19a532a 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,
> @@ -268,8 +269,9 @@ 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;
> +	int			retval = 0;
>  
>  	/*
>  	 * If the attribute list is already in leaf format, jump straight to
> @@ -320,8 +322,82 @@ 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);
> +		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_work(args);
> +out:
> +	if (state)
> +		xfs_da_state_free(state);
> +	if (error)
> +		return error;
> +	return retval;
>  
> -	return error;
>  }
>  
>  /*
> @@ -955,7 +1031,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);
>  
> @@ -963,8 +1039,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
> @@ -1010,85 +1086,10 @@ 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_work(args);
>  out:
>  	if (state)
>  		xfs_da_state_free(state);
> -	if (error)
> -		return error;
> -	return retval;
> +	return error;
>  }
>  
>  
> -- 
> 2.7.4
> 

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

* Re: [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname
  2021-02-18 16:53 ` [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
  2021-02-24 18:42   ` Brian Foster
@ 2021-03-01 18:19   ` Darrick J. Wong
  2021-03-02  8:26     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-03-01 18:19 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:35AM -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 asetheic right away, but it is a

"aesthetic"

> preliminary step to to manageing the state machine code.

"to merging in" ?

The goto label is ugly, but afaict this patch moves code and the next
one rearranges it the way you ultimately want it, right?

With spelling fixed,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D


> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  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 19a532a..bfd4466 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.
> @@ -269,8 +269,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;
>  	int			retval = 0;
>  
>  	/*
> @@ -286,10 +287,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.
>  		 */
> @@ -731,115 +823,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 10/22] xfs: Hoist node transaction handling
  2021-02-18 16:53 ` [PATCH v15 10/22] xfs: Hoist node transaction handling Allison Henderson
  2021-02-24 18:43   ` Brian Foster
@ 2021-03-01 18:20   ` Darrick J. Wong
  2021-03-02  8:26     ` Allison Henderson
  1 sibling, 1 reply; 87+ messages in thread
From: Darrick J. Wong @ 2021-03-01 18:20 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:36AM -0700, 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.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c | 53 +++++++++++++++++++++++++-----------------------
>  1 file changed, 28 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index bfd4466..56d4b56 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -288,8 +288,34 @@ 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)
>  			return error;

With the braces and indenting fixed the way Brian said,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

>  
> @@ -381,32 +407,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 12/22] xfs: Add delay ready attr set routines
  2021-02-18 16:53 ` [PATCH v15 12/22] xfs: Add delay ready attr set routines Allison Henderson
@ 2021-03-02  1:39   ` Darrick J. Wong
  0 siblings, 0 replies; 87+ messages in thread
From: Darrick J. Wong @ 2021-03-02  1:39 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Thu, Feb 18, 2021 at 09:53:38AM -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>

I /think/ this looks reasonable.

By the way, would you mind going through the comments in these patches
looking for typos?  I noticed a few ("sucesfull") lurking here and
there.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/libxfs/xfs_attr.c        | 448 ++++++++++++++++++++++++----------------
>  fs/xfs/libxfs/xfs_attr.h        | 241 ++++++++++++++++++++-
>  fs/xfs/libxfs/xfs_attr_remote.c |  98 ++++++---
>  fs/xfs/libxfs/xfs_attr_remote.h |   5 +-
>  fs/xfs/xfs_trace.h              |   1 -
>  5 files changed, 583 insertions(+), 210 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index d46b92a..c7b86d5 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -53,16 +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_work(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_work(struct xfs_delattr_context *dac);
>  STATIC int xfs_attr_node_removename_iter(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(
> @@ -226,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)
>  {
> @@ -249,29 +249,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;
>  
> @@ -280,93 +306,140 @@ 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);
> +	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;
> -	int			retval = 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;
> +	int				retval = 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 sucesfull, 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;
> +			dac->dela_state = XFS_DAS_FOUND_NBLK;
> +			return -EAGAIN;
>  		}
> -		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;
> +		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
> @@ -395,22 +468,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;
>  
> -			error = xfs_attr_rmtval_remove(args);
> +		/* Set state in case xfs_attr_rmtval_remove returns -EAGAIN */
> +		dac->dela_state = XFS_DAS_RM_LBLK;
> +
> +		/* fallthrough */
> +	case XFS_DAS_RM_LBLK:
> +		if (args->rmtblkno) {
> +			error = __xfs_attr_rmtval_remove(dac);
>  			if (error)
>  				return error;
>  		}
> @@ -435,94 +512,117 @@ 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);
> +			retval = error;
> +			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);
> -		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;
> +		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);
> +		/* 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_work(dac);
> +
> +out:
> +		if (state)
> +			xfs_da_state_free(state);
>  		if (error)
>  			return error;
> -	}
> +		return retval;
>  
> -	error = xfs_attr_node_addname_work(args);
> -out:
> -	if (state)
> -		xfs_da_state_free(state);
> -	if (error)
> -		return error;
> -	return retval;
> +	default:
> +		ASSERT(dac->dela_state != XFS_DAS_RM_SHRINK);
> +		break;
> +	}
>  
> +	return error;
>  }
>  
> +
>  /*
>   * Return EEXIST if attr is found, or ENOATTR if not
>   */
> @@ -1021,18 +1121,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 out;
> +		return retval;
>  
>  	if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE))
>  		goto out;
> @@ -1058,8 +1158,8 @@ xfs_attr_node_addname_find_attr(
>  
>  	return 0;
>  out:
> -	if (*state)
> -		xfs_da_state_free(*state);
> +	if (dac->da_state)
> +		xfs_da_state_free(dac->da_state);
>  	return retval;
>  }
>  
> @@ -1072,20 +1172,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);
> @@ -1101,18 +1205,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;
>  		}
>  
> @@ -1125,9 +1226,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.
> @@ -1144,8 +1243,9 @@ xfs_attr_node_addname(
>  
>  STATIC
>  int xfs_attr_node_addname_work(
> -	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 3154ef4..603887e 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -135,6 +135,233 @@ struct xfs_attr_list_context {
>   *              v
>   *            done
>   *
> + *
> + * Below is a state machine diagram for attr set operations.
> + *
> + * It seems the challenge with undertanding 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
> + * statemachine. 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 statemachine to
> + * a subroutine, it needs to respect the simple rule that it doesn't "own" the
> + * statemachine 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 statemachine.
> + *
> + *  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 <──────┘
> + *
>   */
>  
>  /*
> @@ -149,12 +376,20 @@ struct xfs_attr_list_context {
>  enum xfs_delattr_state {
>  	XFS_DAS_UNINIT		= 0,  /* No state has been set yet */
>  	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
> @@ -162,6 +397,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;
>  
> @@ -188,7 +428,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 f09820c..6af86bf 100644
> --- a/fs/xfs/libxfs/xfs_attr_remote.c
> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
> @@ -441,7 +441,7 @@ xfs_attr_rmtval_get(
>   * Find a "hole" in the attribute address space large enough for us to drop the
>   * new attribute's value into
>   */
> -STATIC int
> +int
>  xfs_attr_rmt_find_hole(
>  	struct xfs_da_args	*args)
>  {
> @@ -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 63ecbc6..363e1bf 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -1942,7 +1942,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] 87+ messages in thread

* Re: [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations
  2021-02-27  0:56     ` Allison Henderson
@ 2021-03-02  7:13       ` Allison Henderson
  2021-03-03 11:50         ` Brian Foster
  0 siblings, 1 reply; 87+ messages in thread
From: Allison Henderson @ 2021-03-02  7:13 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 2/26/21 5:56 PM, Allison Henderson wrote:
> 
> 
> On 2/25/21 9:58 PM, Darrick J. Wong wrote:
>> On Thu, Feb 18, 2021 at 09:53:42AM -0700, Allison Henderson wrote:
>>> Currently attributes are modified directly across one or more
>>> transactions. But they are not logged or replayed in the event of an
>>> error. The goal of delayed attributes is to enable logging and replaying
>>> of attribute operations using the existing delayed operations
>>> infrastructure.  This will later enable the attributes to become part of
>>> larger multi part operations that also must first be recorded to the
>>> log.  This is mostly of interest in the scheme of parent pointers which
>>> would need to maintain an attribute containing parent inode information
>>> any time an inode is moved, created, or removed.  Parent pointers would
>>> then be of interest to any feature that would need to quickly derive an
>>> inode path from the mount point. Online scrub, nfs lookups and fs grow
>>> or shrink operations are all features that could take advantage of this.
>>>
>>> This patch adds two new log item types for setting or removing
>>> attributes as deferred operations.  The xfs_attri_log_item logs an
>>> intent to set or remove an attribute.  The corresponding
>>> xfs_attrd_log_item holds a reference to the xfs_attri_log_item and is
>>> freed once the transaction is done.  Both log items use a generic
>>> xfs_attr_log_format structure that contains the attribute name, value,
>>> flags, inode, and an op_flag that indicates if the operations is a set
>>> or remove.
>>>
>>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>>
>> I saw a 36K patch and thought "Jeez!  This should be broken up!"
>>
>> Then I looked at atomic extent swap (which does add its log item code
>> gradually) and decided that maybe you just don't want to explode the
>> patchset from 22 to 30.  That sounds (kind of) reasonable.
>>
>> (...and by 'kind of', I probably mean that if I end up taking everything
>> up to this patch for 5.13 then this part really ought to get broken up a
>> bit for a future submission.  But let's wait until the end of this
>> review to decide that...)
> 
> Yes, I've thought about falling back into a "Delay ready" sub series 
> again.  It think the focus right now is the state machine and getting 
> everyone on board with that, so I think it's reasonable to delay this 
> part for now.  Since really the statemachine is a bit of a pre-req for 
> this.
> 
> Allison
I'm sorry, I didnt see you had more feed back.  Comments below...
>>
>>> ---
>>>   fs/xfs/Makefile                 |   1 +
>>>   fs/xfs/libxfs/xfs_attr.c        |   7 +-
>>>   fs/xfs/libxfs/xfs_attr.h        |  31 ++
>>>   fs/xfs/libxfs/xfs_defer.c       |   1 +
>>>   fs/xfs/libxfs/xfs_defer.h       |   3 +
>>>   fs/xfs/libxfs/xfs_log_format.h  |  44 ++-
>>>   fs/xfs/libxfs/xfs_log_recover.h |   2 +
>>>   fs/xfs/scrub/common.c           |   2 +
>>>   fs/xfs/xfs_acl.c                |   2 +
>>>   fs/xfs/xfs_attr_item.c          | 828 
>>> ++++++++++++++++++++++++++++++++++++++++
>>>   fs/xfs/xfs_attr_item.h          |  52 +++
>>>   fs/xfs/xfs_attr_list.c          |   1 +
>>>   fs/xfs/xfs_ioctl.c              |   2 +
>>>   fs/xfs/xfs_ioctl32.c            |   2 +
>>>   fs/xfs/xfs_iops.c               |   2 +
>>>   fs/xfs/xfs_log.c                |   4 +
>>>   fs/xfs/xfs_log_recover.c        |   2 +
>>>   fs/xfs/xfs_ondisk.h             |   2 +
>>>   fs/xfs/xfs_xattr.c              |   1 +
>>>   19 files changed, 984 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
>>> index 04611a1..b056cfc 100644
>>> --- a/fs/xfs/Makefile
>>> +++ b/fs/xfs/Makefile
>>> @@ -102,6 +102,7 @@ xfs-y                += xfs_log.o \
>>>                      xfs_buf_item_recover.o \
>>>                      xfs_dquot_item_recover.o \
>>>                      xfs_extfree_item.o \
>>> +                   xfs_attr_item.o \
>>>                      xfs_icreate_item.o \
>>>                      xfs_inode_item.o \
>>>                      xfs_inode_item_recover.o \
>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>> index 2b8e481..e4c1b4b 100644
>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>> @@ -24,6 +24,7 @@
>>>   #include "xfs_quota.h"
>>>   #include "xfs_trans_space.h"
>>>   #include "xfs_trace.h"
>>> +#include "xfs_attr_item.h"
>>>   /*
>>>    * xfs_attr.c
>>> @@ -61,8 +62,8 @@ 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_attr_set_iter(struct xfs_delattr_context *dac,
>>> +              struct xfs_buf **leaf_bp);
>>>   int
>>>   xfs_inode_hasattr(
>>> @@ -144,7 +145,7 @@ xfs_attr_get(
>>>   /*
>>>    * Calculate how many blocks we need for the new attribute,
>>>    */
>>> -STATIC int
>>> +int
>>>   xfs_attr_calc_size(
>>>       struct xfs_da_args    *args,
>>>       int            *local)
>>> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
>>> index 603887e..ee79763 100644
>>> --- a/fs/xfs/libxfs/xfs_attr.h
>>> +++ b/fs/xfs/libxfs/xfs_attr.h
>>> @@ -28,6 +28,11 @@ struct xfs_attr_list_context;
>>>    */
>>>   #define    ATTR_MAX_VALUELEN    (64*1024)    /* max length of a 
>>> value */
>>> +static inline bool xfs_hasdelattr(struct xfs_mount *mp)
>>> +{
>>> +    return false;
>>> +}
>>> +
>>>   /*
>>>    * Kernel-internal version of the attrlist cursor.
>>>    */
>>> @@ -390,6 +395,7 @@ enum xfs_delattr_state {
>>>    */
>>>   #define XFS_DAC_DEFER_FINISH        0x01 /* finish the transaction */
>>>   #define XFS_DAC_LEAF_ADDNAME_INIT    0x02 /* xfs_attr_leaf_addname 
>>> init*/
>>> +#define XFS_DAC_DELAYED_OP_INIT        0x04 /* delayed operations 
>>> init*/
>>>   /*
>>>    * Context used for keeping track of delayed attribute operations
>>> @@ -397,6 +403,11 @@ enum xfs_delattr_state {
>>>   struct xfs_delattr_context {
>>>       struct xfs_da_args      *da_args;
>>> +    /*
>>> +     * Used by xfs_attr_set to hold a leaf buffer across a 
>>> transaction roll
>>> +     */
>>> +    struct xfs_buf        *leaf_bp;
>>> +
>>>       /* Used in xfs_attr_rmtval_set_blk to roll through allocating 
>>> blocks */
>>>       struct xfs_bmbt_irec    map;
>>>       xfs_dablk_t        lblkno;
>>> @@ -410,6 +421,23 @@ struct xfs_delattr_context {
>>>       enum xfs_delattr_state  dela_state;
>>>   };
>>> +/*
>>> + * List of attrs to commit later.
>>> + */
>>> +struct xfs_attr_item {
>>> +    struct xfs_delattr_context    xattri_dac;
>>> +
>>> +    /*
>>> +     * Indicates if the attr operation is a set or a remove
>>> +     * XFS_ATTR_OP_FLAGS_{SET,REMOVE}
>>> +     */
>>> +    uint32_t            xattri_op_flags;
>>> +
>>> +    /* used to log this item to an intent */
>>> +    struct list_head        xattri_list;
>>> +};
>>> +
>>> +
>>>   
>>> /*======================================================================== 
>>>
>>>    * Function prototypes for the kernel.
>>>    
>>> *========================================================================*/ 
>>>
>>> @@ -425,11 +453,14 @@ int xfs_attr_get_ilocked(struct xfs_da_args 
>>> *args);
>>>   int xfs_attr_get(struct xfs_da_args *args);
>>>   int xfs_attr_set(struct xfs_da_args *args);
>>>   int xfs_attr_set_args(struct xfs_da_args *args);
>>> +int xfs_attr_set_iter(struct xfs_delattr_context *dac,
>>> +              struct xfs_buf **leaf_bp);
>>>   int xfs_has_attr(struct xfs_da_args *args);
>>>   int xfs_attr_remove_args(struct xfs_da_args *args);
>>>   int xfs_attr_remove_iter(struct xfs_delattr_context *dac);
>>>   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);
>>> +int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
>>>   #endif    /* __XFS_ATTR_H__ */
>>> diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
>>> index eff4a12..e9caff7 100644
>>> --- a/fs/xfs/libxfs/xfs_defer.c
>>> +++ b/fs/xfs/libxfs/xfs_defer.c
>>> @@ -178,6 +178,7 @@ static const struct xfs_defer_op_type 
>>> *defer_op_types[] = {
>>>       [XFS_DEFER_OPS_TYPE_RMAP]    = &xfs_rmap_update_defer_type,
>>>       [XFS_DEFER_OPS_TYPE_FREE]    = &xfs_extent_free_defer_type,
>>>       [XFS_DEFER_OPS_TYPE_AGFL_FREE]    = &xfs_agfl_free_defer_type,
>>> +    [XFS_DEFER_OPS_TYPE_ATTR]    = &xfs_attr_defer_type,
>>>   };
>>>   static void
>>> diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
>>> index 05472f7..72a5789 100644
>>> --- a/fs/xfs/libxfs/xfs_defer.h
>>> +++ b/fs/xfs/libxfs/xfs_defer.h
>>> @@ -19,6 +19,7 @@ enum xfs_defer_ops_type {
>>>       XFS_DEFER_OPS_TYPE_RMAP,
>>>       XFS_DEFER_OPS_TYPE_FREE,
>>>       XFS_DEFER_OPS_TYPE_AGFL_FREE,
>>> +    XFS_DEFER_OPS_TYPE_ATTR,
>>>       XFS_DEFER_OPS_TYPE_MAX,
>>>   };
>>> @@ -63,6 +64,8 @@ extern const struct xfs_defer_op_type 
>>> xfs_refcount_update_defer_type;
>>>   extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
>>>   extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
>>>   extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
>>> +extern const struct xfs_defer_op_type xfs_attr_defer_type;
>>> +
>>>   /*
>>>    * This structure enables a dfops user to detach the chain of deferred
>>> diff --git a/fs/xfs/libxfs/xfs_log_format.h 
>>> b/fs/xfs/libxfs/xfs_log_format.h
>>> index 8bd00da..19963b6 100644
>>> --- a/fs/xfs/libxfs/xfs_log_format.h
>>> +++ b/fs/xfs/libxfs/xfs_log_format.h
>>> @@ -117,7 +117,12 @@ struct xfs_unmount_log_format {
>>>   #define XLOG_REG_TYPE_CUD_FORMAT    24
>>>   #define XLOG_REG_TYPE_BUI_FORMAT    25
>>>   #define XLOG_REG_TYPE_BUD_FORMAT    26
>>> -#define XLOG_REG_TYPE_MAX        26
>>> +#define XLOG_REG_TYPE_ATTRI_FORMAT    27
>>> +#define XLOG_REG_TYPE_ATTRD_FORMAT    28
>>> +#define XLOG_REG_TYPE_ATTR_NAME    29
>>> +#define XLOG_REG_TYPE_ATTR_VALUE    30
>>> +#define XLOG_REG_TYPE_MAX        30
>>> +
>>>   /*
>>>    * Flags to log operation header
>>> @@ -240,6 +245,8 @@ typedef struct xfs_trans_header {
>>>   #define    XFS_LI_CUD        0x1243
>>>   #define    XFS_LI_BUI        0x1244    /* bmbt update intent */
>>>   #define    XFS_LI_BUD        0x1245
>>> +#define    XFS_LI_ATTRI        0x1246  /* attr set/remove intent*/
>>> +#define    XFS_LI_ATTRD        0x1247  /* attr set/remove done */
>>>   #define XFS_LI_TYPE_DESC \
>>>       { XFS_LI_EFI,        "XFS_LI_EFI" }, \
>>> @@ -255,7 +262,9 @@ typedef struct xfs_trans_header {
>>>       { XFS_LI_CUI,        "XFS_LI_CUI" }, \
>>>       { XFS_LI_CUD,        "XFS_LI_CUD" }, \
>>>       { XFS_LI_BUI,        "XFS_LI_BUI" }, \
>>> -    { XFS_LI_BUD,        "XFS_LI_BUD" }
>>> +    { XFS_LI_BUD,        "XFS_LI_BUD" }, \
>>> +    { XFS_LI_ATTRI,        "XFS_LI_ATTRI" }, \
>>> +    { XFS_LI_ATTRD,        "XFS_LI_ATTRD" }
>>>   /*
>>>    * Inode Log Item Format definitions.
>>> @@ -863,4 +872,35 @@ struct xfs_icreate_log {
>>>       __be32        icl_gen;    /* inode generation number to use */
>>>   };
>>> +/*
>>> + * Flags for deferred attribute operations.
>>> + * Upper bits are flags, lower byte is type code
>>> + */
>>> +#define XFS_ATTR_OP_FLAGS_SET        1    /* Set the attribute */
>>> +#define XFS_ATTR_OP_FLAGS_REMOVE    2    /* Remove the attribute */
>>> +#define XFS_ATTR_OP_FLAGS_TYPE_MASK    0x0FF    /* Flags type mask */
>>> +
>>> +/*
>>> + * This is the structure used to lay out an attr log item in the
>>> + * log.
>>> + */
>>> +struct xfs_attri_log_format {
>>> +    uint16_t    alfi_type;    /* attri log item type */
>>> +    uint16_t    alfi_size;    /* size of this item */
>>> +    uint32_t    __pad;        /* pad to 64 bit aligned */
>>> +    uint64_t    alfi_id;    /* attri identifier */
>>> +    uint64_t    alfi_ino;    /* the inode for this attr operation */
>>> +    uint32_t    alfi_op_flags;    /* marks the op as a set or remove */
>>> +    uint32_t    alfi_name_len;    /* attr name length */
>>> +    uint32_t    alfi_value_len;    /* attr value length */
>>> +    uint32_t    alfi_attr_flags;/* attr flags */
>>> +};
>>> +
>>> +struct xfs_attrd_log_format {
>>> +    uint16_t    alfd_type;    /* attrd log item type */
>>> +    uint16_t    alfd_size;    /* size of this item */
>>> +    uint32_t    __pad;        /* pad to 64 bit aligned */
>>> +    uint64_t    alfd_alf_id;    /* id of corresponding attri */
>>> +};
>>> +
>>>   #endif /* __XFS_LOG_FORMAT_H__ */
>>> diff --git a/fs/xfs/libxfs/xfs_log_recover.h 
>>> b/fs/xfs/libxfs/xfs_log_recover.h
>>> index 3cca2bf..b6e5514 100644
>>> --- a/fs/xfs/libxfs/xfs_log_recover.h
>>> +++ b/fs/xfs/libxfs/xfs_log_recover.h
>>> @@ -72,6 +72,8 @@ extern const struct xlog_recover_item_ops 
>>> xlog_rui_item_ops;
>>>   extern const struct xlog_recover_item_ops xlog_rud_item_ops;
>>>   extern const struct xlog_recover_item_ops xlog_cui_item_ops;
>>>   extern const struct xlog_recover_item_ops xlog_cud_item_ops;
>>> +extern const struct xlog_recover_item_ops xlog_attri_item_ops;
>>> +extern const struct xlog_recover_item_ops xlog_attrd_item_ops;
>>>   /*
>>>    * Macros, structures, prototypes for internal log manager use.
>>> diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
>>> index 53456f3..ac35121 100644
>>> --- a/fs/xfs/scrub/common.c
>>> +++ b/fs/xfs/scrub/common.c
>>> @@ -24,6 +24,8 @@
>>>   #include "xfs_rmap_btree.h"
>>>   #include "xfs_log.h"
>>>   #include "xfs_trans_priv.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_reflink.h"
>>>   #include "scrub/scrub.h"
>>> diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
>>> index 779cb73..79f7bd2 100644
>>> --- a/fs/xfs/xfs_acl.c
>>> +++ b/fs/xfs/xfs_acl.c
>>> @@ -10,6 +10,8 @@
>>>   #include "xfs_trans_resv.h"
>>>   #include "xfs_mount.h"
>>>   #include "xfs_inode.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_trace.h"
>>>   #include "xfs_error.h"
>>> diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
>>> new file mode 100644
>>> index 0000000..8c8f72d
>>> --- /dev/null
>>> +++ b/fs/xfs/xfs_attr_item.c
>>> @@ -0,0 +1,828 @@
>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>> +/*
>>> + * Copyright (C) 2021 Oracle.  All Rights Reserved.
>>> + * Author: Allison Collins <allison.henderson@oracle.com>
>>> + */
>>> +
>>> +#include "xfs.h"
>>> +#include "xfs_fs.h"
>>> +#include "xfs_format.h"
>>> +#include "xfs_log_format.h"
>>> +#include "xfs_trans_resv.h"
>>> +#include "xfs_bit.h"
>>> +#include "xfs_shared.h"
>>> +#include "xfs_mount.h"
>>> +#include "xfs_defer.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_log_format.h"
>>> +#include "xfs_trans.h"
>>> +#include "xfs_bmap.h"
>>> +#include "xfs_bmap_btree.h"
>>> +#include "xfs_trans_priv.h"
>>> +#include "xfs_buf_item.h"
>>> +#include "xfs_attr_item.h"
>>> +#include "xfs_log.h"
>>> +#include "xfs_btree.h"
>>> +#include "xfs_rmap.h"
>>> +#include "xfs_inode.h"
>>> +#include "xfs_icache.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_da_btree.h"
>>> +#include "xfs_attr.h"
>>> +#include "xfs_shared.h"
>>> +#include "xfs_attr_item.h"
>>> +#include "xfs_alloc.h"
>>> +#include "xfs_bmap.h"
>>> +#include "xfs_trace.h"
>>> +#include "libxfs/xfs_da_format.h"
>>> +#include "xfs_inode.h"
>>> +#include "xfs_quota.h"
>>> +#include "xfs_trans_space.h"
>>> +#include "xfs_log_priv.h"
>>> +#include "xfs_log_recover.h"
>>> +
>>> +static const struct xfs_item_ops xfs_attri_item_ops;
>>> +static const struct xfs_item_ops xfs_attrd_item_ops;
>>> +
>>> +/* iovec length must be 32-bit aligned */
>>> +static inline size_t ATTR_NVEC_SIZE(size_t size)
>>> +{
>>> +    return size == sizeof(int32_t) ? size :
>>> +           sizeof(int32_t) + round_up(size, sizeof(int32_t));
>>> +}
>>> +
>>> +static inline struct xfs_attri_log_item *ATTRI_ITEM(struct 
>>> xfs_log_item *lip)
>>> +{
>>> +    return container_of(lip, struct xfs_attri_log_item, attri_item);
>>> +}
>>> +
>>> +STATIC void
>>> +xfs_attri_item_free(
>>> +    struct xfs_attri_log_item    *attrip)
>>> +{
>>> +    kmem_free(attrip->attri_item.li_lv_shadow);
>>> +    kmem_free(attrip);
>>> +}
>>> +
>>> +/*
>>> + * Freeing the attrip requires that we remove it from the AIL if it 
>>> has already
>>> + * been placed there. However, the ATTRI may not yet have been 
>>> placed in the
>>> + * AIL when called by xfs_attri_release() from ATTRD processing due 
>>> to the
>>> + * ordering of committed vs unpin operations in bulk insert 
>>> operations. Hence
>>> + * the reference count to ensure only the last caller frees the ATTRI.
>>> + */
>>> +STATIC void
>>> +xfs_attri_release(
>>> +    struct xfs_attri_log_item    *attrip)
>>> +{
>>> +    ASSERT(atomic_read(&attrip->attri_refcount) > 0);
>>> +    if (atomic_dec_and_test(&attrip->attri_refcount)) {
>>> +        xfs_trans_ail_delete(&attrip->attri_item,
>>> +                     SHUTDOWN_LOG_IO_ERROR);
>>> +        xfs_attri_item_free(attrip);
>>> +    }
>>> +}
>>> +
>>> +STATIC void
>>> +xfs_attri_item_size(
>>> +    struct xfs_log_item    *lip,
>>> +    int            *nvecs,
>>> +    int            *nbytes)
>>> +{
>>> +    struct xfs_attri_log_item       *attrip = ATTRI_ITEM(lip);
>>> +
>>> +    *nvecs += 1;
>>> +    *nbytes += sizeof(struct xfs_attri_log_format);
>>> +
>>> +    /* Attr set and remove operations require a name */
>>> +    ASSERT(attrip->attri_name_len > 0);
>>> +
>>> +    *nvecs += 1;
>>> +    *nbytes += ATTR_NVEC_SIZE(attrip->attri_name_len);
>>> +
>>> +    /*
>>> +     * Set ops can accept a value of 0 len to clear an attr value.  
>>> Remove
>>> +     * ops do not need a value at all.  So only account for the value
>>> +     * when it is needed.
>>> +     */
>>> +    if (attrip->attri_value_len > 0) {
>>> +        *nvecs += 1;
>>> +        *nbytes += ATTR_NVEC_SIZE(attrip->attri_value_len);
>>> +    }
>>> +}
>>> +
>>> +/*
>>> + * This is called to fill in the log iovecs for the given attri log
>>> + * item. We use  1 iovec for the attri_format_item, 1 for the name, and
>>> + * another for the value if it is present
>>> + */
>>> +STATIC void
>>> +xfs_attri_item_format(
>>> +    struct xfs_log_item    *lip,
>>> +    struct xfs_log_vec    *lv)
>>> +{
>>> +    struct xfs_attri_log_item    *attrip = ATTRI_ITEM(lip);
>>> +    struct xfs_log_iovec        *vecp = NULL;
>>> +
>>> +    attrip->attri_format.alfi_type = XFS_LI_ATTRI;
>>> +    attrip->attri_format.alfi_size = 1;
>>> +
>>> +    /*
>>> +     * This size accounting must be done before copying the attrip 
>>> into the
>>> +     * iovec.  If we do it after, the wrong size will be recorded to 
>>> the log
>>> +     * and we trip across assertion checks for bad region sizes 
>>> later during
>>> +     * the log recovery.
>>> +     */
>>> +
>>> +    ASSERT(attrip->attri_name_len > 0);
>>> +    attrip->attri_format.alfi_size++;
>>> +
>>> +    if (attrip->attri_value_len > 0)
>>> +        attrip->attri_format.alfi_size++;
>>> +
>>> +    xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT,
>>> +            &attrip->attri_format,
>>> +            sizeof(struct xfs_attri_log_format));
>>> +    xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME,
>>> +            attrip->attri_name,
>>> +            ATTR_NVEC_SIZE(attrip->attri_name_len));
>>> +    if (attrip->attri_value_len > 0)
>>> +        xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE,
>>> +                attrip->attri_value,
>>> +                ATTR_NVEC_SIZE(attrip->attri_value_len));
>>> +}
>>> +
>>> +/*
>>> + * The unpin operation is the last place an ATTRI is manipulated in 
>>> the log. It
>>> + * is either inserted in the AIL or aborted in the event of a log 
>>> I/O error. In
>>> + * either case, the ATTRI transaction has been successfully 
>>> committed to make
>>> + * it this far. Therefore, we expect whoever committed the ATTRI to 
>>> either
>>> + * construct and commit the ATTRD or drop the ATTRD's reference in 
>>> the event of
>>> + * error. Simply drop the log's ATTRI reference now that the log is 
>>> done with
>>> + * it.
>>> + */
>>> +STATIC void
>>> +xfs_attri_item_unpin(
>>> +    struct xfs_log_item    *lip,
>>> +    int            remove)
>>> +{
>>> +    xfs_attri_release(ATTRI_ITEM(lip));
>>> +}
>>> +
>>> +
>>> +STATIC void
>>> +xfs_attri_item_release(
>>> +    struct xfs_log_item    *lip)
>>> +{
>>> +    xfs_attri_release(ATTRI_ITEM(lip));
>>> +}
>>> +
>>> +/*
>>> + * Allocate and initialize an attri item.  Caller may allocate an 
>>> additional
>>> + * trailing buffer of the specified size
>>> + */
>>> +STATIC struct xfs_attri_log_item *
>>> +xfs_attri_init(
>>> +    struct xfs_mount        *mp,
>>> +    int                buffer_size)
>>> +
>>> +{
>>> +    struct xfs_attri_log_item    *attrip;
>>> +    uint                size;
>>> +
>>> +    size = sizeof(struct xfs_attri_log_item) + buffer_size;
>>> +    attrip = kmem_alloc_large(size, KM_ZERO);
>>> +    if (attrip == NULL)
>>> +        return NULL;
>>> +
>>> +    xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI,
>>> +              &xfs_attri_item_ops);
>>> +    attrip->attri_format.alfi_id = (uintptr_t)(void *)attrip;
>>> +    atomic_set(&attrip->attri_refcount, 2);
>>> +
>>> +    return attrip;
>>> +}
>>> +
>>> +/*
>>> + * Copy an attr format buffer from the given buf, and into the 
>>> destination attr
>>> + * format structure.
>>> + */
>>> +STATIC int
>>> +xfs_attri_copy_format(
>>> +    struct xfs_log_iovec        *buf,
>>> +    struct xfs_attri_log_format    *dst_attr_fmt)
>>> +{
>>> +    struct xfs_attri_log_format    *src_attr_fmt = buf->i_addr;
>>> +    uint                len;
>>> +
>>> +    len = sizeof(struct xfs_attri_log_format);
>>> +    if (buf->i_len != len)
>>> +        return -EFSCORRUPTED;
>>> +
>>> +    memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len);
>>> +    return 0;
>>> +}
>>> +
>>> +static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct 
>>> xfs_log_item *lip)
>>> +{
>>> +    return container_of(lip, struct xfs_attrd_log_item, attrd_item);
>>> +}
>>> +
>>> +STATIC void
>>> +xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp)
>>> +{
>>> +    kmem_free(attrdp->attrd_item.li_lv_shadow);
>>> +    kmem_free(attrdp);
>>> +}
>>> +
>>> +STATIC void
>>> +xfs_attrd_item_size(
>>> +    struct xfs_log_item        *lip,
>>> +    int                *nvecs,
>>> +    int                *nbytes)
>>> +{
>>> +    *nvecs += 1;
>>> +    *nbytes += sizeof(struct xfs_attrd_log_format);
>>> +}
>>> +
>>> +/*
>>> + * This is called to fill in the log iovecs for the given attrd log 
>>> item. We use
>>> + * only 1 iovec for the attrd_format, and we point that at the 
>>> attr_log_format
>>> + * structure embedded in the attrd item.
>>> + */
>>> +STATIC void
>>> +xfs_attrd_item_format(
>>> +    struct xfs_log_item    *lip,
>>> +    struct xfs_log_vec    *lv)
>>> +{
>>> +    struct xfs_attrd_log_item    *attrdp = ATTRD_ITEM(lip);
>>> +    struct xfs_log_iovec        *vecp = NULL;
>>> +
>>> +    attrdp->attrd_format.alfd_type = XFS_LI_ATTRD;
>>> +    attrdp->attrd_format.alfd_size = 1;
>>> +
>>> +    xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRD_FORMAT,
>>> +            &attrdp->attrd_format,
>>> +            sizeof(struct xfs_attrd_log_format));
>>> +}
>>> +
>>> +/*
>>> + * The ATTRD is either committed or aborted if the transaction is 
>>> cancelled. If
>>> + * the transaction is cancelled, drop our reference to the ATTRI and 
>>> free the
>>> + * ATTRD.
>>> + */
>>> +STATIC void
>>> +xfs_attrd_item_release(
>>> +    struct xfs_log_item        *lip)
>>> +{
>>> +    struct xfs_attrd_log_item    *attrdp = ATTRD_ITEM(lip);
>>> +
>>> +    xfs_attri_release(attrdp->attrd_attrip);
>>> +    xfs_attrd_item_free(attrdp);
>>> +}
>>> +
>>> +/*
>>> + * Performs one step of an attribute update intent and marks the 
>>> attrd item
>>> + * dirty..  An attr operation may be a set or a remove.  Note that the
>>> + * transaction is marked dirty regardless of whether the operation 
>>> succeeds or
>>> + * fails to support the ATTRI/ATTRD lifecycle rules.
>>> + */
>>> +int
>>> +xfs_trans_attr(
>>
>> xfs_attri_finish_update() ?
Well, while this is more descriptive, I think it's a bit of an 
anti-pattern.  xfs_trans_attr sort of fit with the existing scheme that 
the other delayed ops did.  Like xfs_trans_free_extent, 
xfs_trans_log_finish_refcount_update or 
xfs_trans_log_finish_bmap_update.  I guess I just noticed the pattern of 
a xfs_trans_* getting called by it's corresponding xfs_*_finish_item. 
So I figured xfs_trans_attr would be consistent?  I suppose if 
consistency is not a concern though, xfs_attri_finish_update is 
appropriate as long as everyone else is ok with it.


>>
>>> +    struct xfs_delattr_context    *dac,
>>> +    struct xfs_attrd_log_item    *attrdp,
>>> +    struct xfs_buf            **leaf_bp,
>>> +    uint32_t            op_flags)
>>> +{
>>> +    struct xfs_da_args        *args = dac->da_args;
>>> +    int                error;
>>> +
>>> +    error = xfs_qm_dqattach_locked(args->dp, 0);
>>> +    if (error)
>>> +        return error;
>>> +
>>> +    switch (op_flags) {
>>> +    case XFS_ATTR_OP_FLAGS_SET:
>>> +        args->op_flags |= XFS_DA_OP_ADDNAME;
>>> +        error = xfs_attr_set_iter(dac, leaf_bp);
>>> +        break;
>>> +    case XFS_ATTR_OP_FLAGS_REMOVE:
>>> +        ASSERT(XFS_IFORK_Q(args->dp));
>>> +        error = xfs_attr_remove_iter(dac);
>>> +        break;
>>> +    default:
>>> +        error = -EFSCORRUPTED;
>>> +        break;
>>> +    }
>>> +
>>> +    /*
>>> +     * Mark the transaction dirty, even on error. This ensures the
>>> +     * transaction is aborted, which:
>>> +     *
>>> +     * 1.) releases the ATTRI and frees the ATTRD
>>> +     * 2.) shuts down the filesystem
>>> +     */
>>> +    args->trans->t_flags |= XFS_TRANS_DIRTY;
>>> +
>>> +    /*
>>> +     * attr intent/done items are null when delayed attributes are 
>>> disabled
>>> +     */
>>> +    if (attrdp)
>>> +        set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
>>> +
>>> +    return error;
>>> +}
>>> +
>>> +/* Log an attr to the intent item. */
>>> +STATIC void
>>> +xfs_attr_log_item(
>>> +    struct xfs_trans        *tp,
>>> +    struct xfs_attri_log_item    *attrip,
>>> +    struct xfs_attr_item        *attr)
>>> +{
>>> +    struct xfs_attri_log_format    *attrp;
>>> +
>>> +    tp->t_flags |= XFS_TRANS_DIRTY;
>>> +    set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags);
>>> +
>>> +    /*
>>> +     * At this point the xfs_attr_item has been constructed, and we've
>>> +     * created the log intent. Fill in the attri log item and log 
>>> format
>>> +     * structure with fields from this xfs_attr_item
>>> +     */
>>> +    attrp = &attrip->attri_format;
>>> +    attrp->alfi_ino = attr->xattri_dac.da_args->dp->i_ino;
>>> +    attrp->alfi_op_flags = attr->xattri_op_flags;
>>> +    attrp->alfi_value_len = attr->xattri_dac.da_args->valuelen;
>>> +    attrp->alfi_name_len = attr->xattri_dac.da_args->namelen;
>>> +    attrp->alfi_attr_flags = attr->xattri_dac.da_args->attr_filter;
>>> +
>>> +    attrip->attri_name = (void *)attr->xattri_dac.da_args->name;
>>> +    attrip->attri_value = attr->xattri_dac.da_args->value;
>>> +    attrip->attri_name_len = attr->xattri_dac.da_args->namelen;
>>> +    attrip->attri_value_len = attr->xattri_dac.da_args->valuelen;
>>> +}
>>> +
>>> +/* Get an ATTRI. */
>>> +static struct xfs_log_item *
>>> +xfs_attr_create_intent(
>>> +    struct xfs_trans        *tp,
>>> +    struct list_head        *items,
>>> +    unsigned int            count,
>>> +    bool                sort)
>>> +{
>>> +    struct xfs_mount        *mp = tp->t_mountp;
>>> +    struct xfs_attri_log_item    *attrip;
>>> +    struct xfs_attr_item        *attr;
>>> +
>>> +    ASSERT(count == 1);
>>> +
>>> +    if (!xfs_hasdelattr(mp))
>>> +        return NULL;
>>> +
>>> +    attrip = xfs_attri_init(mp, 0);
>>> +    if (attrip == NULL)
>>> +        return NULL;
>>> +
>>> +    xfs_trans_add_item(tp, &attrip->attri_item);
>>> +    list_for_each_entry(attr, items, xattri_list)
>>> +        xfs_attr_log_item(tp, attrip, attr);
>>> +    return &attrip->attri_item;
>>> +}
>>> +
>>> +/* Process an attr. */
>>> +STATIC int
>>> +xfs_attr_finish_item(
>>> +    struct xfs_trans        *tp,
>>> +    struct xfs_log_item        *done,
>>> +    struct list_head        *item,
>>> +    struct xfs_btree_cur        **state)
>>> +{
>>> +    struct xfs_attr_item        *attr;
>>> +    struct xfs_attrd_log_item    *done_item = NULL;
>>> +    int                error;
>>> +    struct xfs_delattr_context    *dac;
>>> +
>>> +    attr = container_of(item, struct xfs_attr_item, xattri_list);
>>> +    dac = &attr->xattri_dac;
>>> +    if (done)
>>> +        done_item = ATTRD_ITEM(done);
>>> +
>>> +    /*
>>> +     * Corner case that can happen during a recovery.  Because the 
>>> first
>>> +     * iteration of a multi part delay op happens in 
>>> xfs_attri_item_recover
>>> +     * to maintain the order of the log replay items.  But the new
>>> +     * transactions do not automatically rejoin during a recovery as 
>>> they do
>>> +     * in a standard delay op, so we need to catch this here and 
>>> rejoin the
>>> +     * leaf to the new transaction
>>> +     */
>>> +    if (attr->xattri_dac.leaf_bp &&
>>> +        attr->xattri_dac.leaf_bp->b_transp != tp) {
>>> +        xfs_trans_bjoin(tp, attr->xattri_dac.leaf_bp);
>>> +        xfs_trans_bhold(tp, attr->xattri_dac.leaf_bp);
>>> +    }
>>> +
>>> +    /*
>>> +     * Always reset trans after EAGAIN cycle
>>> +     * since the transaction is new
>>> +     */
>>> +    dac->da_args->trans = tp;
>>> +
>>> +    error = xfs_trans_attr(dac, done_item, &dac->leaf_bp,
>>> +                   attr->xattri_op_flags);
>>> +    if (error != -EAGAIN)
>>> +        kmem_free(attr);
>>> +
>>> +    return error;
>>> +}
>>> +
>>> +/* Abort all pending ATTRs. */
>>> +STATIC void
>>> +xfs_attr_abort_intent(
>>> +    struct xfs_log_item        *intent)
>>> +{
>>> +    xfs_attri_release(ATTRI_ITEM(intent));
>>> +}
>>> +
>>> +/* Cancel an attr */
>>> +STATIC void
>>> +xfs_attr_cancel_item(
>>> +    struct list_head        *item)
>>> +{
>>> +    struct xfs_attr_item        *attr;
>>> +
>>> +    attr = container_of(item, struct xfs_attr_item, xattri_list);
>>> +    kmem_free(attr);
>>> +}
>>> +
>>> +STATIC xfs_lsn_t
>>> +xfs_attri_item_committed(
>>> +    struct xfs_log_item        *lip,
>>> +    xfs_lsn_t            lsn)
>>> +{
>>> +    struct xfs_attri_log_item    *attrip;
>>> +    /*
>>> +     * The attrip refers to xfs_attr_item memory to log the name and 
>>> value
>>> +     * with the intent item. This already occurred when the intent was
>>> +     * committed so these fields are no longer accessed. Clear them 
>>> out of
>>   +     * caution since we're about to free the xfs_attr_item.
>>> +     */
>>> +    attrip = ATTRI_ITEM(lip);
>>> +    attrip->attri_name = NULL;
>>> +    attrip->attri_value = NULL;
>>> +
>>> +    /*
>>> +     * The ATTRI is logged only once and cannot be moved in the log, so
>>> +     * simply return the lsn at which it's been logged.
>>> +     */
>>> +    return lsn;
>>> +}
>>> +
>>> +STATIC bool
>>> +xfs_attri_item_match(
>>> +    struct xfs_log_item    *lip,
>>> +    uint64_t        intent_id)
>>> +{
>>> +    return ATTRI_ITEM(lip)->attri_format.alfi_id == intent_id;
>>> +}
>>> +
>>> +/*
>>> + * This routine is called to allocate an "attr free done" log item.
>>> + */
>>> +struct xfs_attrd_log_item *
>>> +xfs_trans_get_attrd(struct xfs_trans        *tp,
>>> +          struct xfs_attri_log_item    *attrip)
>>> +{
>>> +    struct xfs_attrd_log_item        *attrdp;
>>> +    uint                    size;
>>> +
>>> +    ASSERT(tp != NULL);
>>> +
>>> +    size = sizeof(struct xfs_attrd_log_item);
>>> +    attrdp = kmem_zalloc(size, 0);
>>> +
>>> +    xfs_log_item_init(tp->t_mountp, &attrdp->attrd_item, XFS_LI_ATTRD,
>>> +              &xfs_attrd_item_ops);
>>> +    attrdp->attrd_attrip = attrip;
>>> +    attrdp->attrd_format.alfd_alf_id = attrip->attri_format.alfi_id;
>>> +
>>> +    xfs_trans_add_item(tp, &attrdp->attrd_item);
>>> +    return attrdp;
>>> +}
>>> +
>>> +static const struct xfs_item_ops xfs_attrd_item_ops = {
>>> +    .flags        = XFS_ITEM_RELEASE_WHEN_COMMITTED,
>>> +    .iop_size    = xfs_attrd_item_size,
>>> +    .iop_format    = xfs_attrd_item_format,
>>> +    .iop_release    = xfs_attrd_item_release,
>>> +};
>>> +
>>> +
>>> +/* Get an ATTRD so we can process all the attrs. */
>>> +static struct xfs_log_item *
>>> +xfs_attr_create_done(
>>> +    struct xfs_trans        *tp,
>>> +    struct xfs_log_item        *intent,
>>> +    unsigned int            count)
>>> +{
>>> +    if (!intent)
>>> +        return NULL;
>>> +
>>> +    return &xfs_trans_get_attrd(tp, ATTRI_ITEM(intent))->attrd_item;
>>> +}
>>> +
>>> +const struct xfs_defer_op_type xfs_attr_defer_type = {
>>> +    .max_items    = 1,
>>> +    .create_intent    = xfs_attr_create_intent,
>>> +    .abort_intent    = xfs_attr_abort_intent,
>>> +    .create_done    = xfs_attr_create_done,
>>> +    .finish_item    = xfs_attr_finish_item,
>>> +    .cancel_item    = xfs_attr_cancel_item,
>>> +};
>>> +
>>> +/*
>>> + * Process an attr intent item that was recovered from the log.  We 
>>> need to
>>> + * delete the attr that it describes.
>>> + */
>>> +STATIC int
>>> +xfs_attri_item_recover(
>>> +    struct xfs_log_item        *lip,
>>> +    struct list_head        *capture_list)
>>> +{
>>> +    struct xfs_attri_log_item    *attrip = ATTRI_ITEM(lip);
>>> +    struct xfs_attr_item        *new_attr;
>>> +    struct xfs_mount        *mp = lip->li_mountp;
>>> +    struct xfs_inode        *ip;
>>> +    struct xfs_da_args        args;
>>> +    struct xfs_da_args        *new_args;
>>> +    struct xfs_trans_res        tres;
>>> +    bool                rsvd;
>>> +    struct xfs_attri_log_format    *attrp;
>>> +    int                error;
>>> +    int                total;
>>> +    int                local;
>>> +    struct xfs_attrd_log_item    *done_item = NULL;
>>> +    struct xfs_attr_item        attr = {
>>> +        .xattri_op_flags    = attrip->attri_format.alfi_op_flags,
>>> +        .xattri_dac.da_args    = &args,
>>> +    };
>>> +
>>> +    /*
>>> +     * First check the validity of the attr described by the ATTRI.  
>>> If any
>>> +     * are bad, then assume that all are bad and just toss the ATTRI.
>>> +     */
>>> +    attrp = &attrip->attri_format;
>>> +    if (!(attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET ||
>>> +          attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE) ||
>>> +        (attrp->alfi_value_len > XATTR_SIZE_MAX) ||
>>> +        (attrp->alfi_name_len > XATTR_NAME_MAX) ||
>>> +        (attrp->alfi_name_len == 0) ||
>>> +        xfs_verify_ino(mp, attrp->alfi_ino) == false ||
>>
>> Please put this validation logic in a separate predicate.
>>
>> It probably ought to ensure that there aren't illegal characters in the
>> attr name too.
Sure, will do

>>
>>> +        !xfs_hasdelattr(mp)) {
>>
>> Weird nit: If you mount with -o delattr, crash, and remount without that
>> mount option, we'll report that as a corruption error.
>>
>> Not sure what we want to do about that, it's a rough edge but it's also
>> an experimental feature.
Oh i see.  Yeah, this is a corner case where we sort of need the 
incompat flag to do that correctly.

>>
>>> +        return -EFSCORRUPTED;
>>> +    }
>>> +
>>> +    error = xfs_iget(mp, 0, attrp->alfi_ino, 0, 0, &ip);
>>> +    if (error)
>>> +        return error;
>>> +
>>> +    if (VFS_I(ip)->i_nlink == 0)
>>> +        xfs_iflags_set(ip, XFS_IRECOVERY);
>>
>>> +
>>> +    memset(&args, 0, sizeof(struct xfs_da_args));
>>> +    args.dp = ip;
>>> +    args.geo = mp->m_attr_geo;
>>> +    args.op_flags = attrp->alfi_op_flags;
>>> +    args.whichfork = XFS_ATTR_FORK;
>>> +    args.name = attrip->attri_name;
>>> +    args.namelen = attrp->alfi_name_len;
>>> +    args.hashval = xfs_da_hashname(args.name, args.namelen);
>>> +    args.attr_filter = attrp->alfi_attr_flags;
>>> +
>>> +    if (attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET) {
>>> +        args.value = attrip->attri_value;
>>> +        args.valuelen = attrp->alfi_value_len;
>>> +        args.total = xfs_attr_calc_size(&args, &local);
>>> +
>>> +        tres.tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
>>> +                 M_RES(mp)->tr_attrsetrt.tr_logres *
>>> +                    args.total;
>>> +        tres.tr_logcount = XFS_ATTRSET_LOG_COUNT;
>>> +        tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
>>> +        total = args.total;
>>> +    } else {
>>> +        tres = M_RES(mp)->tr_attrrm;
>>> +        total = XFS_ATTRRM_SPACE_RES(mp);
>>> +    }
>>
>> Isn't there already code in xfs_attr.c that does this?
Mmm, I think in a previous version it did because we had to unpack the 
intent into the args to kick off the underlying attr operation.  But 
then we sort of submerged the args into dac and and the attr item, so 
now it doenst unpack, it just passes the attr item through.  But this 
case in the recover is different because now we have to reconstruct the 
attr item from a replayed log item.  Does that make sense?  Or am I 
missing something?

>>
>>> +    error = xfs_trans_alloc(mp, &tres, total, 0,
>>> +                rsvd ? XFS_TRANS_RESERVE : 0, &args.trans);
>>> +    if (error)
>>> +        return error;
>>> +
>>> +    done_item = xfs_trans_get_attrd(args.trans, attrip);
>>> +
>>> +    xfs_ilock(ip, XFS_ILOCK_EXCL);
>>> +    xfs_trans_ijoin(args.trans, ip, 0);
>>> +
>>> +    error = xfs_trans_attr(&attr.xattri_dac, done_item,
>>> +                   &attr.xattri_dac.leaf_bp, attrp->alfi_op_flags);
>>> +    if (error == -EAGAIN) {
>>> +        /*
>>> +         * There's more work to do, so make a new xfs_attr_item and add
>>> +         * it to this transaction.  We dont use xfs_attr_item_init here
>>> +         * because we need the info stored in the current attr to
>>> +         * continue with this multi-part operation.  So, alloc space
>>> +         * for it and the args and copy everything there.
>>> +         */
>>> +        new_attr = kmem_zalloc(sizeof(struct xfs_attr_item) +
>>> +                       sizeof(struct xfs_da_args), KM_NOFS);
>>> +        new_args = (struct xfs_da_args *)((char *)new_attr +
>>> +               sizeof(struct xfs_attr_item));
>>> +
>>> +        memcpy(new_args, &args, sizeof(struct xfs_da_args));
>>> +        memcpy(new_attr, &attr, sizeof(struct xfs_attr_item));
>>> +
>>> +        new_attr->xattri_dac.da_args = new_args;
>>> +        memset(&new_attr->xattri_list, 0, sizeof(struct list_head));
>>> +
>>> +        xfs_defer_add(args.trans, XFS_DEFER_OPS_TYPE_ATTR,
>>> +                  &new_attr->xattri_list);
>>> +
>>> +        /* Do not send -EAGAIN back to caller */
>>> +        error = 0;
>>> +    } else if (error) {
>>> +        xfs_trans_cancel(args.trans);
>>> +        goto out;
>>> +    }
>>> +
>>> +    xfs_defer_ops_capture_and_commit(args.trans, ip, capture_list);
>>> +
>>> +out:
>>> +    xfs_iunlock(ip, XFS_ILOCK_EXCL);
>>> +    xfs_irele(ip);
>>> +    return error;
>>> +}
>>> +
>>> +/* Relog an intent item to push the log tail forward. */
>>> +static struct xfs_log_item *
>>> +xfs_attri_item_relog(
>>> +    struct xfs_log_item        *intent,
>>> +    struct xfs_trans        *tp)
>>> +{
>>> +    struct xfs_attrd_log_item    *attrdp;
>>> +    struct xfs_attri_log_item    *old_attrip;
>>> +    struct xfs_attri_log_item    *new_attrip;
>>> +    struct xfs_attri_log_format    *new_attrp;
>>> +    struct xfs_attri_log_format    *old_attrp;
>>> +    int                buffer_size;
>>> +
>>> +    old_attrip = ATTRI_ITEM(intent);
>>> +    old_attrp = &old_attrip->attri_format;
>>> +    buffer_size = old_attrp->alfi_value_len + old_attrp->alfi_name_len;
>>> +
>>> +    tp->t_flags |= XFS_TRANS_DIRTY;
>>> +    attrdp = xfs_trans_get_attrd(tp, old_attrip);
>>> +    set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags);
>>> +
>>> +    new_attrip = xfs_attri_init(tp->t_mountp, buffer_size);
>>> +    new_attrp = &new_attrip->attri_format;
>>> +
>>> +    new_attrp->alfi_ino = old_attrp->alfi_ino;
>>> +    new_attrp->alfi_op_flags = old_attrp->alfi_op_flags;
>>> +    new_attrp->alfi_value_len = old_attrp->alfi_value_len;
>>> +    new_attrp->alfi_name_len = old_attrp->alfi_name_len;
>>> +    new_attrp->alfi_attr_flags = old_attrp->alfi_attr_flags;
>>> +
>>> +    new_attrip->attri_name_len = old_attrip->attri_name_len;
>>> +    new_attrip->attri_name = ((char *)new_attrip) +
>>> +                 sizeof(struct xfs_attri_log_item);
>>> +    memcpy(new_attrip->attri_name, old_attrip->attri_name,
>>> +        new_attrip->attri_name_len);
>>> +
>>> +    new_attrip->attri_value_len = old_attrip->attri_value_len;
>>> +    if (new_attrip->attri_value_len > 0) {
>>> +        new_attrip->attri_value = new_attrip->attri_name +
>>> +                      new_attrip->attri_name_len;
>>> +
>>> +        memcpy(new_attrip->attri_value, old_attrip->attri_value,
>>> +               new_attrip->attri_value_len);
>>> +    }
>>> +
>>> +    xfs_trans_add_item(tp, &new_attrip->attri_item);
>>> +    set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags);
>>> +
>>> +    return &new_attrip->attri_item;
>>> +}
>>> +
>>> +static const struct xfs_item_ops xfs_attri_item_ops = {
>>> +    .iop_size    = xfs_attri_item_size,
>>> +    .iop_format    = xfs_attri_item_format,
>>> +    .iop_unpin    = xfs_attri_item_unpin,
>>> +    .iop_committed    = xfs_attri_item_committed,
>>> +    .iop_release    = xfs_attri_item_release,
>>> +    .iop_recover    = xfs_attri_item_recover,
>>> +    .iop_match    = xfs_attri_item_match,
>>> +    .iop_relog    = xfs_attri_item_relog,
>>> +};
>>> +
>>> +
>>> +
>>> +STATIC int
>>> +xlog_recover_attri_commit_pass2(
>>> +    struct xlog                     *log,
>>> +    struct list_head        *buffer_list,
>>> +    struct xlog_recover_item        *item,
>>> +    xfs_lsn_t                       lsn)
>>> +{
>>> +    int                             error;
>>> +    struct xfs_mount                *mp = log->l_mp;
>>> +    struct xfs_attri_log_item       *attrip;
>>> +    struct xfs_attri_log_format     *attri_formatp;
>>> +    char                *name = NULL;
>>> +    char                *value = NULL;
>>> +    int                region = 0;
>>> +    int                buffer_size;
>>> +
>>> +    attri_formatp = item->ri_buf[region].i_addr;
>>> +
>>> +    /* Validate xfs_attri_log_format */
>>> +    if (attri_formatp->__pad != 0 || attri_formatp->alfi_name_len == 
>>> 0 ||
>>> +        (attri_formatp->alfi_op_flags == XFS_ATTR_OP_FLAGS_REMOVE &&
>>> +        attri_formatp->alfi_value_len != 0))
>>> +        return -EFSCORRUPTED;
>>
>> Might be worth an XFS_ERROR_REPORT here since this is evidence of incore
>> memory corruption, right?
Sure, will add

>>
>>> +
>>> +    buffer_size = attri_formatp->alfi_name_len +
>>> +              attri_formatp->alfi_value_len;
>>> +
>>> +    attrip = xfs_attri_init(mp, buffer_size);
>>> +    if (attrip == NULL)
>>> +        return -ENOMEM;
>>> +
>>> +    error = xfs_attri_copy_format(&item->ri_buf[region],
>>> +                      &attrip->attri_format);
>>> +    if (error) {
>>> +        xfs_attri_item_free(attrip);
>>> +        return error;
>>> +    }
>>> +
>>> +    attrip->attri_name_len = attri_formatp->alfi_name_len;
>>> +    attrip->attri_value_len = attri_formatp->alfi_value_len;
>>> +    region++;
>>> +    name = ((char *)attrip) + sizeof(struct xfs_attri_log_item);
>>> +    memcpy(name, item->ri_buf[region].i_addr, attrip->attri_name_len);
>>> +    attrip->attri_name = name;
>>> +
>>> +    if (attrip->attri_value_len > 0) {
>>> +        region++;
>>> +        value = ((char *)attrip) + sizeof(struct xfs_attri_log_item) +
>>> +            attrip->attri_name_len;
>>> +        memcpy(value, item->ri_buf[region].i_addr,
>>> +            attrip->attri_value_len);
>>> +        attrip->attri_value = value;
>>> +    }
>>> +
>>> +    /*
>>> +     * The ATTRI has two references. One for the ATTRD and one for 
>>> ATTRI to
>>> +     * ensure it makes it into the AIL. Insert the ATTRI into the AIL
>>> +     * directly and drop the ATTRI reference. Note that
>>> +     * xfs_trans_ail_update() drops the AIL lock.
>>> +     */
>>> +    xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn);
>>> +    xfs_attri_release(attrip);
>>> +    return 0;
>>> +}
>>> +
>>> +const struct xlog_recover_item_ops xlog_attri_item_ops = {
>>> +    .item_type    = XFS_LI_ATTRI,
>>> +    .commit_pass2    = xlog_recover_attri_commit_pass2,
>>> +};
>>> +
>>> +/*
>>> + * This routine is called when an ATTRD format structure is found in 
>>> a committed
>>> + * transaction in the log. Its purpose is to cancel the 
>>> corresponding ATTRI if
>>> + * it was still in the log. To do this it searches the AIL for the 
>>> ATTRI with
>>> + * an id equal to that in the ATTRD format structure. If we find it 
>>> we drop
>>> + * the ATTRD reference, which removes the ATTRI from the AIL and 
>>> frees it.
>>> + */
>>> +STATIC int
>>> +xlog_recover_attrd_commit_pass2(
>>> +    struct xlog            *log,
>>> +    struct list_head        *buffer_list,
>>> +    struct xlog_recover_item    *item,
>>> +    xfs_lsn_t            lsn)
>>> +{
>>> +    struct xfs_attrd_log_format    *attrd_formatp;
>>> +
>>> +    attrd_formatp = item->ri_buf[0].i_addr;
>>> +    ASSERT((item->ri_buf[0].i_len ==
>>> +                (sizeof(struct xfs_attrd_log_format))));
>>> +
>>> +    xlog_recover_release_intent(log, XFS_LI_ATTRI,
>>> +                    attrd_formatp->alfd_alf_id);
>>> +    return 0;
>>> +}
>>> +
>>> +const struct xlog_recover_item_ops xlog_attrd_item_ops = {
>>> +    .item_type    = XFS_LI_ATTRD,
>>> +    .commit_pass2    = xlog_recover_attrd_commit_pass2,
>>> +};
>>> diff --git a/fs/xfs/xfs_attr_item.h b/fs/xfs/xfs_attr_item.h
>>> new file mode 100644
>>> index 0000000..27c6bae
>>> --- /dev/null
>>> +++ b/fs/xfs/xfs_attr_item.h
>>> @@ -0,0 +1,52 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later
>>> + *
>>> + * Copyright (C) 2019 Oracle.  All Rights Reserved.
>>> + * Author: Allison Collins <allison.henderson@oracle.com>
>>> + */
>>> +#ifndef    __XFS_ATTR_ITEM_H__
>>> +#define    __XFS_ATTR_ITEM_H__
>>> +
>>> +/* kernel only ATTRI/ATTRD definitions */
>>> +
>>> +struct xfs_mount;
>>> +struct kmem_zone;
>>> +
>>> +/*
>>> + * Define ATTR flag bits. Manipulated by set/clear/test_bit operators.
>>> + */
>>> +#define    XFS_ATTRI_RECOVERED    1
>>> +
>>> +
>>> +/*
>>> + * This is the "attr intention" log item.  It is used to log the 
>>> fact that some
>>
>> Well, it's the incore state tracking for attr intent log items... :)
>>
>> Aside from the nits I 've pointed out so far, this looks more or less ok
>> to me.
>>
>> --------------
>>
>> Ok, so here we are at the end.  Looking at my own atomic extent swap
>> series, I broke this up into two pieces -- one to create the barebones
>> log item (with dummy implementations); and a second one to create the
>> defer ops code and connect it to the log item.
>>
>> Oh, heh.  The first patch is 16K and the second one is 47K.
>>
>> That might be a good way to break *this* patch into smaller parts, if
>> nothing else -- concentrate on getting the log parts working, then
>> connect the new state machine to defer ops and log items.
Sure, I can break up this patch in a similar way

>>
>> That said, as this series gets longer and longer I find it really more
>> difficult to go through the whole series one by one vs. just diffing the
>> whole branch and reviewing that.
That's fine, it's that's easier for you, I could probably figure out in 
what patch the line of code you are commenting on would affect.  As 
noted in the call earlier today, I try to help reviewers in the cover 
letter by listing off which patches have changed since the last 
revision, and which havnt.  That way you dont have to grind through 
things that havnt changed.  Though I'm not sure if folks really use the 
cover letter :-)

>>
>> <shrug> I don't really have a definitive answer for which is better.
>> The xattr code is very complex, and I struggle even combining both of my
>> usual strategies and attacking review from both ends.
I actually think it's very reasonable to just focus on patches 11 and 
12?  Or maybe just the diff of the branch up to 12 if that's easier for 
you.  Because if there's something about 11 or 12 that people want to 
change, it generally implies some change to the underlying refactoring. 
  So it seems a bit wasteful to keep re-reviewing the refactoring if the 
end result has some gripe about it.  If everyone is happy about how the 
refactoring ends up, then it makes sense to go back and review the 
implementation details.

To be clear, it's certaintly not that I dont appreciate the reviews, but 
I understand it's an exhausting thing to grind through, and I try to be 
mindful of that.  The only reason I dont push out the entire extended 
set is because its a 40 patch monster that's not reasonable for anyone 
to review right now, and I dont want people to think that I'm asking for 
that at this time.  So I just sort of send the links out so that people 
can see where it's meant to go.  For similar reasons, I've thought about 
reducing the visible window of patches to reduce reviewer burnout. So 
dont feel like you have to flog yourself through all 22, I think 12 is 
also reasonable cap off.

>>
>> By the way, have you been stress testing the xattr code with all this
>> stuff applied?  At some point it becomes easier to pull this in and fix
>> up the breakage than it is to review 22 slice-n-dice patches every cycle.
>>
No, I've been testing with the attr group, and then a  few tests I came 
up with to replay the journal and also parent pointers.  I can toss in 
some stress tests too and make sure nothing turns up.

Thank you for the reviews, I know it's a lot
Allison

>> --D
>>
>>> + * attribute operations need to be processed.  An operation is 
>>> currently either
>>> + * a set or remove.  Set or remove operations are described by the 
>>> xfs_attr_item
>>> + * which may be logged to this intent.
>>> + *
>>> + * During a normal attr operation, name and value point to the name 
>>> and value
>>> + * feilds of the calling functions xfs_da_args.  During a recovery, 
>>> the name
>>> + * and value buffers are copied from the log, and stored in a 
>>> trailing buffer
>>> + * attached to the xfs_attr_item until they are committed.  They are 
>>> freed when
>>> + * the xfs_attr_item itself is freed when the work is done.
>>> + */
>>> +struct xfs_attri_log_item {
>>> +    struct xfs_log_item        attri_item;
>>> +    atomic_t            attri_refcount;
>>> +    int                attri_name_len;
>>> +    int                attri_value_len;
>>> +    void                *attri_name;
>>> +    void                *attri_value;
>>> +    struct xfs_attri_log_format    attri_format;
>>> +};
>>> +
>>> +/*
>>> + * This is the "attr done" log item.  It is used to log the fact 
>>> that some attrs
>>> + * earlier mentioned in an attri item have been freed.
>>> + */
>>> +struct xfs_attrd_log_item {
>>> +    struct xfs_attri_log_item    *attrd_attrip;
>>> +    struct xfs_log_item        attrd_item;
>>> +    struct xfs_attrd_log_format    attrd_format;
>>> +};
>>> +
>>> +#endif    /* __XFS_ATTR_ITEM_H__ */
>>> diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
>>> index 8f8837f..d7787a5 100644
>>> --- a/fs/xfs/xfs_attr_list.c
>>> +++ b/fs/xfs/xfs_attr_list.c
>>> @@ -15,6 +15,7 @@
>>>   #include "xfs_inode.h"
>>>   #include "xfs_trans.h"
>>>   #include "xfs_bmap.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_attr_sf.h"
>>>   #include "xfs_attr_leaf.h"
>>> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
>>> index 248083e..6682936 100644
>>> --- a/fs/xfs/xfs_ioctl.c
>>> +++ b/fs/xfs/xfs_ioctl.c
>>> @@ -15,6 +15,8 @@
>>>   #include "xfs_iwalk.h"
>>>   #include "xfs_itable.h"
>>>   #include "xfs_error.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_bmap.h"
>>>   #include "xfs_bmap_util.h"
>>> diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
>>> index c1771e7..62e1534 100644
>>> --- a/fs/xfs/xfs_ioctl32.c
>>> +++ b/fs/xfs/xfs_ioctl32.c
>>> @@ -17,6 +17,8 @@
>>>   #include "xfs_itable.h"
>>>   #include "xfs_fsops.h"
>>>   #include "xfs_rtalloc.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_ioctl.h"
>>>   #include "xfs_ioctl32.h"
>>> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
>>> index 00369502f..ce04721 100644
>>> --- a/fs/xfs/xfs_iops.c
>>> +++ b/fs/xfs/xfs_iops.c
>>> @@ -13,6 +13,8 @@
>>>   #include "xfs_inode.h"
>>>   #include "xfs_acl.h"
>>>   #include "xfs_quota.h"
>>> +#include "xfs_da_format.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_trans.h"
>>>   #include "xfs_trace.h"
>>> diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
>>> index 0604183..290e57b 100644
>>> --- a/fs/xfs/xfs_log.c
>>> +++ b/fs/xfs/xfs_log.c
>>> @@ -2070,6 +2070,10 @@ xlog_print_tic_res(
>>>           REG_TYPE_STR(CUD_FORMAT, "cud_format"),
>>>           REG_TYPE_STR(BUI_FORMAT, "bui_format"),
>>>           REG_TYPE_STR(BUD_FORMAT, "bud_format"),
>>> +        REG_TYPE_STR(ATTRI_FORMAT, "attri_format"),
>>> +        REG_TYPE_STR(ATTRD_FORMAT, "attrd_format"),
>>> +        REG_TYPE_STR(ATTR_NAME, "attr_name"),
>>> +        REG_TYPE_STR(ATTR_VALUE, "attr_value"),
>>>       };
>>>       BUILD_BUG_ON(ARRAY_SIZE(res_type_str) != XLOG_REG_TYPE_MAX + 1);
>>>   #undef REG_TYPE_STR
>>> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
>>> index 295a5c6..c0821b6 100644
>>> --- a/fs/xfs/xfs_log_recover.c
>>> +++ b/fs/xfs/xfs_log_recover.c
>>> @@ -1775,6 +1775,8 @@ static const struct xlog_recover_item_ops 
>>> *xlog_recover_item_ops[] = {
>>>       &xlog_cud_item_ops,
>>>       &xlog_bui_item_ops,
>>>       &xlog_bud_item_ops,
>>> +    &xlog_attri_item_ops,
>>> +    &xlog_attrd_item_ops,
>>>   };
>>>   static const struct xlog_recover_item_ops *
>>> diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
>>> index 0aa87c21..bc9c25e 100644
>>> --- a/fs/xfs/xfs_ondisk.h
>>> +++ b/fs/xfs/xfs_ondisk.h
>>> @@ -132,6 +132,8 @@ xfs_check_ondisk_structs(void)
>>>       XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format,    56);
>>>       XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat,    20);
>>>       XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header,        16);
>>> +    XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,    40);
>>> +    XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,    16);
>>>       /*
>>>        * The v5 superblock format extended several v4 header 
>>> structures with
>>> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
>>> index bca48b3..9b0c790 100644
>>> --- a/fs/xfs/xfs_xattr.c
>>> +++ b/fs/xfs/xfs_xattr.c
>>> @@ -10,6 +10,7 @@
>>>   #include "xfs_log_format.h"
>>>   #include "xfs_da_format.h"
>>>   #include "xfs_inode.h"
>>> +#include "xfs_da_btree.h"
>>>   #include "xfs_attr.h"
>>>   #include "xfs_acl.h"
>>>   #include "xfs_da_btree.h"
>>> -- 
>>> 2.7.4
>>>

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

* Re: [PATCH v15 11/22] xfs: Add delay ready attr remove routines
  2021-02-28 15:39           ` Brian Foster
@ 2021-03-02  8:26             ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-03-02  8:26 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs



On 2/28/21 8:39 AM, Brian Foster wrote:
> On Thu, Feb 25, 2021 at 03:28:00PM -0700, Allison Henderson wrote:
>>
>>
>> On 2/25/21 7:22 AM, Brian Foster wrote:
>>> On Thu, Feb 25, 2021 at 12:01:10AM -0700, Allison Henderson wrote:
>>>>
>>>>
>>>> On 2/24/21 11:45 AM, Brian Foster wrote:
>>>>> On Thu, Feb 18, 2021 at 09:53:37AM -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 has become xfs_attr_remove_iter, which
>>>>>> uses a sort of state machine like switch to keep track of where it was
>>>>>> when EAGAIN was returned. xfs_attr_node_removename has also been
>>>>>> modified to use the switch, and a new version of xfs_attr_remove_args
>>>>>> consists of a simple loop to refresh the transaction until the operation
>>>>>> is completed. 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        | 223 +++++++++++++++++++++++++++++-----------
>>>>>>     fs/xfs/libxfs/xfs_attr.h        | 100 ++++++++++++++++++
>>>>>>     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, 294 insertions(+), 83 deletions(-)
>>>>>>
>>>>>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>>>>>> index 56d4b56..d46b92a 100644
>>>>>> --- a/fs/xfs/libxfs/xfs_attr.c
>>>>>> +++ b/fs/xfs/libxfs/xfs_attr.c
>>> ...
>>>>>> @@ -1285,51 +1365,74 @@ xfs_attr_node_remove_step(
>>>>>>      *
>>>>>>      * This routine will find the blocks of the name to remove, remove them and
>>>>>>      * shrink the tree if needed.
>>>>>> + *
>>>>>> + * This routine is meant to function as either an inline or delayed operation,
>>>>>> + * and may return -EAGAIN when the transaction needs to be rolled.  Calling
>>>>>> + * functions will need to handle this, and recall the function until a
>>>>>> + * successful error code is returned.
>>>>>>      */
>>>>>>     STATIC int
>>>>>> -xfs_attr_node_removename(
>>>>>> -	struct xfs_da_args	*args)
>>>>>> +xfs_attr_node_removename_iter(
>>>>>> +	struct xfs_delattr_context	*dac)
>>>>>>     {
>>>>>> -	struct xfs_da_state	*state = NULL;
>>>>>> -	int			retval, error;
>>>>>> -	struct xfs_inode	*dp = args->dp;
>>>>>> +	struct xfs_da_args		*args = dac->da_args;
>>>>>> +	struct xfs_da_state		*state = NULL;
>>>>>> +	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;
>>>>>> -
>>>>>> -	error = xfs_attr_node_remove_step(args, state);
>>>>>> -	if (error)
>>>>>> -		goto out;
>>>>>> -
>>>>>> -	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 (!dac->da_state) {
>>>>>> +		error = xfs_attr_node_removename_setup(dac);
>>>>>>     		if (error)
>>>>>>     			goto out;
>>>>>> +	}
>>>>>> +	state = dac->da_state;
>>>>>> +
>>>>>> +	switch (dac->dela_state) {
>>>>>> +	case XFS_DAS_UNINIT:
>>>>>>     		/*
>>>>>> -		 * Commit the Btree join operation and start a new trans.
>>>>>> +		 * repeatedly remove remote blocks, remove the entry and join.
>>>>>> +		 * returns -EAGAIN or 0 for completion of the step.
>>>>>>     		 */
>>>>>> -		error = xfs_trans_roll_inode(&args->trans, dp);
>>>>>> +		error = xfs_attr_node_remove_step(dac);
>>>>>>     		if (error)
>>>>>> -			goto out;
>>>>>> -	}
>>>>>> +			break;
>>>>>
>>>>> Hmm.. so re: my comment further down on xfs_attr_rmtval_remove(),
>>>>> wouldn't that change semantics here? I.e., once remote blocks are
>>>>> removed this would previously carry on with a clean transaction. Now it
>>>>> looks like we'd carry on with the dirty transaction that removed the
>>>>> last remote extent. This suggests that perhaps we should return once
>>>>> more and fall into a new state to remove the name..?
>>>> I suspect the diff might be making this a bit difficult to see.  The roll
>>>> that you see being removed here belongs to the transaction we hoisted up  in
>>>> patch 3 which happens after the clean up below, and we have the
>>>> corresponding EAGAIN fot that one.  I think the diff gets things a little
>>>> interlaced here because the switch adds another level of indentation.
>>>>
>>>
>>> Hmm.. the roll in patch 3 appears to be related to the _cleanup()
>>> helper. What I'm referring to here is the state of the transaction after
>>> the final remote block is removed from the attr. I'm not sure we're
>>> talking about the same thing here..
>>>
>>>> some times i do like to I use a graphical diffviewer like diffuse when
>>>> patches get weird like this.  Something like this:
>>>>
>>>> git config --global diff.tool  diffuse
>>>> git difftool 3c53e49 e201c09
>>>>
>>>> You'd need to download the branch and also the diffuse tool, but sometimes i
>>>> think it makes some of these diffs a bit easier to see
>>>>
>>>
>>> I think it's easier just to refer to the code directly. The current
>>> upstream code flows down into:
>>>
>>> ...
>>> xfs_attr_node_removename()
>>>    xfs_attr_node_remove_rmt()
>>>     xfs_attr_rmtval_remove()
>>>
>>> ... which then implements the following loop:
>>>
>>>           do {
>>>                   retval = __xfs_attr_rmtval_remove(args);
>>>                   if (retval && retval != -EAGAIN)
>>>                           return retval;
>>>
>>>                   /*
>>>                    * Close out trans and start the next one in the chain.
>>>                    */
>>>                   error = xfs_trans_roll_inode(&args->trans, args->dp);
>>>                   if (error)
>>>                           return error;
>>>           } while (retval == -EAGAIN);
>>>
>>> This rolls the transaction when retval == -EAGAIN or retval == 0, thus
>>> always returns with a clean transaction after the remote block removal
>>> completes.
>>>
>>> The code as of this patch does:
>>>
>>> ...
>>> xfs_attr_node_removename_iter()
>>>    xfs_attr_node_remove_step()
>>>     xfs_attr_node_remove_rmt()
>>>      __xfs_attr_rmtval_remove()
>>>
>>> ... which either returns -EAGAIN (since the roll is now implemented at
>>> the very top) or 0 when done == true. The transaction might be dirty in
>>> the latter case, but xfs_attr_node_removename_iter() moves right on to
>>> xfs_attr_node_remove_cleanup() which can now do more work in that same
>>> transaction. Am I following that correctly?
>>>
>>>> Also, it would be
>>>>> nice to remove the several seemingly unnecessary layers of indirection
>>>>> here. For example, something like the following (also considering my
>>>>> comment above wrt to xfs_attr_remove_iter() and UNINIT):
>>>>>
>>>>> 	case UNINIT:
>>>>> 		...
>>>>> 		/* fallthrough */
>>>>> 	case RMTBLK:
>>>>> 		if (args->rmtblkno > 0) {
>>>>> 			dac->dela_state = RMTBLK;
>>>>> 			error = __xfs_attr_rmtval_remove(dac);
>>>>> 			if (error)
>>>>> 				break;
>>>>>
>>>>> 			ASSERT(args->rmtblkno == 0);
>>>>> 			xfs_attr_refillstate(state);
>>>>> 			dac->flags |= XFS_DAC_DEFER_FINISH;
>>>>> 			dac->dela_state = RMNAME;
>>>>> 			return -EAGAIN;
>>>>> 		}
>>>> Ok, this looks to me like we've hoisted both xfs_attr_node_remove_rmt and
>>>> xfs_attr_node_remove_step into this scope, but I still think this adds an
>>>> extra roll where non previously was.  With out that extra EAGAIN, I think we
>>>> are fine to have all that just under the UNINIT case.  I also think it's
>>>> also worth noteing here that this is kind of a reverse of patch 1, which I
>>>> think we put in for reasons of trying to modularize the higher level
>>>> functions as much as possible.
>>>>
>>>> I suspect some of where you were going with this may have been influenced by
>>>> the earlier diff confusion too.  Maybe take a second look there before we go
>>>> too much down this change....
>>>>
>>>
>>> I can certainly be getting lost somewhere in all the refactoring. If so,
>>> can you point out where in the flow described above?
>> Ok, I think see it.  So basically I think this means we cant have the
>> helpers because it's ambiguos as to if the transaction is dirty or not.  I
>> dont see that there's anything in the review history where we rationalized
>> that away, so I think we just overlooked it.  So I think what this means is
>> that we need to reverse apply commit 72b97ea40d (which is where we added
>> xfs_attr_node_remove_rmt), then drop patch 1 which leaves no need for patch
>> 3, since the transaction will have not moved.  Then add state RMTBLK?  I
>> think that arrives at what you have here.
>>
> 
> It's not clear to me if anything needs to change before this patch or
> the changes can just fold into this patch itself. You probably have a
> better sense of that than I do atm. 
I can unfold it here in this patch too if people prefer. I'm getting the 
impression that others are having a hard time keeping up with the 
refactoring, so maybe that might be better.


 From my perspective, I think we want
> that transaction to roll after the final remote extent removal unless we
> had some reason to explicitly change existing behavior. This used to be
> handled by the old loop that rolled the transaction down in the remote
> block removal code. ISTM that the proper way to maintain the same
> behavior in the new state machine code is to unconditionally fall out of
> a RMTBLKREMOVE state with an -EAGAIN from _iter().
Sure, I think that describes what you have up there

> 
> IOW, __xfs_attr_rmtval_remove() returns -EAGAIN when it has more work to
> do. _iter() returns -EAGAIN when __xfs_attr_rmtval_remove() was called,
> finished its work, but we need to roll the transaction before the next
> step of the operation..
Right, ok, will make those modifcations here then.  Thanks for the 
reviews!  I know it's a lot.

Allison

> 
> Brian
> 
>> Allison
>>
>>>
>>> Brian
>>>
>>>>
>>>>> 		/* fallthrough */
>>>>> 	case RMNAME:
>>>>> 		...
>>>>> 	...
>>>>>
>>>>>> -	/*
>>>>>> -	 * 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);
>>>>>> +		retval = xfs_attr_node_remove_cleanup(args, state);
>>>>> ...
>>>> I think the overlooked EAGAIN was in this area that got clipped out.....
>>>>
>>>>>> diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
>>>>>> index 48d8e9c..f09820c 100644
>>>>>> --- a/fs/xfs/libxfs/xfs_attr_remote.c
>>>>>> +++ b/fs/xfs/libxfs/xfs_attr_remote.c
>>>>> ...
>>>>>> @@ -685,31 +687,29 @@ c(
>>>>>>     	 * 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;
>>>>>
>>>>> Previously this would roll once and exit the loop on retval == 0. Now it
>>>>> looks like we break out of the loop immediately. Why the change?
>>>>
>>>> Gosh, I think sometime in reviewing v9, we had come up with a
>>>> "xfs_attr_roll_again" helper that took the error code as a paramater and
>>>> decided whether or not to roll.  And then in v10 i think people thought that
>>>> was weird and we turned it into xfs_attr_trans_roll.  I think I likley
>>>> forgot to restore the orginal retval handling here.  This whole function
>>>> disappears in the next patch, but the original error handling should be
>>>> restored to keep things consistent. Thx for the catch!
>>>>
>>>>
>>>> Thx for the reviews!!  I know it's complicated!  I've chased my tail many
>>>> times with it myself :-)
>>>>
>>>> Allison
>>>>
>>>>
>>>>
>>>>
>>>>>
>>>>> Brian
>>>>>
>>>>>> -		/*
>>>>>> -		 * 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 dont 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 reminicent 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] 87+ messages in thread

* Re: [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work
  2021-03-01 18:00       ` Darrick J. Wong
@ 2021-03-02  8:26         ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-03-02  8:26 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 3/1/21 11:00 AM, Darrick J. Wong wrote:
> On Fri, Feb 26, 2021 at 05:54:51PM -0700, Allison Henderson wrote:
>>
>>
>> On 2/25/21 9:02 PM, Darrick J. Wong wrote:
>>> On Thu, Feb 18, 2021 at 09:53:32AM -0700, 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>
>>>> ---
>>>>    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 205ad26..bee8d3fb 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_work(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);
>>>> @@ -1059,6 +1060,25 @@ xfs_attr_node_addname(
>>>>    			return error;
>>>>    	}
>>>> +	error = xfs_attr_node_addname_work(args);
>>>> +out:
>>>> +	if (state)
>>>> +		xfs_da_state_free(state);
>>>> +	if (error)
>>>> +		return error;
>>>> +	return retval;
>>>> +}
>>>> +
>>>> +
>>>> +STATIC
>>>> +int xfs_attr_node_addname_work(
>>>
>>> What, erm, work does this function do?  Since it survives to the end of
>>> the patchset, I think this needs a better name (or at least needs a
>>> comment about what it's actually supposed to do).
>> To directly answer the question: it's here to help xfs_attr_set_iter not be
>> any bigger than it has to. I think we likely struggled with the name because
>> it's almost like it's just the "remainder" of the operation that doesnt need
>> state management
>>
>>>
>>> AFAICT you're splitting node_addname() into two functions because we're
>>> at a transaction roll point, and this "_work" function exists to remove
>>> the copy of the xattr key that has the "INCOMPLETE" bit set (aka the old
>>> one), right?
>> Thats about right. Maybe just a quick comment?
>> /*
>>   * Removes the old xattr key marked with the INCOMPLETE bit
>>   */
>>
>> I suppose we could consider something like
>> "xfs_attr_node_addname_remv_incomplete"?  Or xfs_attr_node_addname_cleanup?
>> Trying to cram it into the name maybe getting a bit wordy too.
> 
> xfs_attr_node_addname_clear_incomplete?
I'm fine with that as long as everyone else is :-)

Allison
> 
> --D
> 
>>
>> Allison
>>>
>>> --D
>>>
>>>> +	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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname
  2021-03-01 18:05   ` Darrick J. Wong
@ 2021-03-02  8:26     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-03-02  8:26 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 3/1/21 11:05 AM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:34AM -0700, 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>
> 
> Simple enough transplant,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Great, thank you!
Allison

> 
> --D
> 
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 165 ++++++++++++++++++++++++-----------------------
>>   1 file changed, 83 insertions(+), 82 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index 4333b61..19a532a 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,
>> @@ -268,8 +269,9 @@ 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;
>> +	int			retval = 0;
>>   
>>   	/*
>>   	 * If the attribute list is already in leaf format, jump straight to
>> @@ -320,8 +322,82 @@ 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);
>> +		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_work(args);
>> +out:
>> +	if (state)
>> +		xfs_da_state_free(state);
>> +	if (error)
>> +		return error;
>> +	return retval;
>>   
>> -	return error;
>>   }
>>   
>>   /*
>> @@ -955,7 +1031,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);
>>   
>> @@ -963,8 +1039,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
>> @@ -1010,85 +1086,10 @@ 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_work(args);
>>   out:
>>   	if (state)
>>   		xfs_da_state_free(state);
>> -	if (error)
>> -		return error;
>> -	return retval;
>> +	return error;
>>   }
>>   
>>   
>> -- 
>> 2.7.4
>>

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

* Re: [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname
  2021-03-01 18:19   ` Darrick J. Wong
@ 2021-03-02  8:26     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-03-02  8:26 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 3/1/21 11:19 AM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:35AM -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 asetheic right away, but it is a
> 
> "aesthetic"
> 
>> preliminary step to to manageing the state machine code.
> 
> "to merging in" ?
> 
> The goto label is ugly, but afaict this patch moves code and the next
> one rearranges it the way you ultimately want it, right?
> 
> With spelling fixed,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> 
Ok, will fix spelling nits.  Thank you!
Allison

> --D
> 
> 
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   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 19a532a..bfd4466 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.
>> @@ -269,8 +269,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;
>>   	int			retval = 0;
>>   
>>   	/*
>> @@ -286,10 +287,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.
>>   		 */
>> @@ -731,115 +823,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 10/22] xfs: Hoist node transaction handling
  2021-03-01 18:20   ` Darrick J. Wong
@ 2021-03-02  8:26     ` Allison Henderson
  0 siblings, 0 replies; 87+ messages in thread
From: Allison Henderson @ 2021-03-02  8:26 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs



On 3/1/21 11:20 AM, Darrick J. Wong wrote:
> On Thu, Feb 18, 2021 at 09:53:36AM -0700, 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.
>>
>> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
>> ---
>>   fs/xfs/libxfs/xfs_attr.c | 53 +++++++++++++++++++++++++-----------------------
>>   1 file changed, 28 insertions(+), 25 deletions(-)
>>
>> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
>> index bfd4466..56d4b56 100644
>> --- a/fs/xfs/libxfs/xfs_attr.c
>> +++ b/fs/xfs/libxfs/xfs_attr.c
>> @@ -288,8 +288,34 @@ 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)
>>   			return error;
> 
> With the braces and indenting fixed the way Brian said,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Sure, will do.  Thank you!
Allison

> 
> --D
> 
>>   
>> @@ -381,32 +407,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	[flat|nested] 87+ messages in thread

* Re: [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations
  2021-03-02  7:13       ` Allison Henderson
@ 2021-03-03 11:50         ` Brian Foster
  0 siblings, 0 replies; 87+ messages in thread
From: Brian Foster @ 2021-03-03 11:50 UTC (permalink / raw)
  To: Allison Henderson; +Cc: Darrick J. Wong, linux-xfs

On Tue, Mar 02, 2021 at 12:13:23AM -0700, Allison Henderson wrote:
> 
> 
> On 2/26/21 5:56 PM, Allison Henderson wrote:
> > 
> > 
> > On 2/25/21 9:58 PM, Darrick J. Wong wrote:
> > > On Thu, Feb 18, 2021 at 09:53:42AM -0700, Allison Henderson wrote:
...
> > > 
> > > That said, as this series gets longer and longer I find it really more
> > > difficult to go through the whole series one by one vs. just diffing the
> > > whole branch and reviewing that.
> That's fine, it's that's easier for you, I could probably figure out in what
> patch the line of code you are commenting on would affect.  As noted in the
> call earlier today, I try to help reviewers in the cover letter by listing
> off which patches have changed since the last revision, and which havnt.
> That way you dont have to grind through things that havnt changed.  Though
> I'm not sure if folks really use the cover letter :-)
> 
> > > 
> > > <shrug> I don't really have a definitive answer for which is better.
> > > The xattr code is very complex, and I struggle even combining both of my
> > > usual strategies and attacking review from both ends.
> I actually think it's very reasonable to just focus on patches 11 and 12?
> Or maybe just the diff of the branch up to 12 if that's easier for you.
> Because if there's something about 11 or 12 that people want to change, it
> generally implies some change to the underlying refactoring.  So it seems a
> bit wasteful to keep re-reviewing the refactoring if the end result has some
> gripe about it.  If everyone is happy about how the refactoring ends up,
> then it makes sense to go back and review the implementation details.
> 

Just FWIW, I've been pretty much focusing on getting up to those one or
two patches and the preceding factoring leading up to it. I'm aware of
what's coming on top and have passed through it on (much) earlier
versions, but the release to release churn that results from the review
cycles is too much for me to keep up with in combination with other
things. To be clear, I'm certainly not _expecting_ to see a refresh of
the world so to speak on every post cycle based on feedback to the state
management code. (Of course there is nothing wrong with that if that's
your preference from a development perspective.)

One of the advantages of that IMO is that if everything leading up to
the introduction of state machine code is clean, isolated refactoring,
then ISTM those patches could roll into upstream as the series
progresses and minimize the snowball effect of the series overall.
That's one of the reasons I might harp a bit on some of the factoring
warts we might introduce, even if temporary and ultimately cleaned up by
the state management code, just because that makes it a little harder to
justify rolling things off the start of the series into upstream...

Brian

> To be clear, it's certaintly not that I dont appreciate the reviews, but I
> understand it's an exhausting thing to grind through, and I try to be
> mindful of that.  The only reason I dont push out the entire extended set is
> because its a 40 patch monster that's not reasonable for anyone to review
> right now, and I dont want people to think that I'm asking for that at this
> time.  So I just sort of send the links out so that people can see where
> it's meant to go.  For similar reasons, I've thought about reducing the
> visible window of patches to reduce reviewer burnout. So dont feel like you
> have to flog yourself through all 22, I think 12 is also reasonable cap off.
> 
> > > 
> > > By the way, have you been stress testing the xattr code with all this
> > > stuff applied?  At some point it becomes easier to pull this in and fix
> > > up the breakage than it is to review 22 slice-n-dice patches every cycle.
> > > 
> No, I've been testing with the attr group, and then a  few tests I came up
> with to replay the journal and also parent pointers.  I can toss in some
> stress tests too and make sure nothing turns up.
> 
> Thank you for the reviews, I know it's a lot
> Allison
> 
> > > --D
> > > 
> > > > + * attribute operations need to be processed.  An operation is
> > > > currently either
> > > > + * a set or remove.  Set or remove operations are described by
> > > > the xfs_attr_item
> > > > + * which may be logged to this intent.
> > > > + *
> > > > + * During a normal attr operation, name and value point to the
> > > > name and value
> > > > + * feilds of the calling functions xfs_da_args.  During a
> > > > recovery, the name
> > > > + * and value buffers are copied from the log, and stored in a
> > > > trailing buffer
> > > > + * attached to the xfs_attr_item until they are committed. 
> > > > They are freed when
> > > > + * the xfs_attr_item itself is freed when the work is done.
> > > > + */
> > > > +struct xfs_attri_log_item {
> > > > +    struct xfs_log_item        attri_item;
> > > > +    atomic_t            attri_refcount;
> > > > +    int                attri_name_len;
> > > > +    int                attri_value_len;
> > > > +    void                *attri_name;
> > > > +    void                *attri_value;
> > > > +    struct xfs_attri_log_format    attri_format;
> > > > +};
> > > > +
> > > > +/*
> > > > + * This is the "attr done" log item.  It is used to log the
> > > > fact that some attrs
> > > > + * earlier mentioned in an attri item have been freed.
> > > > + */
> > > > +struct xfs_attrd_log_item {
> > > > +    struct xfs_attri_log_item    *attrd_attrip;
> > > > +    struct xfs_log_item        attrd_item;
> > > > +    struct xfs_attrd_log_format    attrd_format;
> > > > +};
> > > > +
> > > > +#endif    /* __XFS_ATTR_ITEM_H__ */
> > > > diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
> > > > index 8f8837f..d7787a5 100644
> > > > --- a/fs/xfs/xfs_attr_list.c
> > > > +++ b/fs/xfs/xfs_attr_list.c
> > > > @@ -15,6 +15,7 @@
> > > >   #include "xfs_inode.h"
> > > >   #include "xfs_trans.h"
> > > >   #include "xfs_bmap.h"
> > > > +#include "xfs_da_btree.h"
> > > >   #include "xfs_attr.h"
> > > >   #include "xfs_attr_sf.h"
> > > >   #include "xfs_attr_leaf.h"
> > > > diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> > > > index 248083e..6682936 100644
> > > > --- a/fs/xfs/xfs_ioctl.c
> > > > +++ b/fs/xfs/xfs_ioctl.c
> > > > @@ -15,6 +15,8 @@
> > > >   #include "xfs_iwalk.h"
> > > >   #include "xfs_itable.h"
> > > >   #include "xfs_error.h"
> > > > +#include "xfs_da_format.h"
> > > > +#include "xfs_da_btree.h"
> > > >   #include "xfs_attr.h"
> > > >   #include "xfs_bmap.h"
> > > >   #include "xfs_bmap_util.h"
> > > > diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
> > > > index c1771e7..62e1534 100644
> > > > --- a/fs/xfs/xfs_ioctl32.c
> > > > +++ b/fs/xfs/xfs_ioctl32.c
> > > > @@ -17,6 +17,8 @@
> > > >   #include "xfs_itable.h"
> > > >   #include "xfs_fsops.h"
> > > >   #include "xfs_rtalloc.h"
> > > > +#include "xfs_da_format.h"
> > > > +#include "xfs_da_btree.h"
> > > >   #include "xfs_attr.h"
> > > >   #include "xfs_ioctl.h"
> > > >   #include "xfs_ioctl32.h"
> > > > diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> > > > index 00369502f..ce04721 100644
> > > > --- a/fs/xfs/xfs_iops.c
> > > > +++ b/fs/xfs/xfs_iops.c
> > > > @@ -13,6 +13,8 @@
> > > >   #include "xfs_inode.h"
> > > >   #include "xfs_acl.h"
> > > >   #include "xfs_quota.h"
> > > > +#include "xfs_da_format.h"
> > > > +#include "xfs_da_btree.h"
> > > >   #include "xfs_attr.h"
> > > >   #include "xfs_trans.h"
> > > >   #include "xfs_trace.h"
> > > > diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
> > > > index 0604183..290e57b 100644
> > > > --- a/fs/xfs/xfs_log.c
> > > > +++ b/fs/xfs/xfs_log.c
> > > > @@ -2070,6 +2070,10 @@ xlog_print_tic_res(
> > > >           REG_TYPE_STR(CUD_FORMAT, "cud_format"),
> > > >           REG_TYPE_STR(BUI_FORMAT, "bui_format"),
> > > >           REG_TYPE_STR(BUD_FORMAT, "bud_format"),
> > > > +        REG_TYPE_STR(ATTRI_FORMAT, "attri_format"),
> > > > +        REG_TYPE_STR(ATTRD_FORMAT, "attrd_format"),
> > > > +        REG_TYPE_STR(ATTR_NAME, "attr_name"),
> > > > +        REG_TYPE_STR(ATTR_VALUE, "attr_value"),
> > > >       };
> > > >       BUILD_BUG_ON(ARRAY_SIZE(res_type_str) != XLOG_REG_TYPE_MAX + 1);
> > > >   #undef REG_TYPE_STR
> > > > diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> > > > index 295a5c6..c0821b6 100644
> > > > --- a/fs/xfs/xfs_log_recover.c
> > > > +++ b/fs/xfs/xfs_log_recover.c
> > > > @@ -1775,6 +1775,8 @@ static const struct xlog_recover_item_ops
> > > > *xlog_recover_item_ops[] = {
> > > >       &xlog_cud_item_ops,
> > > >       &xlog_bui_item_ops,
> > > >       &xlog_bud_item_ops,
> > > > +    &xlog_attri_item_ops,
> > > > +    &xlog_attrd_item_ops,
> > > >   };
> > > >   static const struct xlog_recover_item_ops *
> > > > diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
> > > > index 0aa87c21..bc9c25e 100644
> > > > --- a/fs/xfs/xfs_ondisk.h
> > > > +++ b/fs/xfs/xfs_ondisk.h
> > > > @@ -132,6 +132,8 @@ xfs_check_ondisk_structs(void)
> > > >       XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format,    56);
> > > >       XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat,    20);
> > > >       XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header,        16);
> > > > +    XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,    40);
> > > > +    XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,    16);
> > > >       /*
> > > >        * The v5 superblock format extended several v4 header
> > > > structures with
> > > > diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> > > > index bca48b3..9b0c790 100644
> > > > --- a/fs/xfs/xfs_xattr.c
> > > > +++ b/fs/xfs/xfs_xattr.c
> > > > @@ -10,6 +10,7 @@
> > > >   #include "xfs_log_format.h"
> > > >   #include "xfs_da_format.h"
> > > >   #include "xfs_inode.h"
> > > > +#include "xfs_da_btree.h"
> > > >   #include "xfs_attr.h"
> > > >   #include "xfs_acl.h"
> > > >   #include "xfs_da_btree.h"
> > > > -- 
> > > > 2.7.4
> > > > 
> 


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

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

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-18 16:53 [PATCH v15 00/22] xfs: Delayed Attributes Allison Henderson
2021-02-18 16:53 ` [PATCH v15 01/22] xfs: Add helper xfs_attr_node_remove_step Allison Henderson
2021-02-24 15:03   ` Brian Foster
2021-02-25  6:17     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 02/22] xfs: Add xfs_attr_node_remove_cleanup Allison Henderson
2021-02-24 15:03   ` Brian Foster
2021-02-25  6:17     ` Allison Henderson
2021-02-26  3:00   ` Darrick J. Wong
2021-02-27  0:48     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 03/22] xfs: Hoist transaction handling in xfs_attr_node_remove_step Allison Henderson
2021-02-24 15:04   ` Brian Foster
2021-02-25  6:18     ` Allison Henderson
2021-02-26  3:02   ` Darrick J. Wong
2021-02-27  0:48     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 04/22] xfs: Hoist xfs_attr_set_shortform Allison Henderson
2021-02-24 15:04   ` Brian Foster
2021-02-25  6:18     ` Allison Henderson
2021-02-26  3:03   ` Darrick J. Wong
2021-02-27  0:48     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 05/22] xfs: Add helper xfs_attr_set_fmt Allison Henderson
2021-02-24 15:04   ` Brian Foster
2021-02-25  6:18     ` Allison Henderson
2021-02-26  3:07   ` Darrick J. Wong
2021-02-27  0:49     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 06/22] xfs: Separate xfs_attr_node_addname and xfs_attr_node_addname_work Allison Henderson
2021-02-24 15:04   ` Brian Foster
2021-02-25  6:18     ` Allison Henderson
2021-02-26  4:02   ` Darrick J. Wong
2021-02-27  0:54     ` Allison Henderson
2021-03-01 18:00       ` Darrick J. Wong
2021-03-02  8:26         ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 07/22] xfs: Add helper xfs_attr_node_addname_find_attr Allison Henderson
2021-02-24 15:04   ` Brian Foster
2021-02-25  6:18     ` Allison Henderson
2021-02-26  4:06   ` Darrick J. Wong
2021-02-27  0:54     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 08/22] xfs: Hoist xfs_attr_node_addname Allison Henderson
2021-02-24 18:42   ` Brian Foster
2021-02-25  6:19     ` Allison Henderson
2021-03-01 18:05   ` Darrick J. Wong
2021-03-02  8:26     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 09/22] xfs: Hoist xfs_attr_leaf_addname Allison Henderson
2021-02-24 18:42   ` Brian Foster
2021-02-25  6:19     ` Allison Henderson
2021-02-25 14:20       ` Brian Foster
2021-03-01 18:19   ` Darrick J. Wong
2021-03-02  8:26     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 10/22] xfs: Hoist node transaction handling Allison Henderson
2021-02-24 18:43   ` Brian Foster
2021-02-25  6:20     ` Allison Henderson
2021-03-01 18:20   ` Darrick J. Wong
2021-03-02  8:26     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 11/22] xfs: Add delay ready attr remove routines Allison Henderson
2021-02-24 18:45   ` Brian Foster
2021-02-25  7:01     ` Allison Henderson
2021-02-25 14:22       ` Brian Foster
2021-02-25 22:28         ` Allison Henderson
2021-02-28 15:39           ` Brian Foster
2021-03-02  8:26             ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 12/22] xfs: Add delay ready attr set routines Allison Henderson
2021-03-02  1:39   ` Darrick J. Wong
2021-02-18 16:53 ` [PATCH v15 13/22] xfs: Add state machine tracepoints Allison Henderson
2021-02-26  5:06   ` Darrick J. Wong
2021-02-27  0:57     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 14/22] xfs: Rename __xfs_attr_rmtval_remove Allison Henderson
2021-02-18 16:53 ` [PATCH v15 15/22] xfs: Handle krealloc errors in xlog_recover_add_to_cont_trans Allison Henderson
2021-02-26  5:06   ` Darrick J. Wong
2021-02-27  0:57     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 16/22] xfs: Set up infastructure for deferred attribute operations Allison Henderson
2021-02-26  4:58   ` Darrick J. Wong
2021-02-27  0:56     ` Allison Henderson
2021-03-02  7:13       ` Allison Henderson
2021-03-03 11:50         ` Brian Foster
2021-02-18 16:53 ` [PATCH v15 17/22] xfs: Skip flip flags for delayed attrs Allison Henderson
2021-02-26  5:02   ` Darrick J. Wong
2021-02-27  0:56     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 18/22] xfs: Add xfs_attr_set_deferred and xfs_attr_remove_deferred Allison Henderson
2021-02-26  5:00   ` Darrick J. Wong
2021-02-27  0:57     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 19/22] xfs: Remove unused xfs_attr_*_args Allison Henderson
2021-02-26  4:58   ` Darrick J. Wong
2021-02-27  0:57     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 20/22] xfs: Add delayed attributes error tag Allison Henderson
2021-02-18 16:53 ` [PATCH v15 21/22] xfs: Add delattr mount option Allison Henderson
2021-02-26  4:29   ` Darrick J. Wong
2021-02-27  0:55     ` Allison Henderson
2021-02-18 16:53 ` [PATCH v15 22/22] xfs: Merge xfs_delattr_context into xfs_attr_item Allison Henderson

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