All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 00/17] Return of the Parent Pointers
@ 2022-06-11  9:41 Allison Henderson
  2022-06-11  9:41 ` [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK Allison Henderson
                   ` (16 more replies)
  0 siblings, 17 replies; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Hi all,

This is the latest rebase of parent pointer attributes for xfs. The goal of
this patch set is to add a parent pointer attribute to each inode.  The
attribute name containing the parent inode, generation, and directory offset,
while the  attribute value contains the file name.  This feature will enable
future optimizations for online scrub, or any other feature that could make
use of quickly deriving an inodes path from  the mount point.  

It's been quite a while since we've seen parent pointers, so I think I'm just
going to start the versioning back at 1 rather than continue as v30 of the
giant extended set.

Questions comments and feedback appreciated!

Thanks all!
Allison

Allison Henderson (17):
  xfs: Add larp state XFS_DAS_CREATE_FORK
  xfs: Hold inode locks in xfs_ialloc
  xfs: get directory offset when adding directory name
  xfs: get directory offset when removing directory name
  xfs: get directory offset when replacing a directory name
  xfs: add parent pointer support to attribute code
  xfs: define parent pointer xattr format
  xfs: Add xfs_verify_pptr
  xfs: extent transaction reservations for parent attributes
  xfs: parent pointer attribute creation
  xfs: add parent attributes to link
  xfs: remove parent pointers in unlink
  xfs: Add parent pointers to rename
  xfs: Add the parent pointer support to the  superblock version 5.
  xfs: Add helper function xfs_attr_list_context_init
  xfs: Increase  XFS_DEFER_OPS_NR_INODES to 4
  xfs: Add parent pointer ioctl

 fs/xfs/Makefile                |   2 +
 fs/xfs/libxfs/xfs_attr.c       |  73 ++++++-
 fs/xfs/libxfs/xfs_attr.h       |   7 +-
 fs/xfs/libxfs/xfs_bmap.c       |   2 +-
 fs/xfs/libxfs/xfs_bmap.h       |   1 +
 fs/xfs/libxfs/xfs_da_btree.h   |   1 +
 fs/xfs/libxfs/xfs_da_format.h  |  30 ++-
 fs/xfs/libxfs/xfs_defer.h      |   2 +-
 fs/xfs/libxfs/xfs_dir2.c       |  21 +-
 fs/xfs/libxfs/xfs_dir2.h       |   7 +-
 fs/xfs/libxfs/xfs_dir2_block.c |   9 +-
 fs/xfs/libxfs/xfs_dir2_leaf.c  |   8 +-
 fs/xfs/libxfs/xfs_dir2_node.c  |   8 +-
 fs/xfs/libxfs/xfs_dir2_sf.c    |   6 +
 fs/xfs/libxfs/xfs_format.h     |  17 +-
 fs/xfs/libxfs/xfs_fs.h         |  47 ++++
 fs/xfs/libxfs/xfs_log_format.h |   1 +
 fs/xfs/libxfs/xfs_parent.c     |  87 ++++++++
 fs/xfs/libxfs/xfs_parent.h     |  33 +++
 fs/xfs/libxfs/xfs_sb.c         |   2 +
 fs/xfs/libxfs/xfs_trans_resv.c | 103 +++++++--
 fs/xfs/libxfs/xfs_trans_resv.h |   1 +
 fs/xfs/scrub/attr.c            |   2 +-
 fs/xfs/xfs_attr_item.c         |  10 +-
 fs/xfs/xfs_attr_list.c         |  17 +-
 fs/xfs/xfs_file.c              |   1 +
 fs/xfs/xfs_inode.c             | 380 ++++++++++++++++++++++++---------
 fs/xfs/xfs_ioctl.c             | 144 +++++++++++--
 fs/xfs/xfs_ioctl.h             |   2 +
 fs/xfs/xfs_ondisk.h            |   4 +
 fs/xfs/xfs_parent_utils.c      | 133 ++++++++++++
 fs/xfs/xfs_parent_utils.h      |  22 ++
 fs/xfs/xfs_qm.c                |   4 +-
 fs/xfs/xfs_super.c             |   4 +
 fs/xfs/xfs_symlink.c           |   6 +-
 fs/xfs/xfs_trans.c             |   7 +-
 fs/xfs/xfs_trans.h             |   2 +-
 fs/xfs/xfs_xattr.c             |   2 +-
 fs/xfs/xfs_xattr.h             |   1 +
 39 files changed, 1029 insertions(+), 180 deletions(-)
 create mode 100644 fs/xfs/libxfs/xfs_parent.c
 create mode 100644 fs/xfs/libxfs/xfs_parent.h
 create mode 100644 fs/xfs/xfs_parent_utils.c
 create mode 100644 fs/xfs/xfs_parent_utils.h

-- 
2.25.1


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

* [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-15  1:09   ` Dave Chinner
  2022-06-11  9:41 ` [PATCH v1 02/17] xfs: Hold inode locks in xfs_ialloc Allison Henderson
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Recent parent pointer testing has exposed a bug in the underlying
larp state machine.  A replace operation may remove an old attr
before adding the new one, but if it is the only attr in the fork,
then the fork is removed.  This later causes a null pointer in
xfs_attr_try_sf_addname which expects the fork present.  This
patch adds an extra state to create the fork.

Additionally the new state will be used by parent pointers which
need to add attributes to newly created inodes that do not yet
have a fork.

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

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 836ab1b8ed7b..a94850d9b8b1 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -719,15 +719,31 @@ xfs_attr_set_iter(
 	struct xfs_attr_intent		*attr)
 {
 	struct xfs_da_args              *args = attr->xattri_da_args;
+	int				sf_size;
 	int				error = 0;
 
 	/* State machine switch */
+
 next_state:
 	switch (attr->xattri_dela_state) {
 	case XFS_DAS_UNINIT:
 		ASSERT(0);
 		return -EFSCORRUPTED;
+	case XFS_DAS_CREATE_FORK:
+		sf_size = sizeof(struct xfs_attr_sf_hdr) +
+			  xfs_attr_sf_entsize_byname(args->namelen,
+						     args->valuelen);
+		error = xfs_bmap_set_attrforkoff(args->dp, sf_size, NULL);
+		if (error)
+			return error;
+		args->dp->i_afp = kmem_cache_zalloc(xfs_ifork_cache, 0);
+		args->dp->i_afp->if_format = XFS_DINODE_FMT_EXTENTS;
+		fallthrough;
 	case XFS_DAS_SF_ADD:
+		if (!args->dp->i_afp) {
+			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
+			goto next_state;
+		}
 		return xfs_attr_sf_addname(attr);
 	case XFS_DAS_LEAF_ADD:
 		return xfs_attr_leaf_addname(attr);
@@ -920,8 +936,10 @@ xfs_attr_defer_add(
 	error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_SET, &new);
 	if (error)
 		return error;
-
-	new->xattri_dela_state = xfs_attr_init_add_state(args);
+	if (!args->dp->i_afp)
+		new->xattri_dela_state = XFS_DAS_CREATE_FORK;
+	else
+		new->xattri_dela_state = xfs_attr_init_add_state(args);
 	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
 	trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
 
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index e329da3e7afa..7600eac74db7 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -445,7 +445,7 @@ struct xfs_attr_list_context {
  */
 enum xfs_delattr_state {
 	XFS_DAS_UNINIT		= 0,	/* No state has been set yet */
-
+	XFS_DAS_CREATE_FORK,		/* Create the attr fork */
 	/*
 	 * Initial sequence states. The replace setup code relies on the
 	 * ADD and REMOVE states for a specific format to be sequential so
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 6833110d1bd4..edafb6b1bfd6 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -977,7 +977,7 @@ xfs_bmap_add_attrfork_local(
 /*
  * Set an inode attr fork offset based on the format of the data fork.
  */
-static int
+int
 xfs_bmap_set_attrforkoff(
 	struct xfs_inode	*ip,
 	int			size,
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index 16db95b11589..a35945d44b80 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -172,6 +172,7 @@ void	xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
 		xfs_filblks_t len);
 unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp);
 int	xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
+int	xfs_bmap_set_attrforkoff(struct xfs_inode *ip, int size, int *version);
 void	xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
 		struct xfs_inode *ip, int whichfork);
 void	xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork);
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 4a28c2d77070..f524dbbb42d3 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -625,7 +625,9 @@ xfs_attri_item_recover(
 		args->value = nv->value.i_addr;
 		args->valuelen = nv->value.i_len;
 		args->total = xfs_attr_calc_size(args, &local);
-		if (xfs_inode_hasattr(args->dp))
+		if (!args->dp->i_afp)
+			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
+		else if (xfs_inode_hasattr(args->dp))
 			attr->xattri_dela_state = xfs_attr_init_replace_state(args);
 		else
 			attr->xattri_dela_state = xfs_attr_init_add_state(args);
-- 
2.25.1


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

* [PATCH v1 02/17] xfs: Hold inode locks in xfs_ialloc
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
  2022-06-11  9:41 ` [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:28   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 03/17] xfs: get directory offset when adding directory name Allison Henderson
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Modify xfs_ialloc to hold locks after return.  Caller will be
responsible for manual unlock.  We will need this later to hold locks
across parent pointer operations

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/xfs_inode.c   | 6 +++++-
 fs/xfs/xfs_qm.c      | 4 +++-
 fs/xfs/xfs_symlink.c | 3 +++
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 52d6f2c7d58b..23b93403a330 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -787,6 +787,8 @@ xfs_inode_inherit_flags2(
 /*
  * Initialise a newly allocated inode and return the in-core inode to the
  * caller locked exclusively.
+ *
+ * Caller is responsible for unlocking the inode manually upon return
  */
 int
 xfs_init_new_inode(
@@ -913,7 +915,7 @@ xfs_init_new_inode(
 	/*
 	 * Log the new values stuffed into the inode.
 	 */
-	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, ip, 0);
 	xfs_trans_log_inode(tp, ip, flags);
 
 	/* now that we have an i_mode we can setup the inode structure */
@@ -1090,6 +1092,7 @@ xfs_create(
 	xfs_qm_dqrele(pdqp);
 
 	*ipp = ip;
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 	return 0;
 
  out_trans_cancel:
@@ -1186,6 +1189,7 @@ xfs_create_tmpfile(
 	xfs_qm_dqrele(pdqp);
 
 	*ipp = ip;
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 	return 0;
 
  out_trans_cancel:
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index abf08bbf34a9..fa8321f74c13 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -817,8 +817,10 @@ xfs_qm_qino_alloc(
 		ASSERT(xfs_is_shutdown(mp));
 		xfs_alert(mp, "%s failed (error %d)!", __func__, error);
 	}
-	if (need_alloc)
+	if (need_alloc) {
 		xfs_finish_inode_setup(*ipp);
+		xfs_iunlock(*ipp, XFS_ILOCK_EXCL);
+	}
 	return error;
 }
 
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 4145ba872547..18f71fc90dd0 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -337,6 +337,7 @@ xfs_symlink(
 	xfs_qm_dqrele(pdqp);
 
 	*ipp = ip;
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 	return 0;
 
 out_trans_cancel:
@@ -358,6 +359,8 @@ xfs_symlink(
 
 	if (unlock_dp_on_error)
 		xfs_iunlock(dp, XFS_ILOCK_EXCL);
+	if (ip)
+		xfs_iunlock(ip, XFS_ILOCK_EXCL);
 	return error;
 }
 
-- 
2.25.1


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

* [PATCH v1 03/17] xfs: get directory offset when adding directory name
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
  2022-06-11  9:41 ` [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK Allison Henderson
  2022-06-11  9:41 ` [PATCH v1 02/17] xfs: Hold inode locks in xfs_ialloc Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:29   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 04/17] xfs: get directory offset when removing " Allison Henderson
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Return the directory offset information when adding an entry to the
directory.

This offset will be used as the parent pointer offset in xfs_create,
xfs_symlink, xfs_link and xfs_rename.

[dchinner: forward ported and cleaned up]
[dchinner: no s-o-b from Mark]
[bfoster: rebased, use args->geo in dir code]
[achender: rebased, chaged __uint32_t to xfs_dir2_dataptr_t]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_da_btree.h   | 1 +
 fs/xfs/libxfs/xfs_dir2.c       | 9 +++++++--
 fs/xfs/libxfs/xfs_dir2.h       | 2 +-
 fs/xfs/libxfs/xfs_dir2_block.c | 1 +
 fs/xfs/libxfs/xfs_dir2_leaf.c  | 2 ++
 fs/xfs/libxfs/xfs_dir2_node.c  | 2 ++
 fs/xfs/libxfs/xfs_dir2_sf.c    | 2 ++
 fs/xfs/xfs_inode.c             | 6 +++---
 fs/xfs/xfs_symlink.c           | 3 ++-
 9 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h
index d33b7686a0b3..e07eeecbe8a9 100644
--- a/fs/xfs/libxfs/xfs_da_btree.h
+++ b/fs/xfs/libxfs/xfs_da_btree.h
@@ -79,6 +79,7 @@ typedef struct xfs_da_args {
 	int		rmtvaluelen2;	/* remote attr value length in bytes */
 	uint32_t	op_flags;	/* operation flags */
 	enum xfs_dacmp	cmpresult;	/* name compare result for lookups */
+	xfs_dir2_dataptr_t offset;	/* OUT: offset in directory */
 } xfs_da_args_t;
 
 /*
diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
index 3cd51fa3837b..f7f7fa79593f 100644
--- a/fs/xfs/libxfs/xfs_dir2.c
+++ b/fs/xfs/libxfs/xfs_dir2.c
@@ -257,7 +257,8 @@ xfs_dir_createname(
 	struct xfs_inode	*dp,
 	const struct xfs_name	*name,
 	xfs_ino_t		inum,		/* new entry inode number */
-	xfs_extlen_t		total)		/* bmap's total block count */
+	xfs_extlen_t		total,		/* bmap's total block count */
+	xfs_dir2_dataptr_t	*offset)	/* OUT entry's dir offset */
 {
 	struct xfs_da_args	*args;
 	int			rval;
@@ -312,6 +313,10 @@ xfs_dir_createname(
 		rval = xfs_dir2_node_addname(args);
 
 out_free:
+	/* return the location that this entry was place in the parent inode */
+	if (offset)
+		*offset = args->offset;
+
 	kmem_free(args);
 	return rval;
 }
@@ -550,7 +555,7 @@ xfs_dir_canenter(
 	xfs_inode_t	*dp,
 	struct xfs_name	*name)		/* name of entry to add */
 {
-	return xfs_dir_createname(tp, dp, name, 0, 0);
+	return xfs_dir_createname(tp, dp, name, 0, 0, NULL);
 }
 
 /*
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index b6df3c34b26a..4d1c2570b833 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -40,7 +40,7 @@ extern int xfs_dir_init(struct xfs_trans *tp, struct xfs_inode *dp,
 				struct xfs_inode *pdp);
 extern int xfs_dir_createname(struct xfs_trans *tp, struct xfs_inode *dp,
 				const struct xfs_name *name, xfs_ino_t inum,
-				xfs_extlen_t tot);
+				xfs_extlen_t tot, xfs_dir2_dataptr_t *offset);
 extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
 				const struct xfs_name *name, xfs_ino_t *inum,
 				struct xfs_name *ci_name);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index df0869bba275..85869f604960 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -573,6 +573,7 @@ xfs_dir2_block_addname(
 	xfs_dir2_data_put_ftype(dp->i_mount, dep, args->filetype);
 	tagp = xfs_dir2_data_entry_tag_p(dp->i_mount, dep);
 	*tagp = cpu_to_be16((char *)dep - (char *)hdr);
+	args->offset = xfs_dir2_byte_to_dataptr((char *)dep - (char *)hdr);
 	/*
 	 * Clean up the bestfree array and log the header, tail, and entry.
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index d9b66306a9a7..bd0c2f963545 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -865,6 +865,8 @@ xfs_dir2_leaf_addname(
 	xfs_dir2_data_put_ftype(dp->i_mount, dep, args->filetype);
 	tagp = xfs_dir2_data_entry_tag_p(dp->i_mount, dep);
 	*tagp = cpu_to_be16((char *)dep - (char *)hdr);
+	args->offset = xfs_dir2_db_off_to_dataptr(args->geo, use_block,
+						(char *)dep - (char *)hdr);
 	/*
 	 * Need to scan fix up the bestfree table.
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 7a03aeb9f4c9..5a9513c036b8 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -1974,6 +1974,8 @@ xfs_dir2_node_addname_int(
 	xfs_dir2_data_put_ftype(dp->i_mount, dep, args->filetype);
 	tagp = xfs_dir2_data_entry_tag_p(dp->i_mount, dep);
 	*tagp = cpu_to_be16((char *)dep - (char *)hdr);
+	args->offset = xfs_dir2_db_off_to_dataptr(args->geo, dbno,
+						  (char *)dep - (char *)hdr);
 	xfs_dir2_data_log_entry(args, dbp, dep);
 
 	/* Rescan the freespace and log the data block if needed. */
diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c
index 5a97a87eaa20..c6c06e8ab54b 100644
--- a/fs/xfs/libxfs/xfs_dir2_sf.c
+++ b/fs/xfs/libxfs/xfs_dir2_sf.c
@@ -485,6 +485,7 @@ xfs_dir2_sf_addname_easy(
 	memcpy(sfep->name, args->name, sfep->namelen);
 	xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber);
 	xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
+	args->offset = xfs_dir2_byte_to_dataptr(offset);
 
 	/*
 	 * Update the header and inode.
@@ -575,6 +576,7 @@ xfs_dir2_sf_addname_hard(
 	memcpy(sfep->name, args->name, sfep->namelen);
 	xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber);
 	xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
+	args->offset = xfs_dir2_byte_to_dataptr(offset);
 	sfp->count++;
 	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
 		sfp->i8count++;
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 23b93403a330..05be02f6f62b 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1052,7 +1052,7 @@ xfs_create(
 	unlock_dp_on_error = false;
 
 	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
-					resblks - XFS_IALLOC_SPACE_RES(mp));
+				   resblks - XFS_IALLOC_SPACE_RES(mp), NULL);
 	if (error) {
 		ASSERT(error != -ENOSPC);
 		goto out_trans_cancel;
@@ -1275,7 +1275,7 @@ xfs_link(
 	}
 
 	error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
-				   resblks);
+				   resblks, NULL);
 	if (error)
 		goto error_return;
 	xfs_trans_ichgtime(tp, tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
@@ -3294,7 +3294,7 @@ xfs_rename(
 		 * to account for the ".." reference from the new entry.
 		 */
 		error = xfs_dir_createname(tp, target_dp, target_name,
-					   src_ip->i_ino, spaceres);
+					   src_ip->i_ino, spaceres, NULL);
 		if (error)
 			goto out_trans_cancel;
 
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 18f71fc90dd0..c8b252fa98ff 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -314,7 +314,8 @@ xfs_symlink(
 	/*
 	 * Create the directory entry for the symlink.
 	 */
-	error = xfs_dir_createname(tp, dp, link_name, ip->i_ino, resblks);
+	error = xfs_dir_createname(tp, dp, link_name,
+			ip->i_ino, resblks, NULL);
 	if (error)
 		goto out_trans_cancel;
 	xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
-- 
2.25.1


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

* [PATCH v1 04/17] xfs: get directory offset when removing directory name
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (2 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 03/17] xfs: get directory offset when adding directory name Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:30   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 05/17] xfs: get directory offset when replacing a " Allison Henderson
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Return the directory offset information when removing an entry to the
directory.

This offset will be used as the parent pointer offset in xfs_remove.

[dchinner: forward ported and cleaned up]
[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
           Changed typedefs to raw struct types]

Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_dir2.c       | 6 +++++-
 fs/xfs/libxfs/xfs_dir2.h       | 3 ++-
 fs/xfs/libxfs/xfs_dir2_block.c | 4 ++--
 fs/xfs/libxfs/xfs_dir2_leaf.c  | 5 +++--
 fs/xfs/libxfs/xfs_dir2_node.c  | 5 +++--
 fs/xfs/libxfs/xfs_dir2_sf.c    | 2 ++
 fs/xfs/xfs_inode.c             | 4 ++--
 7 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
index f7f7fa79593f..c3fa1bd1c370 100644
--- a/fs/xfs/libxfs/xfs_dir2.c
+++ b/fs/xfs/libxfs/xfs_dir2.c
@@ -436,7 +436,8 @@ xfs_dir_removename(
 	struct xfs_inode	*dp,
 	struct xfs_name		*name,
 	xfs_ino_t		ino,
-	xfs_extlen_t		total)		/* bmap's total block count */
+	xfs_extlen_t		total,		/* bmap's total block count */
+	xfs_dir2_dataptr_t	*offset)	/* OUT: offset in directory */
 {
 	struct xfs_da_args	*args;
 	int			rval;
@@ -481,6 +482,9 @@ xfs_dir_removename(
 	else
 		rval = xfs_dir2_node_removename(args);
 out_free:
+	if (offset)
+		*offset = args->offset;
+
 	kmem_free(args);
 	return rval;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 4d1c2570b833..c581d3b19bc6 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -46,7 +46,8 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
 				struct xfs_name *ci_name);
 extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
 				struct xfs_name *name, xfs_ino_t ino,
-				xfs_extlen_t tot);
+				xfs_extlen_t tot,
+				xfs_dir2_dataptr_t *offset);
 extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
 				const struct xfs_name *name, xfs_ino_t inum,
 				xfs_extlen_t tot);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 85869f604960..4579e9be5d1a 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -810,9 +810,9 @@ xfs_dir2_block_removename(
 	/*
 	 * Point to the data entry using the leaf entry.
 	 */
+	args->offset = be32_to_cpu(blp[ent].address);
 	dep = (xfs_dir2_data_entry_t *)((char *)hdr +
-			xfs_dir2_dataptr_to_off(args->geo,
-						be32_to_cpu(blp[ent].address)));
+			xfs_dir2_dataptr_to_off(args->geo, args->offset));
 	/*
 	 * Mark the data entry's space free.
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index bd0c2f963545..c13763c16095 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -1381,9 +1381,10 @@ xfs_dir2_leaf_removename(
 	 * Point to the leaf entry, use that to point to the data entry.
 	 */
 	lep = &leafhdr.ents[index];
-	db = xfs_dir2_dataptr_to_db(geo, be32_to_cpu(lep->address));
+	args->offset = be32_to_cpu(lep->address);
+	db = xfs_dir2_dataptr_to_db(args->geo, args->offset);
 	dep = (xfs_dir2_data_entry_t *)((char *)hdr +
-		xfs_dir2_dataptr_to_off(geo, be32_to_cpu(lep->address)));
+		xfs_dir2_dataptr_to_off(args->geo, args->offset));
 	needscan = needlog = 0;
 	oldbest = be16_to_cpu(bf[0].length);
 	ltp = xfs_dir2_leaf_tail_p(geo, leaf);
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 5a9513c036b8..39cbdeafa0f6 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -1296,9 +1296,10 @@ xfs_dir2_leafn_remove(
 	/*
 	 * Extract the data block and offset from the entry.
 	 */
-	db = xfs_dir2_dataptr_to_db(geo, be32_to_cpu(lep->address));
+	args->offset = be32_to_cpu(lep->address);
+	db = xfs_dir2_dataptr_to_db(args->geo, args->offset);
 	ASSERT(dblk->blkno == db);
-	off = xfs_dir2_dataptr_to_off(geo, be32_to_cpu(lep->address));
+	off = xfs_dir2_dataptr_to_off(args->geo, args->offset);
 	ASSERT(dblk->index == off);
 
 	/*
diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c
index c6c06e8ab54b..51d42faabb18 100644
--- a/fs/xfs/libxfs/xfs_dir2_sf.c
+++ b/fs/xfs/libxfs/xfs_dir2_sf.c
@@ -971,6 +971,8 @@ xfs_dir2_sf_removename(
 								XFS_CMP_EXACT) {
 			ASSERT(xfs_dir2_sf_get_ino(mp, sfp, sfep) ==
 			       args->inumber);
+			args->offset = xfs_dir2_byte_to_dataptr(
+						xfs_dir2_sf_get_offset(sfep));
 			break;
 		}
 	}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 05be02f6f62b..0c0c82e5dc59 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2836,7 +2836,7 @@ xfs_remove(
 	if (error)
 		goto out_trans_cancel;
 
-	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks);
+	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks, NULL);
 	if (error) {
 		ASSERT(error != -ENOENT);
 		goto out_trans_cancel;
@@ -3391,7 +3391,7 @@ xfs_rename(
 					spaceres);
 	else
 		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
-					   spaceres);
+					   spaceres, NULL);
 
 	if (error)
 		goto out_trans_cancel;
-- 
2.25.1


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

* [PATCH v1 05/17] xfs: get directory offset when replacing a directory name
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (3 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 04/17] xfs: get directory offset when removing " Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:30   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 06/17] xfs: add parent pointer support to attribute code Allison Henderson
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Return the directory offset information when replacing an entry to the
directory.

This offset will be used as the parent pointer offset in xfs_rename.

[dchinner: forward ported and cleaned up]
[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
           Changed typedefs to raw struct types]

Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_dir2.c       |  8 ++++++--
 fs/xfs/libxfs/xfs_dir2.h       |  2 +-
 fs/xfs/libxfs/xfs_dir2_block.c |  4 ++--
 fs/xfs/libxfs/xfs_dir2_leaf.c  |  1 +
 fs/xfs/libxfs/xfs_dir2_node.c  |  1 +
 fs/xfs/libxfs/xfs_dir2_sf.c    |  2 ++
 fs/xfs/xfs_inode.c             | 16 ++++++++--------
 7 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
index c3fa1bd1c370..6af2f5a8e627 100644
--- a/fs/xfs/libxfs/xfs_dir2.c
+++ b/fs/xfs/libxfs/xfs_dir2.c
@@ -482,7 +482,7 @@ xfs_dir_removename(
 	else
 		rval = xfs_dir2_node_removename(args);
 out_free:
-	if (offset)
+	if (!rval && offset)
 		*offset = args->offset;
 
 	kmem_free(args);
@@ -498,7 +498,8 @@ xfs_dir_replace(
 	struct xfs_inode	*dp,
 	const struct xfs_name	*name,		/* name of entry to replace */
 	xfs_ino_t		inum,		/* new inode number */
-	xfs_extlen_t		total)		/* bmap's total block count */
+	xfs_extlen_t		total,		/* bmap's total block count */
+	xfs_dir2_dataptr_t	*offset)	/* OUT: offset in directory */
 {
 	struct xfs_da_args	*args;
 	int			rval;
@@ -546,6 +547,9 @@ xfs_dir_replace(
 	else
 		rval = xfs_dir2_node_replace(args);
 out_free:
+	if (offset)
+		*offset = args->offset;
+
 	kmem_free(args);
 	return rval;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index c581d3b19bc6..fd943c0c00a0 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -50,7 +50,7 @@ extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
 				xfs_dir2_dataptr_t *offset);
 extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
 				const struct xfs_name *name, xfs_ino_t inum,
-				xfs_extlen_t tot);
+				xfs_extlen_t tot, xfs_dir2_dataptr_t *offset);
 extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
 				struct xfs_name *name);
 
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 4579e9be5d1a..ee8905d16223 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -885,9 +885,9 @@ xfs_dir2_block_replace(
 	/*
 	 * Point to the data entry we need to change.
 	 */
+	args->offset = be32_to_cpu(blp[ent].address);
 	dep = (xfs_dir2_data_entry_t *)((char *)hdr +
-			xfs_dir2_dataptr_to_off(args->geo,
-						be32_to_cpu(blp[ent].address)));
+			xfs_dir2_dataptr_to_off(args->geo, args->offset));
 	ASSERT(be64_to_cpu(dep->inumber) != args->inumber);
 	/*
 	 * Change the inode number to the new value.
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index c13763c16095..958b9fea64bd 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -1518,6 +1518,7 @@ xfs_dir2_leaf_replace(
 	/*
 	 * Point to the data entry.
 	 */
+	args->offset = be32_to_cpu(lep->address);
 	dep = (xfs_dir2_data_entry_t *)
 	      ((char *)dbp->b_addr +
 	       xfs_dir2_dataptr_to_off(args->geo, be32_to_cpu(lep->address)));
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 39cbdeafa0f6..53cd0d5d94f7 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -2242,6 +2242,7 @@ xfs_dir2_node_replace(
 		hdr = state->extrablk.bp->b_addr;
 		ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
 		       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC));
+		args->offset = be32_to_cpu(leafhdr.ents[blk->index].address);
 		dep = (xfs_dir2_data_entry_t *)
 		      ((char *)hdr +
 		       xfs_dir2_dataptr_to_off(args->geo,
diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c
index 51d42faabb18..1de51eded26b 100644
--- a/fs/xfs/libxfs/xfs_dir2_sf.c
+++ b/fs/xfs/libxfs/xfs_dir2_sf.c
@@ -1109,6 +1109,8 @@ xfs_dir2_sf_replace(
 				xfs_dir2_sf_put_ino(mp, sfp, sfep,
 						args->inumber);
 				xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
+				args->offset = xfs_dir2_byte_to_dataptr(
+						  xfs_dir2_sf_get_offset(sfep));
 				break;
 			}
 		}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 0c0c82e5dc59..b2dfd84e1f62 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2817,7 +2817,7 @@ xfs_remove(
 		 */
 		if (dp->i_ino != tp->t_mountp->m_sb.sb_rootino) {
 			error = xfs_dir_replace(tp, ip, &xfs_name_dotdot,
-					tp->t_mountp->m_sb.sb_rootino, 0);
+					tp->t_mountp->m_sb.sb_rootino, 0, NULL);
 			if (error)
 				return error;
 		}
@@ -2952,12 +2952,12 @@ xfs_cross_rename(
 	int		dp2_flags = 0;
 
 	/* Swap inode number for dirent in first parent */
-	error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres);
+	error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres, NULL);
 	if (error)
 		goto out_trans_abort;
 
 	/* Swap inode number for dirent in second parent */
-	error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, spaceres);
+	error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, spaceres, NULL);
 	if (error)
 		goto out_trans_abort;
 
@@ -2971,7 +2971,7 @@ xfs_cross_rename(
 
 		if (S_ISDIR(VFS_I(ip2)->i_mode)) {
 			error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
-						dp1->i_ino, spaceres);
+						dp1->i_ino, spaceres, NULL);
 			if (error)
 				goto out_trans_abort;
 
@@ -2995,7 +2995,7 @@ xfs_cross_rename(
 
 		if (S_ISDIR(VFS_I(ip1)->i_mode)) {
 			error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
-						dp2->i_ino, spaceres);
+						dp2->i_ino, spaceres, NULL);
 			if (error)
 				goto out_trans_abort;
 
@@ -3315,7 +3315,7 @@ xfs_rename(
 		 * name at the destination directory, remove it first.
 		 */
 		error = xfs_dir_replace(tp, target_dp, target_name,
-					src_ip->i_ino, spaceres);
+					src_ip->i_ino, spaceres, NULL);
 		if (error)
 			goto out_trans_cancel;
 
@@ -3349,7 +3349,7 @@ xfs_rename(
 		 * directory.
 		 */
 		error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
-					target_dp->i_ino, spaceres);
+					target_dp->i_ino, spaceres, NULL);
 		ASSERT(error != -EEXIST);
 		if (error)
 			goto out_trans_cancel;
@@ -3388,7 +3388,7 @@ xfs_rename(
 	 */
 	if (wip)
 		error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino,
-					spaceres);
+					spaceres, NULL);
 	else
 		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
 					   spaceres, NULL);
-- 
2.25.1


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

* [PATCH v1 06/17] xfs: add parent pointer support to attribute code
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (4 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 05/17] xfs: get directory offset when replacing a " Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:33   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 07/17] xfs: define parent pointer xattr format Allison Henderson
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Add the new parent attribute type. XFS_ATTR_PARENT is used only for parent pointer
entries; it uses reserved blocks like XFS_ATTR_ROOT.

[dchinner: forward ported and cleaned up]
[achender: rebased]

Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c       | 4 +++-
 fs/xfs/libxfs/xfs_da_format.h  | 5 ++++-
 fs/xfs/libxfs/xfs_log_format.h | 1 +
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index a94850d9b8b1..ee5dfebcf163 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -996,11 +996,13 @@ xfs_attr_set(
 	struct xfs_inode	*dp = args->dp;
 	struct xfs_mount	*mp = dp->i_mount;
 	struct xfs_trans_res	tres;
-	bool			rsvd = (args->attr_filter & XFS_ATTR_ROOT);
+	bool			rsvd;
 	int			error, local;
 	int			rmt_blks = 0;
 	unsigned int		total;
 
+	rsvd = (args->attr_filter & (XFS_ATTR_ROOT | XFS_ATTR_PARENT)) != 0;
+
 	if (xfs_is_shutdown(dp->i_mount))
 		return -EIO;
 
diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
index 25e2841084e1..2d771e6429f2 100644
--- a/fs/xfs/libxfs/xfs_da_format.h
+++ b/fs/xfs/libxfs/xfs_da_format.h
@@ -688,12 +688,15 @@ struct xfs_attr3_leafblock {
 #define	XFS_ATTR_LOCAL_BIT	0	/* attr is stored locally */
 #define	XFS_ATTR_ROOT_BIT	1	/* limit access to trusted attrs */
 #define	XFS_ATTR_SECURE_BIT	2	/* limit access to secure attrs */
+#define 	XFS_ATTR_PARENT_BIT	3	/* parent pointer secure attrs */
 #define	XFS_ATTR_INCOMPLETE_BIT	7	/* attr in middle of create/delete */
 #define XFS_ATTR_LOCAL		(1u << XFS_ATTR_LOCAL_BIT)
 #define XFS_ATTR_ROOT		(1u << XFS_ATTR_ROOT_BIT)
 #define XFS_ATTR_SECURE		(1u << XFS_ATTR_SECURE_BIT)
+#define XFS_ATTR_PARENT		(1u << XFS_ATTR_PARENT_BIT)
 #define XFS_ATTR_INCOMPLETE	(1u << XFS_ATTR_INCOMPLETE_BIT)
-#define XFS_ATTR_NSP_ONDISK_MASK	(XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_NSP_ONDISK_MASK \
+			(XFS_ATTR_ROOT | XFS_ATTR_SECURE | XFS_ATTR_PARENT)
 
 /*
  * Alignment for namelist and valuelist entries (since they are mixed
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index b351b9dc6561..eea53874fde8 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -917,6 +917,7 @@ struct xfs_icreate_log {
  */
 #define XFS_ATTRI_FILTER_MASK		(XFS_ATTR_ROOT | \
 					 XFS_ATTR_SECURE | \
+					 XFS_ATTR_PARENT | \
 					 XFS_ATTR_INCOMPLETE)
 
 /*
-- 
2.25.1


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

* [PATCH v1 07/17] xfs: define parent pointer xattr format
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (5 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 06/17] xfs: add parent pointer support to attribute code Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:34   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 08/17] xfs: Add xfs_verify_pptr Allison Henderson
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

We need to define the parent pointer attribute format before we start
adding support for it into all the code that needs to use it. The EA
format we will use encodes the following information:

        name={parent inode #, parent inode generation, dirent offset}
        value={dirent filename}

The inode/gen gives all the information we need to reliably identify the
parent without requiring child->parent lock ordering, and allows
userspace to do pathname component level reconstruction without the
kernel ever needing to verify the parent itself as part of ioctl calls.

By using the dirent offset in the EA name, we have a method of knowing
the exact parent pointer EA we need to modify/remove in rename/unlink
without an unbound EA name search.

By keeping the dirent name in the value, we have enough information to
be able to validate and reconstruct damaged directory trees. While the
diroffset of a filename alone is not unique enough to identify the
child, the {diroffset,filename,child_inode} tuple is sufficient. That
is, if the diroffset gets reused and points to a different filename, we
can detect that from the contents of EA. If a link of the same name is
created, then we can check whether it points at the same inode as the
parent EA we current have.

[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
           changed p_ino to xfs_ino_t and p_namelen to uint8_t,
           moved to xfs_da_format for xfs_dir2_dataptr_t]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong<darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_da_format.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
index 2d771e6429f2..6ac8c8dd4aab 100644
--- a/fs/xfs/libxfs/xfs_da_format.h
+++ b/fs/xfs/libxfs/xfs_da_format.h
@@ -805,4 +805,29 @@ static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
 xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
 				      struct xfs_da3_blkinfo *hdr3);
 
+/*
+ * Parent pointer attribute format definition
+ *
+ * EA name encodes the parent inode number, generation and the offset of
+ * the dirent that points to the child inode. The EA value contains the
+ * same name as the dirent in the parent directory.
+ */
+struct xfs_parent_name_rec {
+	__be64  p_ino;
+	__be32  p_gen;
+	__be32  p_diroffset;
+};
+
+/*
+ * incore version of the above, also contains name pointers so callers
+ * can pass/obtain all the parent pointer information in a single structure
+ */
+struct xfs_parent_name_irec {
+	xfs_ino_t		p_ino;
+	uint32_t		p_gen;
+	xfs_dir2_dataptr_t	p_diroffset;
+	const char		*p_name;
+	uint8_t			p_namelen;
+};
+
 #endif /* __XFS_DA_FORMAT_H__ */
-- 
2.25.1


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

* [PATCH v1 08/17] xfs: Add xfs_verify_pptr
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (6 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 07/17] xfs: define parent pointer xattr format Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:35   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes Allison Henderson
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Attribute names of parent pointers are not strings.  So we need to modify
attr_namecheck to verify parent pointer records when the XFS_ATTR_PARENT flag is
set.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c | 43 +++++++++++++++++++++++++++++++++++++---
 fs/xfs/libxfs/xfs_attr.h |  3 ++-
 fs/xfs/scrub/attr.c      |  2 +-
 fs/xfs/xfs_attr_item.c   |  6 ++++--
 fs/xfs/xfs_attr_list.c   | 17 +++++++++++-----
 5 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index ee5dfebcf163..30c8d9e9c2f1 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -1606,9 +1606,29 @@ xfs_attr_node_get(
 	return error;
 }
 
-/* Returns true if the attribute entry name is valid. */
-bool
-xfs_attr_namecheck(
+/*
+ * Verify parent pointer attribute is valid.
+ * Return true on success or false on failure
+ */
+STATIC bool
+xfs_verify_pptr(struct xfs_mount *mp, struct xfs_parent_name_rec *rec)
+{
+	xfs_ino_t p_ino = (xfs_ino_t)be64_to_cpu(rec->p_ino);
+	xfs_dir2_dataptr_t p_diroffset =
+		(xfs_dir2_dataptr_t)be32_to_cpu(rec->p_diroffset);
+
+	if (!xfs_verify_ino(mp, p_ino))
+		return false;
+
+	if (p_diroffset > XFS_DIR2_MAX_DATAPTR)
+		return false;
+
+	return true;
+}
+
+/* Returns true if the string attribute entry name is valid. */
+static bool
+xfs_str_attr_namecheck(
 	const void	*name,
 	size_t		length)
 {
@@ -1623,6 +1643,23 @@ xfs_attr_namecheck(
 	return !memchr(name, 0, length);
 }
 
+/* Returns true if the attribute entry name is valid. */
+bool
+xfs_attr_namecheck(
+	struct xfs_mount	*mp,
+	const void		*name,
+	size_t			length,
+	int			flags)
+{
+	if (flags & XFS_ATTR_PARENT) {
+		if (length != sizeof(struct xfs_parent_name_rec))
+			return false;
+		return xfs_verify_pptr(mp, (struct xfs_parent_name_rec *)name);
+	}
+
+	return xfs_str_attr_namecheck(name, length);
+}
+
 int __init
 xfs_attr_intent_init_cache(void)
 {
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 7600eac74db7..a87bc503976b 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -562,7 +562,8 @@ 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_attr_intent *attr);
 int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
-bool xfs_attr_namecheck(const void *name, size_t length);
+bool xfs_attr_namecheck(struct xfs_mount *mp, const void *name, size_t length,
+			int flags);
 int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
 void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
 			 unsigned int *total);
diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
index b6f0c9f3f124..d3e75c077fab 100644
--- a/fs/xfs/scrub/attr.c
+++ b/fs/xfs/scrub/attr.c
@@ -128,7 +128,7 @@ xchk_xattr_listent(
 	}
 
 	/* Does this name make sense? */
-	if (!xfs_attr_namecheck(name, namelen)) {
+	if (!xfs_attr_namecheck(sx->sc->mp, name, namelen, flags)) {
 		xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno);
 		return;
 	}
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index f524dbbb42d3..60fee5814655 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -587,7 +587,8 @@ xfs_attri_item_recover(
 	 */
 	attrp = &attrip->attri_format;
 	if (!xfs_attri_validate(mp, attrp) ||
-	    !xfs_attr_namecheck(nv->name.i_addr, nv->name.i_len))
+	    !xfs_attr_namecheck(mp, nv->name.i_addr, nv->name.i_len,
+				attrp->alfi_attr_filter))
 		return -EFSCORRUPTED;
 
 	error = xlog_recover_iget(mp,  attrp->alfi_ino, &ip);
@@ -742,7 +743,8 @@ xlog_recover_attri_commit_pass2(
 		return -EFSCORRUPTED;
 	}
 
-	if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
+	if (!xfs_attr_namecheck(mp, attr_name, attri_formatp->alfi_name_len,
+				attri_formatp->alfi_attr_filter)) {
 		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
 		return -EFSCORRUPTED;
 	}
diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
index 90a14e85e76d..3bac0647a927 100644
--- a/fs/xfs/xfs_attr_list.c
+++ b/fs/xfs/xfs_attr_list.c
@@ -58,9 +58,13 @@ xfs_attr_shortform_list(
 	struct xfs_attr_sf_sort		*sbuf, *sbp;
 	struct xfs_attr_shortform	*sf;
 	struct xfs_attr_sf_entry	*sfe;
+	struct xfs_mount		*mp;
 	int				sbsize, nsbuf, count, i;
 	int				error = 0;
 
+	ASSERT(context != NULL);
+	ASSERT(dp != NULL);
+	mp = dp->i_mount;
 	ASSERT(dp->i_afp != NULL);
 	sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data;
 	ASSERT(sf != NULL);
@@ -83,8 +87,9 @@ xfs_attr_shortform_list(
 	     (dp->i_afp->if_bytes + sf->hdr.count * 16) < context->bufsize)) {
 		for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
 			if (XFS_IS_CORRUPT(context->dp->i_mount,
-					   !xfs_attr_namecheck(sfe->nameval,
-							       sfe->namelen)))
+					   !xfs_attr_namecheck(mp, sfe->nameval,
+							       sfe->namelen,
+							       sfe->flags)))
 				return -EFSCORRUPTED;
 			context->put_listent(context,
 					     sfe->flags,
@@ -175,8 +180,9 @@ xfs_attr_shortform_list(
 			cursor->offset = 0;
 		}
 		if (XFS_IS_CORRUPT(context->dp->i_mount,
-				   !xfs_attr_namecheck(sbp->name,
-						       sbp->namelen))) {
+				   !xfs_attr_namecheck(mp, sbp->name,
+						       sbp->namelen,
+						       sbp->flags))) {
 			error = -EFSCORRUPTED;
 			goto out;
 		}
@@ -466,7 +472,8 @@ xfs_attr3_leaf_list_int(
 		}
 
 		if (XFS_IS_CORRUPT(context->dp->i_mount,
-				   !xfs_attr_namecheck(name, namelen)))
+				   !xfs_attr_namecheck(mp, name, namelen,
+						       entry->flags)))
 			return -EFSCORRUPTED;
 		context->put_listent(context, entry->flags,
 					      name, namelen, valuelen);
-- 
2.25.1


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

* [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (7 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 08/17] xfs: Add xfs_verify_pptr Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-16  5:38   ` Dave Chinner
  2022-06-29 18:37   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 10/17] xfs: parent pointer attribute creation Allison Henderson
                   ` (7 subsequent siblings)
  16 siblings, 2 replies; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

We need to add, remove or modify parent pointer attributes during
create/link/unlink/rename operations atomically with the dirents in the
parent directories being modified. This means they need to be modified
in the same transaction as the parent directories, and so we need to add
the required space for the attribute modifications to the transaction
reservations.

[achender: rebased, added xfs_sb_version_hasparent stub]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_format.h     |   5 ++
 fs/xfs/libxfs/xfs_trans_resv.c | 103 +++++++++++++++++++++++++++------
 fs/xfs/libxfs/xfs_trans_resv.h |   1 +
 3 files changed, 90 insertions(+), 19 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index afdfc8108c5f..96976497306c 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -390,6 +390,11 @@ xfs_sb_has_incompat_feature(
 	return (sbp->sb_features_incompat & feature) != 0;
 }
 
+static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
+{
+	return false; /* We'll enable this at the end of the set */
+}
+
 #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS   (1 << 0)	/* Delayed Attributes */
 #define XFS_SB_FEAT_INCOMPAT_LOG_ALL \
 	(XFS_SB_FEAT_INCOMPAT_LOG_XATTRS)
diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c
index e9913c2c5a24..fbe46fd3b722 100644
--- a/fs/xfs/libxfs/xfs_trans_resv.c
+++ b/fs/xfs/libxfs/xfs_trans_resv.c
@@ -909,24 +909,30 @@ xfs_calc_sb_reservation(
 	return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
 }
 
-void
-xfs_trans_resv_calc(
+/*
+ * Namespace reservations.
+ *
+ * These get tricky when parent pointers are enabled as we have attribute
+ * modifications occurring from within these transactions. Rather than confuse
+ * each of these reservation calculations with the conditional attribute
+ * reservations, add them here in a clear and concise manner. This assumes that
+ * the attribute reservations have already been calculated.
+ *
+ * Note that we only include the static attribute reservation here; the runtime
+ * reservation will have to be modified by the size of the attributes being
+ * added/removed/modified. See the comments on the attribute reservation
+ * calculations for more details.
+ *
+ * Note for rename: rename will vastly overestimate requirements. This will be
+ * addressed later when modifications are made to ensure parent attribute
+ * modifications can be done atomically with the rename operation.
+ */
+STATIC void
+xfs_calc_namespace_reservations(
 	struct xfs_mount	*mp,
 	struct xfs_trans_resv	*resp)
 {
-	int			logcount_adj = 0;
-
-	/*
-	 * The following transactions are logged in physical format and
-	 * require a permanent reservation on space.
-	 */
-	resp->tr_write.tr_logres = xfs_calc_write_reservation(mp, false);
-	resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
-	resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
-	resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp, false);
-	resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
-	resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+	ASSERT(resp->tr_attrsetm.tr_logres > 0);
 
 	resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
 	resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
@@ -948,15 +954,72 @@ xfs_trans_resv_calc(
 	resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
 	resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
+	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
+	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
+	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+	xfs_calc_parent_ptr_reservations(mp);
+}
+
+void xfs_calc_parent_ptr_reservations(struct xfs_mount     *mp)
+{
+	struct xfs_trans_resv   *resp = M_RES(mp);
+
+	/* Calculate extra space needed for parent pointer attributes */
+	if (!xfs_sb_version_hasparent(&mp->m_sb))
+		return;
+
+	/* rename can add/remove/modify 4 parent attributes */
+	resp->tr_rename.tr_logres += 4 * max(resp->tr_attrsetm.tr_logres,
+					 resp->tr_attrrm.tr_logres);
+	resp->tr_rename.tr_logcount += 4 * max(resp->tr_attrsetm.tr_logcount,
+					   resp->tr_attrrm.tr_logcount);
+
+	/* create will add 1 parent attribute */
+	resp->tr_create.tr_logres += resp->tr_attrsetm.tr_logres;
+	resp->tr_create.tr_logcount += resp->tr_attrsetm.tr_logcount;
+
+	/* mkdir will add 1 parent attribute */
+	resp->tr_mkdir.tr_logres += resp->tr_attrsetm.tr_logres;
+	resp->tr_mkdir.tr_logcount += resp->tr_attrsetm.tr_logcount;
+
+	/* link will add 1 parent attribute */
+	resp->tr_link.tr_logres += resp->tr_attrsetm.tr_logres;
+	resp->tr_link.tr_logcount += resp->tr_attrsetm.tr_logcount;
+
+	/* symlink will add 1 parent attribute */
+	resp->tr_symlink.tr_logres += resp->tr_attrsetm.tr_logres;
+	resp->tr_symlink.tr_logcount += resp->tr_attrsetm.tr_logcount;
+
+	/* remove will remove 1 parent attribute */
+	resp->tr_remove.tr_logres += resp->tr_attrrm.tr_logres;
+	resp->tr_remove.tr_logcount += resp->tr_attrrm.tr_logcount;
+}
+
+void
+xfs_trans_resv_calc(
+	struct xfs_mount	*mp,
+	struct xfs_trans_resv	*resp)
+{
+	int			logcount_adj = 0;
+
+	/*
+	 * The following transactions are logged in physical format and
+	 * require a permanent reservation on space.
+	 */
+	resp->tr_write.tr_logres = xfs_calc_write_reservation(mp, false);
+	resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
+	resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+	resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp, false);
+	resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
+	resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
 	resp->tr_create_tmpfile.tr_logres =
 			xfs_calc_create_tmpfile_reservation(mp);
 	resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
 	resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
-	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
-	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
-	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
 	resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
 	resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
 	resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
@@ -986,6 +1049,8 @@ xfs_trans_resv_calc(
 	resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
 	resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
+	xfs_calc_namespace_reservations(mp, resp);
+
 	/*
 	 * The following transactions are logged in logical format with
 	 * a default log count.
diff --git a/fs/xfs/libxfs/xfs_trans_resv.h b/fs/xfs/libxfs/xfs_trans_resv.h
index 0554b9d775d2..cab8084a84d6 100644
--- a/fs/xfs/libxfs/xfs_trans_resv.h
+++ b/fs/xfs/libxfs/xfs_trans_resv.h
@@ -101,5 +101,6 @@ uint xfs_allocfree_block_count(struct xfs_mount *mp, uint num_ops);
 unsigned int xfs_calc_itruncate_reservation_minlogsize(struct xfs_mount *mp);
 unsigned int xfs_calc_write_reservation_minlogsize(struct xfs_mount *mp);
 unsigned int xfs_calc_qm_dqalloc_reservation_minlogsize(struct xfs_mount *mp);
+void xfs_calc_parent_ptr_reservations(struct xfs_mount *mp);
 
 #endif	/* __XFS_TRANS_RESV_H__ */
-- 
2.25.1


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

* [PATCH v1 10/17] xfs: parent pointer attribute creation
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (8 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-11 15:10   ` kernel test robot
                     ` (2 more replies)
  2022-06-11  9:41 ` [PATCH v1 11/17] xfs: add parent attributes to link Allison Henderson
                   ` (6 subsequent siblings)
  16 siblings, 3 replies; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Add parent pointer attribute during xfs_create, and subroutines to
initialize attributes

[bfoster: rebase, use VFS inode generation]
[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
           fixed some null pointer bugs,
           merged error handling patch,
           added subroutines to handle attribute initialization,
           remove unnecessary ENOSPC handling in xfs_attr_set_first_parent]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/Makefile            |  1 +
 fs/xfs/libxfs/xfs_attr.c   |  2 +-
 fs/xfs/libxfs/xfs_attr.h   |  1 +
 fs/xfs/libxfs/xfs_parent.c | 77 +++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_parent.h | 31 ++++++++++++++
 fs/xfs/xfs_inode.c         | 88 +++++++++++++++++++++++++++-----------
 fs/xfs/xfs_xattr.c         |  2 +-
 fs/xfs/xfs_xattr.h         |  1 +
 8 files changed, 177 insertions(+), 26 deletions(-)

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index b056cfc6398e..fc717dc3470c 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -40,6 +40,7 @@ xfs-y				+= $(addprefix libxfs/, \
 				   xfs_inode_fork.o \
 				   xfs_inode_buf.o \
 				   xfs_log_rlimit.o \
+				   xfs_parent.o \
 				   xfs_ag_resv.o \
 				   xfs_rmap.o \
 				   xfs_rmap_btree.o \
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 30c8d9e9c2f1..f814a9177237 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -926,7 +926,7 @@ xfs_attr_intent_init(
 }
 
 /* Sets an attribute for an inode as a deferred operation */
-static int
+int
 xfs_attr_defer_add(
 	struct xfs_da_args	*args)
 {
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index a87bc503976b..576062e37d11 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -559,6 +559,7 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
 bool xfs_attr_is_leaf(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_defer_add(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
 int xfs_attr_set_iter(struct xfs_attr_intent *attr);
 int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
new file mode 100644
index 000000000000..cb546652bde9
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_da_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_inode.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_trans.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_bmap.h"
+
+/*
+ * Parent pointer attribute handling.
+ *
+ * Because the attribute value is a filename component, it will never be longer
+ * than 255 bytes. This means the attribute will always be a local format
+ * attribute as it is xfs_attr_leaf_entsize_local_max() for v5 filesystems will
+ * always be larger than this (max is 75% of block size).
+ *
+ * Creating a new parent attribute will always create a new attribute - there
+ * should never, ever be an existing attribute in the tree for a new inode.
+ * ENOSPC behavior is problematic - creating the inode without the parent
+ * pointer is effectively a corruption, so we allow parent attribute creation
+ * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
+ * occurring.
+ */
+
+
+/* Initializes a xfs_parent_name_rec to be stored as an attribute name */
+void
+xfs_init_parent_name_rec(
+	struct xfs_parent_name_rec	*rec,
+	struct xfs_inode		*ip,
+	uint32_t			p_diroffset)
+{
+	xfs_ino_t			p_ino = ip->i_ino;
+	uint32_t			p_gen = VFS_I(ip)->i_generation;
+
+	rec->p_ino = cpu_to_be64(p_ino);
+	rec->p_gen = cpu_to_be32(p_gen);
+	rec->p_diroffset = cpu_to_be32(p_diroffset);
+}
+
+/* Initializes a xfs_parent_name_irec from an xfs_parent_name_rec */
+void
+xfs_init_parent_name_irec(
+	struct xfs_parent_name_irec	*irec,
+	struct xfs_parent_name_rec	*rec)
+{
+	irec->p_ino = be64_to_cpu(rec->p_ino);
+	irec->p_gen = be32_to_cpu(rec->p_gen);
+	irec->p_diroffset = be32_to_cpu(rec->p_diroffset);
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
new file mode 100644
index 000000000000..10dc576ce693
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Oracle, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation Inc.
+ */
+#ifndef	__XFS_PARENT_H__
+#define	__XFS_PARENT_H__
+
+#include "xfs_da_format.h"
+#include "xfs_format.h"
+
+/*
+ * Parent pointer attribute prototypes
+ */
+void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
+			      struct xfs_inode *ip,
+			      uint32_t p_diroffset);
+void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
+			       struct xfs_parent_name_rec *rec);
+#endif	/* __XFS_PARENT_H__ */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index b2dfd84e1f62..6b1e4cb11b5c 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -36,6 +36,8 @@
 #include "xfs_reflink.h"
 #include "xfs_ag.h"
 #include "xfs_log_priv.h"
+#include "xfs_parent.h"
+#include "xfs_xattr.h"
 
 struct kmem_cache *xfs_inode_cache;
 
@@ -962,27 +964,40 @@ xfs_bumplink(
 
 int
 xfs_create(
-	struct user_namespace	*mnt_userns,
-	xfs_inode_t		*dp,
-	struct xfs_name		*name,
-	umode_t			mode,
-	dev_t			rdev,
-	bool			init_xattrs,
-	xfs_inode_t		**ipp)
-{
-	int			is_dir = S_ISDIR(mode);
-	struct xfs_mount	*mp = dp->i_mount;
-	struct xfs_inode	*ip = NULL;
-	struct xfs_trans	*tp = NULL;
-	int			error;
-	bool                    unlock_dp_on_error = false;
-	prid_t			prid;
-	struct xfs_dquot	*udqp = NULL;
-	struct xfs_dquot	*gdqp = NULL;
-	struct xfs_dquot	*pdqp = NULL;
-	struct xfs_trans_res	*tres;
-	uint			resblks;
-	xfs_ino_t		ino;
+	struct user_namespace		*mnt_userns,
+	xfs_inode_t			*dp,
+	struct xfs_name			*name,
+	umode_t				mode,
+	dev_t				rdev,
+	bool				init_xattrs,
+	xfs_inode_t			**ipp)
+{
+	int				is_dir = S_ISDIR(mode);
+	struct xfs_mount		*mp = dp->i_mount;
+	struct xfs_inode		*ip = NULL;
+	struct xfs_trans		*tp = NULL;
+	int				error;
+	bool				unlock_dp_on_error = false;
+	prid_t				prid;
+	struct xfs_dquot		*udqp = NULL;
+	struct xfs_dquot		*gdqp = NULL;
+	struct xfs_dquot		*pdqp = NULL;
+	struct xfs_trans_res		*tres;
+	uint				resblks;
+	xfs_ino_t			ino;
+	xfs_dir2_dataptr_t		diroffset;
+	struct xfs_parent_name_rec	rec;
+	struct xfs_da_args		args = {
+		.dp		= dp,
+		.geo		= mp->m_attr_geo,
+		.whichfork	= XFS_ATTR_FORK,
+		.attr_filter	= XFS_ATTR_PARENT,
+		.op_flags	= XFS_DA_OP_OKNOENT,
+		.name		= (const uint8_t *)&rec,
+		.namelen	= sizeof(rec),
+		.value		= (void *)name->name,
+		.valuelen	= name->len,
+	};
 
 	trace_xfs_create(dp, name);
 
@@ -1009,6 +1024,12 @@ xfs_create(
 		tres = &M_RES(mp)->tr_create;
 	}
 
+	if (xfs_has_larp(mp)) {
+		error = xfs_attr_grab_log_assist(mp);
+		if (error)
+			goto out_release_dquots;
+	}
+
 	/*
 	 * Initially assume that the file does not exist and
 	 * reserve the resources for that case.  If that is not
@@ -1024,7 +1045,7 @@ xfs_create(
 				resblks, &tp);
 	}
 	if (error)
-		goto out_release_dquots;
+		goto drop_incompat;
 
 	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
 	unlock_dp_on_error = true;
@@ -1048,11 +1069,12 @@ xfs_create(
 	 * the transaction cancel unlocking dp so don't do it explicitly in the
 	 * error path.
 	 */
-	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, dp, 0);
 	unlock_dp_on_error = false;
 
 	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
-				   resblks - XFS_IALLOC_SPACE_RES(mp), NULL);
+				   resblks - XFS_IALLOC_SPACE_RES(mp),
+				   &diroffset);
 	if (error) {
 		ASSERT(error != -ENOSPC);
 		goto out_trans_cancel;
@@ -1068,6 +1090,20 @@ xfs_create(
 		xfs_bumplink(tp, dp);
 	}
 
+	/*
+	 * If we have parent pointers, we need to add the attribute containing
+	 * the parent information now.
+	 */
+	if (xfs_sb_version_hasparent(&mp->m_sb)) {
+		xfs_init_parent_name_rec(&rec, dp, diroffset);
+		args.dp	= ip;
+		args.trans = tp;
+		args.hashval = xfs_da_hashname(args.name, args.namelen);
+		error =  xfs_attr_defer_add(&args);
+		if (error)
+			goto out_trans_cancel;
+	}
+
 	/*
 	 * If this is a synchronous mount, make sure that the
 	 * create transaction goes to disk before returning to
@@ -1093,6 +1129,7 @@ xfs_create(
 
 	*ipp = ip;
 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	xfs_iunlock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
 	return 0;
 
  out_trans_cancel:
@@ -1107,6 +1144,9 @@ xfs_create(
 		xfs_finish_inode_setup(ip);
 		xfs_irele(ip);
 	}
+ drop_incompat:
+	if (xfs_has_larp(mp))
+		xlog_drop_incompat_feat(mp->m_log);
  out_release_dquots:
 	xfs_qm_dqrele(udqp);
 	xfs_qm_dqrele(gdqp);
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index 35e13e125ec6..6012a6ba512c 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -27,7 +27,7 @@
  * they must release the permission by calling xlog_drop_incompat_feat
  * when they're done.
  */
-static inline int
+inline int
 xfs_attr_grab_log_assist(
 	struct xfs_mount	*mp)
 {
diff --git a/fs/xfs/xfs_xattr.h b/fs/xfs/xfs_xattr.h
index 2b09133b1b9b..3fd6520a4d69 100644
--- a/fs/xfs/xfs_xattr.h
+++ b/fs/xfs/xfs_xattr.h
@@ -7,6 +7,7 @@
 #define __XFS_XATTR_H__
 
 int xfs_attr_change(struct xfs_da_args *args);
+int xfs_attr_grab_log_assist(struct xfs_mount *mp);
 
 extern const struct xattr_handler *xfs_xattr_handlers[];
 
-- 
2.25.1


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

* [PATCH v1 11/17] xfs: add parent attributes to link
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (9 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 10/17] xfs: parent pointer attribute creation Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-16 22:39   ` Dave Chinner
  2022-06-11  9:41 ` [PATCH v1 12/17] xfs: remove parent pointers in unlink Allison Henderson
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

This patch modifies xfs_link to add a parent pointer to the inode.

[bfoster: rebase, use VFS inode fields, fix xfs_bmap_finish() usage]
[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
           fixed null pointer bugs]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/xfs_inode.c | 78 ++++++++++++++++++++++++++++++++++++----------
 fs/xfs/xfs_trans.c |  7 +++--
 fs/xfs/xfs_trans.h |  2 +-
 3 files changed, 67 insertions(+), 20 deletions(-)

diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 6b1e4cb11b5c..41c58df8e568 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1254,14 +1254,28 @@ xfs_create_tmpfile(
 
 int
 xfs_link(
-	xfs_inode_t		*tdp,
-	xfs_inode_t		*sip,
-	struct xfs_name		*target_name)
-{
-	xfs_mount_t		*mp = tdp->i_mount;
-	xfs_trans_t		*tp;
-	int			error, nospace_error = 0;
-	int			resblks;
+	xfs_inode_t			*tdp,
+	xfs_inode_t			*sip,
+	struct xfs_name			*target_name)
+{
+	xfs_mount_t			*mp = tdp->i_mount;
+	xfs_trans_t			*tp;
+	int				error, nospace_error = 0;
+	int				resblks;
+	struct xfs_parent_name_rec	rec;
+	xfs_dir2_dataptr_t		diroffset;
+
+	struct xfs_da_args		args = {
+		.dp		= sip,
+		.geo		= mp->m_attr_geo,
+		.whichfork	= XFS_ATTR_FORK,
+		.attr_filter	= XFS_ATTR_PARENT,
+		.op_flags	= XFS_DA_OP_OKNOENT,
+		.name		= (const uint8_t *)&rec,
+		.namelen	= sizeof(rec),
+		.value		= (void *)target_name->name,
+		.valuelen	= target_name->len,
+	};
 
 	trace_xfs_link(tdp, target_name);
 
@@ -1278,11 +1292,17 @@ xfs_link(
 	if (error)
 		goto std_return;
 
+	if (xfs_has_larp(mp)) {
+		error = xfs_attr_grab_log_assist(mp);
+		if (error)
+			goto std_return;
+	}
+
 	resblks = XFS_LINK_SPACE_RES(mp, target_name->len);
 	error = xfs_trans_alloc_dir(tdp, &M_RES(mp)->tr_link, sip, &resblks,
-			&tp, &nospace_error);
+			&tp, &nospace_error, 0);
 	if (error)
-		goto std_return;
+		goto drop_incompat;
 
 	/*
 	 * If we are using project inheritance, we only allow hard link
@@ -1315,14 +1335,30 @@ xfs_link(
 	}
 
 	error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
-				   resblks, NULL);
+				   resblks, &diroffset);
 	if (error)
-		goto error_return;
+		goto out_defer_cancel;
 	xfs_trans_ichgtime(tp, tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
 	xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);
 
 	xfs_bumplink(tp, sip);
 
+	/*
+	 * If we have parent pointers, we now need to add the parent record to
+	 * the attribute fork of the inode. If this is the initial parent
+	 * attribute, we need to create it correctly, otherwise we can just add
+	 * the parent to the inode.
+	 */
+	if (xfs_sb_version_hasparent(&mp->m_sb)) {
+		args.trans = tp;
+		xfs_init_parent_name_rec(&rec, tdp, diroffset);
+		args.hashval = xfs_da_hashname(args.name,
+					       args.namelen);
+		error = xfs_attr_defer_add(&args);
+		if (error)
+			goto out_defer_cancel;
+	}
+
 	/*
 	 * If this is a synchronous mount, make sure that the
 	 * link transaction goes to disk before returning to
@@ -1331,11 +1367,21 @@ xfs_link(
 	if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))
 		xfs_trans_set_sync(tp);
 
-	return xfs_trans_commit(tp);
+	error = xfs_trans_commit(tp);
+	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
+	xfs_iunlock(sip, XFS_ILOCK_EXCL);
+	return error;
 
- error_return:
+out_defer_cancel:
+	xfs_defer_cancel(tp);
+error_return:
 	xfs_trans_cancel(tp);
- std_return:
+	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
+	xfs_iunlock(sip, XFS_ILOCK_EXCL);
+drop_incompat:
+	if (xfs_has_larp(mp))
+		xlog_drop_incompat_feat(mp->m_log);
+std_return:
 	if (error == -ENOSPC && nospace_error)
 		error = nospace_error;
 	return error;
@@ -2819,7 +2865,7 @@ xfs_remove(
 	 */
 	resblks = XFS_REMOVE_SPACE_RES(mp);
 	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,
-			&tp, &dontcare);
+			&tp, &dontcare, XFS_ILOCK_EXCL);
 	if (error) {
 		ASSERT(error != -ENOSPC);
 		goto std_return;
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 82cf0189c0db..544097004b06 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -1273,7 +1273,8 @@ xfs_trans_alloc_dir(
 	struct xfs_inode	*ip,
 	unsigned int		*dblocks,
 	struct xfs_trans	**tpp,
-	int			*nospace_error)
+	int			*nospace_error,
+	int			join_flags)
 {
 	struct xfs_trans	*tp;
 	struct xfs_mount	*mp = ip->i_mount;
@@ -1295,8 +1296,8 @@ xfs_trans_alloc_dir(
 
 	xfs_lock_two_inodes(dp, XFS_ILOCK_EXCL, ip, XFS_ILOCK_EXCL);
 
-	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
-	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, dp, join_flags);
+	xfs_trans_ijoin(tp, ip, join_flags);
 
 	error = xfs_qm_dqattach_locked(dp, false);
 	if (error) {
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index 9561f193e7e1..4ac175f7ee69 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -266,7 +266,7 @@ int xfs_trans_alloc_ichange(struct xfs_inode *ip, struct xfs_dquot *udqp,
 		struct xfs_trans **tpp);
 int xfs_trans_alloc_dir(struct xfs_inode *dp, struct xfs_trans_res *resv,
 		struct xfs_inode *ip, unsigned int *dblocks,
-		struct xfs_trans **tpp, int *nospace_error);
+		struct xfs_trans **tpp, int *nospace_error, int join_flags);
 
 static inline void
 xfs_trans_set_context(
-- 
2.25.1


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

* [PATCH v1 12/17] xfs: remove parent pointers in unlink
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (10 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 11/17] xfs: add parent attributes to link Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 17:35   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 13/17] xfs: Add parent pointers to rename Allison Henderson
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

This patch removes the parent pointer attribute during unlink

[bfoster: rebase, use VFS inode generation]
[achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t
           implemented xfs_attr_remove_parent]

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_attr.c |  2 +-
 fs/xfs/libxfs/xfs_attr.h |  1 +
 fs/xfs/xfs_inode.c       | 63 +++++++++++++++++++++++++++++++---------
 3 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f814a9177237..b86188b63897 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -966,7 +966,7 @@ xfs_attr_defer_replace(
 }
 
 /* Removes an attribute for an inode as a deferred operation */
-static int
+int
 xfs_attr_defer_remove(
 	struct xfs_da_args	*args)
 {
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 576062e37d11..386dfc8d6053 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -560,6 +560,7 @@ bool xfs_attr_is_leaf(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_defer_add(struct xfs_da_args *args);
+int xfs_attr_defer_remove(struct xfs_da_args *args);
 int xfs_attr_set(struct xfs_da_args *args);
 int xfs_attr_set_iter(struct xfs_attr_intent *attr);
 int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 41c58df8e568..160f57df6d58 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2828,16 +2828,27 @@ xfs_iunpin_wait(
  */
 int
 xfs_remove(
-	xfs_inode_t             *dp,
-	struct xfs_name		*name,
-	xfs_inode_t		*ip)
-{
-	xfs_mount_t		*mp = dp->i_mount;
-	xfs_trans_t             *tp = NULL;
-	int			is_dir = S_ISDIR(VFS_I(ip)->i_mode);
-	int			dontcare;
-	int                     error = 0;
-	uint			resblks;
+	xfs_inode_t             	*dp,
+	struct xfs_name			*name,
+	xfs_inode_t			*ip)
+{
+	xfs_mount_t			*mp = dp->i_mount;
+	xfs_trans_t             	*tp = NULL;
+	int				is_dir = S_ISDIR(VFS_I(ip)->i_mode);
+	int				dontcare;
+	int                     	error = 0;
+	uint				resblks;
+	xfs_dir2_dataptr_t		dir_offset;
+	struct xfs_parent_name_rec	rec;
+	struct xfs_da_args		args = {
+		.dp		= ip,
+		.geo		= mp->m_attr_geo,
+		.whichfork	= XFS_ATTR_FORK,
+		.attr_filter	= XFS_ATTR_PARENT,
+		.op_flags	= XFS_DA_OP_OKNOENT,
+		.name		= (const uint8_t *)&rec,
+		.namelen	= sizeof(rec),
+	};
 
 	trace_xfs_remove(dp, name);
 
@@ -2852,6 +2863,12 @@ xfs_remove(
 	if (error)
 		goto std_return;
 
+	if (xfs_has_larp(mp)) {
+		error = xfs_attr_grab_log_assist(mp);
+		if (error)
+			goto std_return;
+	}
+
 	/*
 	 * We try to get the real space reservation first, allowing for
 	 * directory btree deletion(s) implying possible bmap insert(s).  If we
@@ -2865,10 +2882,10 @@ xfs_remove(
 	 */
 	resblks = XFS_REMOVE_SPACE_RES(mp);
 	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,
-			&tp, &dontcare, XFS_ILOCK_EXCL);
+			&tp, &dontcare, 0);
 	if (error) {
 		ASSERT(error != -ENOSPC);
-		goto std_return;
+		goto drop_incompat;
 	}
 
 	/*
@@ -2922,12 +2939,22 @@ xfs_remove(
 	if (error)
 		goto out_trans_cancel;
 
-	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks, NULL);
+	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks, &dir_offset);
 	if (error) {
 		ASSERT(error != -ENOENT);
 		goto out_trans_cancel;
 	}
 
+	if (xfs_sb_version_hasparent(&mp->m_sb)) {
+		xfs_init_parent_name_rec(&rec, dp, dir_offset);
+		args.hashval = xfs_da_hashname(args.name, args.namelen);
+		args.trans = tp;
+
+		error = xfs_attr_defer_remove(&args);
+		if (error)
+			goto out_trans_cancel;
+	}
+
 	/*
 	 * If this is a synchronous mount, make sure that the
 	 * remove transaction goes to disk before returning to
@@ -2938,15 +2965,23 @@ xfs_remove(
 
 	error = xfs_trans_commit(tp);
 	if (error)
-		goto std_return;
+		goto out_unlock;
 
 	if (is_dir && xfs_inode_is_filestream(ip))
 		xfs_filestream_deassociate(ip);
 
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	xfs_iunlock(dp, XFS_ILOCK_EXCL);
 	return 0;
 
  out_trans_cancel:
 	xfs_trans_cancel(tp);
+ out_unlock:
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ drop_incompat:
+	if (xfs_has_larp(mp))
+		xlog_drop_incompat_feat(mp->m_log);
  std_return:
 	return error;
 }
-- 
2.25.1


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

* [PATCH v1 13/17] xfs: Add parent pointers to rename
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (11 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 12/17] xfs: remove parent pointers in unlink Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:02   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5 Allison Henderson
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

This patch removes the old parent pointer attribute during the rename
operation, and re-adds the updated parent pointer.  In the case of
xfs_cross_rename, we modify the routine not to roll the transaction just
yet.  We will do this after the parent pointer is added in the calling
xfs_rename function.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/xfs_inode.c | 137 +++++++++++++++++++++++++++++++++------------
 1 file changed, 101 insertions(+), 36 deletions(-)

diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 160f57df6d58..4566613c6a71 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -3153,7 +3153,7 @@ xfs_cross_rename(
 	}
 	xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
 	xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
-	return xfs_finish_rename(tp);
+	return 0;
 
 out_trans_abort:
 	xfs_trans_cancel(tp);
@@ -3200,26 +3200,52 @@ xfs_rename_alloc_whiteout(
  */
 int
 xfs_rename(
-	struct user_namespace	*mnt_userns,
-	struct xfs_inode	*src_dp,
-	struct xfs_name		*src_name,
-	struct xfs_inode	*src_ip,
-	struct xfs_inode	*target_dp,
-	struct xfs_name		*target_name,
-	struct xfs_inode	*target_ip,
-	unsigned int		flags)
-{
-	struct xfs_mount	*mp = src_dp->i_mount;
-	struct xfs_trans	*tp;
-	struct xfs_inode	*wip = NULL;		/* whiteout inode */
-	struct xfs_inode	*inodes[__XFS_SORT_INODES];
-	int			i;
-	int			num_inodes = __XFS_SORT_INODES;
-	bool			new_parent = (src_dp != target_dp);
-	bool			src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
-	int			spaceres;
-	bool			retried = false;
-	int			error, nospace_error = 0;
+	struct user_namespace		*mnt_userns,
+	struct xfs_inode		*src_dp,
+	struct xfs_name			*src_name,
+	struct xfs_inode		*src_ip,
+	struct xfs_inode		*target_dp,
+	struct xfs_name			*target_name,
+	struct xfs_inode		*target_ip,
+	unsigned int			flags)
+{
+	struct xfs_mount		*mp = src_dp->i_mount;
+	struct xfs_trans		*tp;
+	struct xfs_inode		*wip = NULL;		/* whiteout inode */
+	struct xfs_inode		*inodes[__XFS_SORT_INODES];
+	int				i;
+	int				num_inodes = __XFS_SORT_INODES;
+	bool				new_parent = (src_dp != target_dp);
+	bool				src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
+	int				spaceres;
+	bool				retried = false;
+	int				error, nospace_error = 0;
+	struct xfs_parent_name_rec	new_rec;
+	struct xfs_parent_name_rec	old_rec;
+	xfs_dir2_dataptr_t		new_diroffset;
+	xfs_dir2_dataptr_t		old_diroffset;
+	struct xfs_da_args		new_args = {
+		.dp		= src_ip,
+		.geo		= mp->m_attr_geo,
+		.whichfork	= XFS_ATTR_FORK,
+		.attr_filter	= XFS_ATTR_PARENT,
+		.op_flags	= XFS_DA_OP_OKNOENT,
+		.name		= (const uint8_t *)&new_rec,
+		.namelen	= sizeof(new_rec),
+		.value		= (void *)target_name->name,
+		.valuelen	= target_name->len,
+	};
+	struct xfs_da_args		old_args = {
+		.dp		= src_ip,
+		.geo		= mp->m_attr_geo,
+		.whichfork	= XFS_ATTR_FORK,
+		.attr_filter	= XFS_ATTR_PARENT,
+		.op_flags	= XFS_DA_OP_OKNOENT,
+		.name		= (const uint8_t *)&old_rec,
+		.namelen	= sizeof(old_rec),
+		.value		= NULL,
+		.valuelen	= 0,
+	};
 
 	trace_xfs_rename(src_dp, target_dp, src_name, target_name);
 
@@ -3242,6 +3268,11 @@ xfs_rename(
 
 	xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
 				inodes, &num_inodes);
+	if (xfs_has_larp(mp)) {
+		error = xfs_attr_grab_log_assist(mp);
+		if (error)
+			goto out_release_wip;
+	}
 
 retry:
 	nospace_error = 0;
@@ -3254,7 +3285,7 @@ xfs_rename(
 				&tp);
 	}
 	if (error)
-		goto out_release_wip;
+		goto drop_incompat;
 
 	/*
 	 * Attach the dquots to the inodes
@@ -3276,14 +3307,14 @@ xfs_rename(
 	 * we can rely on either trans_commit or trans_cancel to unlock
 	 * them.
 	 */
-	xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, src_dp, 0);
 	if (new_parent)
-		xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL);
-	xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);
+		xfs_trans_ijoin(tp, target_dp, 0);
+	xfs_trans_ijoin(tp, src_ip, 0);
 	if (target_ip)
-		xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);
+		xfs_trans_ijoin(tp, target_ip, 0);
 	if (wip)
-		xfs_trans_ijoin(tp, wip, XFS_ILOCK_EXCL);
+		xfs_trans_ijoin(tp, wip, 0);
 
 	/*
 	 * If we are using project inheritance, we only allow renames
@@ -3293,15 +3324,16 @@ xfs_rename(
 	if (unlikely((target_dp->i_diflags & XFS_DIFLAG_PROJINHERIT) &&
 		     target_dp->i_projid != src_ip->i_projid)) {
 		error = -EXDEV;
-		goto out_trans_cancel;
+		goto out_unlock;
 	}
 
 	/* RENAME_EXCHANGE is unique from here on. */
-	if (flags & RENAME_EXCHANGE)
-		return xfs_cross_rename(tp, src_dp, src_name, src_ip,
+	if (flags & RENAME_EXCHANGE) {
+		error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
 					target_dp, target_name, target_ip,
 					spaceres);
-
+		goto out_pptr;
+	}
 	/*
 	 * Try to reserve quota to handle an expansion of the target directory.
 	 * We'll allow the rename to continue in reservationless mode if we hit
@@ -3415,7 +3447,7 @@ xfs_rename(
 		 * to account for the ".." reference from the new entry.
 		 */
 		error = xfs_dir_createname(tp, target_dp, target_name,
-					   src_ip->i_ino, spaceres, NULL);
+					   src_ip->i_ino, spaceres, &new_diroffset);
 		if (error)
 			goto out_trans_cancel;
 
@@ -3436,7 +3468,7 @@ xfs_rename(
 		 * name at the destination directory, remove it first.
 		 */
 		error = xfs_dir_replace(tp, target_dp, target_name,
-					src_ip->i_ino, spaceres, NULL);
+					src_ip->i_ino, spaceres, &new_diroffset);
 		if (error)
 			goto out_trans_cancel;
 
@@ -3470,7 +3502,7 @@ xfs_rename(
 		 * directory.
 		 */
 		error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
-					target_dp->i_ino, spaceres, NULL);
+					target_dp->i_ino, spaceres, &new_diroffset);
 		ASSERT(error != -EEXIST);
 		if (error)
 			goto out_trans_cancel;
@@ -3509,26 +3541,59 @@ xfs_rename(
 	 */
 	if (wip)
 		error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino,
-					spaceres, NULL);
+					spaceres, &old_diroffset);
 	else
 		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
-					   spaceres, NULL);
+					   spaceres, &old_diroffset);
 
 	if (error)
 		goto out_trans_cancel;
 
+out_pptr:
+	if (xfs_sb_version_hasparent(&mp->m_sb)) {
+		new_args.trans	= tp;
+		xfs_init_parent_name_rec(&new_rec, target_dp, new_diroffset);
+		new_args.hashval = xfs_da_hashname(new_args.name,
+						   new_args.namelen);
+		error =  xfs_attr_defer_add(&new_args);
+		if (error)
+			goto out_trans_cancel;
+
+		old_args.trans	= tp;
+		xfs_init_parent_name_rec(&old_rec, src_dp, old_diroffset);
+		old_args.hashval = xfs_da_hashname(old_args.name,
+						   old_args.namelen);
+		error = xfs_attr_defer_remove(&old_args);
+		if (error)
+			goto out_trans_cancel;
+	}
+
 	xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
 	xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
 	if (new_parent)
 		xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
 
 	error = xfs_finish_rename(tp);
+
+out_unlock:
 	if (wip)
 		xfs_irele(wip);
+	if (wip)
+		xfs_iunlock(wip, XFS_ILOCK_EXCL);
+	if (target_ip)
+		xfs_iunlock(target_ip, XFS_ILOCK_EXCL);
+	xfs_iunlock(src_ip, XFS_ILOCK_EXCL);
+	if (new_parent)
+		xfs_iunlock(target_dp, XFS_ILOCK_EXCL);
+	xfs_iunlock(src_dp, XFS_ILOCK_EXCL);
+
 	return error;
 
 out_trans_cancel:
 	xfs_trans_cancel(tp);
+drop_incompat:
+	if (xfs_has_larp(mp))
+		xlog_drop_incompat_feat(mp->m_log);
 out_release_wip:
 	if (wip)
 		xfs_irele(wip);
-- 
2.25.1


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

* [PATCH v1 14/17] xfs: Add the parent pointer support to the  superblock version 5.
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (12 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 13/17] xfs: Add parent pointers to rename Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-16  6:03   ` Dave Chinner
  2022-06-11  9:41 ` [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init Allison Henderson
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

[dchinner: forward ported and cleaned up]
[achender: rebased and added parent pointer attribute to
           compatible attributes mask]

Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_format.h | 14 +++++++++-----
 fs/xfs/libxfs/xfs_fs.h     |  1 +
 fs/xfs/libxfs/xfs_sb.c     |  2 ++
 fs/xfs/xfs_super.c         |  4 ++++
 4 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 96976497306c..e85d6b643622 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -83,6 +83,7 @@ struct xfs_ifork;
 #define	XFS_SB_VERSION2_OKBITS		\
 	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
 	 XFS_SB_VERSION2_ATTR2BIT	| \
+	 XFS_SB_VERSION2_PARENTBIT	| \
 	 XFS_SB_VERSION2_PROJID32BIT	| \
 	 XFS_SB_VERSION2_FTYPE)
 
@@ -353,11 +354,13 @@ xfs_sb_has_compat_feature(
 #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)		/* reverse map btree */
 #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/* reflinked files */
 #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/* inobt block counts */
+#define XFS_SB_FEAT_RO_COMPAT_PARENT	(1 << 4)		/* parent inode ptr */
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
-		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
-		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
-		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
-		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
+		(XFS_SB_FEAT_RO_COMPAT_FINOBT  | \
+		 XFS_SB_FEAT_RO_COMPAT_RMAPBT  | \
+		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
+		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT| \
+		 XFS_SB_FEAT_RO_COMPAT_PARENT)
 #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN	~XFS_SB_FEAT_RO_COMPAT_ALL
 static inline bool
 xfs_sb_has_ro_compat_feature(
@@ -392,7 +395,8 @@ xfs_sb_has_incompat_feature(
 
 static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
 {
-	return false; /* We'll enable this at the end of the set */
+	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
+		(sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_PARENT));
 }
 
 #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS   (1 << 0)	/* Delayed Attributes */
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 1cfd5bc6520a..b0b4d7a3aa15 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -237,6 +237,7 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_BIGTIME	(1 << 21) /* 64-bit nsec timestamps */
 #define XFS_FSOP_GEOM_FLAGS_INOBTCNT	(1 << 22) /* inobt btree counter */
 #define XFS_FSOP_GEOM_FLAGS_NREXT64	(1 << 23) /* large extent counters */
+#define XFS_FSOP_GEOM_FLAGS_PARENT	(1 << 24) /* parent pointers 	    */
 
 /*
  * Minimum and maximum sizes need for growth checks.
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index a20cade590e9..d90b05456dba 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -1187,6 +1187,8 @@ xfs_fs_geometry(
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
 	if (xfs_has_inobtcounts(mp))
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
+	if(xfs_sb_version_hasparent(sbp))
+		geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
 	if (xfs_has_sector(mp)) {
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
 		geo->logsectsize = sbp->sb_logsectsize;
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index a6e7b4176faf..cbb492fea4a5 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1655,6 +1655,10 @@ xfs_fs_fill_super(
 		xfs_warn(mp,
 	"EXPERIMENTAL Large extent counts feature in use. Use at your own risk!");
 
+	if (xfs_sb_version_hasparent(&mp->m_sb))
+		xfs_alert(mp,
+	"EXPERIMENTAL parent pointer feature enabled. Use at your own risk!");
+
 	error = xfs_mountfs(mp);
 	if (error)
 		goto out_filestream_unmount;
-- 
2.25.1


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

* [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (13 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5 Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-29 18:42   ` Darrick J. Wong
  2022-06-11  9:41 ` [PATCH v1 16/17] xfs: Increase XFS_DEFER_OPS_NR_INODES to 4 Allison Henderson
  2022-06-11  9:42 ` [PATCH v1 17/17] xfs: Add parent pointer ioctl Allison Henderson
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

This patch adds a helper function xfs_attr_list_context_init used by
xfs_attr_list. This function initializes the xfs_attr_list_context
structure passed to xfs_attr_list_int. We will need this later to call
xfs_attr_list_int_ilocked when the node is already locked.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/xfs_file.c  |  1 +
 fs/xfs/xfs_ioctl.c | 54 ++++++++++++++++++++++++++++++++--------------
 fs/xfs/xfs_ioctl.h |  2 ++
 3 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index e2f2a3a94634..884827f024fd 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -17,6 +17,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
+#include "xfs_attr.h"
 #include "xfs_ioctl.h"
 #include "xfs_trace.h"
 #include "xfs_log.h"
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 5a364a7d58fd..e1612e99e0c5 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -369,6 +369,40 @@ xfs_attr_flags(
 	return 0;
 }
 
+/*
+ * Initializes an xfs_attr_list_context suitable for
+ * use by xfs_attr_list
+ */
+int
+xfs_ioc_attr_list_context_init(
+	struct xfs_inode		*dp,
+	char				*buffer,
+	int				bufsize,
+	int				flags,
+	struct xfs_attr_list_context	*context)
+{
+	struct xfs_attrlist		*alist;
+
+	/*
+	 * Initialize the output buffer.
+	 */
+	context->dp = dp;
+	context->resynch = 1;
+	context->attr_filter = xfs_attr_filter(flags);
+	context->buffer = buffer;
+	context->bufsize = round_down(bufsize, sizeof(uint32_t));
+	context->firstu = context->bufsize;
+	context->put_listent = xfs_ioc_attr_put_listent;
+
+	alist = context->buffer;
+	alist->al_count = 0;
+	alist->al_more = 0;
+	alist->al_offset[0] = context->bufsize;
+
+	return 0;
+}
+
+
 int
 xfs_ioc_attr_list(
 	struct xfs_inode		*dp,
@@ -378,7 +412,6 @@ xfs_ioc_attr_list(
 	struct xfs_attrlist_cursor __user *ucursor)
 {
 	struct xfs_attr_list_context	context = { };
-	struct xfs_attrlist		*alist;
 	void				*buffer;
 	int				error;
 
@@ -410,21 +443,10 @@ xfs_ioc_attr_list(
 	if (!buffer)
 		return -ENOMEM;
 
-	/*
-	 * Initialize the output buffer.
-	 */
-	context.dp = dp;
-	context.resynch = 1;
-	context.attr_filter = xfs_attr_filter(flags);
-	context.buffer = buffer;
-	context.bufsize = round_down(bufsize, sizeof(uint32_t));
-	context.firstu = context.bufsize;
-	context.put_listent = xfs_ioc_attr_put_listent;
-
-	alist = context.buffer;
-	alist->al_count = 0;
-	alist->al_more = 0;
-	alist->al_offset[0] = context.bufsize;
+	error = xfs_ioc_attr_list_context_init(dp, buffer, bufsize, flags,
+			&context);
+	if (error)
+		return error;
 
 	error = xfs_attr_list(&context);
 	if (error)
diff --git a/fs/xfs/xfs_ioctl.h b/fs/xfs/xfs_ioctl.h
index d4abba2c13c1..ca60e1c427a3 100644
--- a/fs/xfs/xfs_ioctl.h
+++ b/fs/xfs/xfs_ioctl.h
@@ -35,6 +35,8 @@ int xfs_ioc_attrmulti_one(struct file *parfilp, struct inode *inode,
 int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
 		      size_t bufsize, int flags,
 		      struct xfs_attrlist_cursor __user *ucursor);
+int xfs_ioc_attr_list_context_init(struct xfs_inode *dp, char *buffer,
+		int bufsize, int flags, struct xfs_attr_list_context *context);
 
 extern struct dentry *
 xfs_handle_to_dentry(
-- 
2.25.1


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

* [PATCH v1 16/17] xfs: Increase  XFS_DEFER_OPS_NR_INODES to 4
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (14 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init Allison Henderson
@ 2022-06-11  9:41 ` Allison Henderson
  2022-06-16 21:54   ` Dave Chinner
  2022-06-11  9:42 ` [PATCH v1 17/17] xfs: Add parent pointer ioctl Allison Henderson
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:41 UTC (permalink / raw)
  To: linux-xfs

Renames that generate parent pointer updates will need to 2 extra defer
operations. One for the rmap update and another for the parent pointer
update

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/libxfs/xfs_defer.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
index 114a3a4930a3..0c2a6e537016 100644
--- a/fs/xfs/libxfs/xfs_defer.h
+++ b/fs/xfs/libxfs/xfs_defer.h
@@ -70,7 +70,7 @@ extern const struct xfs_defer_op_type xfs_attr_defer_type;
 /*
  * Deferred operation item relogging limits.
  */
-#define XFS_DEFER_OPS_NR_INODES	2	/* join up to two inodes */
+#define XFS_DEFER_OPS_NR_INODES	4	/* join up to four inodes */
 #define XFS_DEFER_OPS_NR_BUFS	2	/* join up to two buffers */
 
 /* Resources that must be held across a transaction roll. */
-- 
2.25.1


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

* [PATCH v1 17/17] xfs: Add parent pointer ioctl
  2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
                   ` (15 preceding siblings ...)
  2022-06-11  9:41 ` [PATCH v1 16/17] xfs: Increase XFS_DEFER_OPS_NR_INODES to 4 Allison Henderson
@ 2022-06-11  9:42 ` Allison Henderson
  2022-06-29 18:52   ` Darrick J. Wong
  16 siblings, 1 reply; 58+ messages in thread
From: Allison Henderson @ 2022-06-11  9:42 UTC (permalink / raw)
  To: linux-xfs

This patch adds a new file ioctl to retrieve the parent pointer of a
given inode

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
 fs/xfs/Makefile            |   1 +
 fs/xfs/libxfs/xfs_fs.h     |  46 +++++++++++++
 fs/xfs/libxfs/xfs_parent.c |  10 +++
 fs/xfs/libxfs/xfs_parent.h |   2 +
 fs/xfs/xfs_ioctl.c         |  90 ++++++++++++++++++++++++-
 fs/xfs/xfs_ondisk.h        |   4 ++
 fs/xfs/xfs_parent_utils.c  | 133 +++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_parent_utils.h  |  22 ++++++
 8 files changed, 306 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index fc717dc3470c..da86f6231f2e 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -86,6 +86,7 @@ xfs-y				+= xfs_aops.o \
 				   xfs_mount.o \
 				   xfs_mru_cache.o \
 				   xfs_pwork.o \
+				   xfs_parent_utils.o \
 				   xfs_reflink.o \
 				   xfs_stats.o \
 				   xfs_super.o \
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index b0b4d7a3aa15..e6c8873cd234 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -574,6 +574,7 @@ typedef struct xfs_fsop_handlereq {
 #define XFS_IOC_ATTR_SECURE	0x0008	/* use attrs in security namespace */
 #define XFS_IOC_ATTR_CREATE	0x0010	/* fail if attr already exists */
 #define XFS_IOC_ATTR_REPLACE	0x0020	/* fail if attr does not exist */
+#define XFS_IOC_ATTR_PARENT	0x0040  /* use attrs in parent namespace */
 
 typedef struct xfs_attrlist_cursor {
 	__u32		opaque[4];
@@ -752,6 +753,50 @@ struct xfs_scrub_metadata {
 				 XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
 #define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
 
+#define XFS_PPTR_MAXNAMELEN				256
+
+/* return parents of the handle, not the open fd */
+#define XFS_PPTR_IFLAG_HANDLE  (1U << 0)
+
+/* target was the root directory */
+#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
+
+/* Cursor is done iterating pptrs */
+#define XFS_PPTR_OFLAG_DONE    (1U << 2)
+
+/* Get an inode parent pointer through ioctl */
+struct xfs_parent_ptr {
+	__u64		xpp_ino;			/* Inode */
+	__u32		xpp_gen;			/* Inode generation */
+	__u32		xpp_diroffset;			/* Directory offset */
+	__u32		xpp_namelen;			/* File name length */
+	__u32		xpp_pad;
+	__u8		xpp_name[XFS_PPTR_MAXNAMELEN];	/* File name */
+};
+
+/* Iterate through an inodes parent pointers */
+struct xfs_pptr_info {
+	struct xfs_handle		pi_handle;
+	struct xfs_attrlist_cursor	pi_cursor;
+	__u32				pi_flags;
+	__u32				pi_reserved;
+	__u32				pi_ptrs_size;
+	__u32				pi_ptrs_used;
+	__u64				pi_reserved2[6];
+
+	/*
+	 * An array of struct xfs_parent_ptr follows the header
+	 * information. Use XFS_PPINFO_TO_PP() to access the
+	 * parent pointer array entries.
+	 */
+};
+
+#define XFS_PPTR_INFO_SIZEOF(nr_ptrs) sizeof (struct xfs_pptr_info) + \
+				      nr_ptrs * sizeof(struct xfs_parent_ptr)
+
+#define XFS_PPINFO_TO_PP(info, idx)    \
+	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))
+
 /*
  * ioctl limits
  */
@@ -797,6 +842,7 @@ struct xfs_scrub_metadata {
 /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
 #define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
 #define XFS_IOC_AG_GEOMETRY	_IOWR('X', 61, struct xfs_ag_geometry)
+#define XFS_IOC_GETPPOINTER	_IOR ('X', 62, struct xfs_parent_ptr)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index cb546652bde9..a5b99f30bc63 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -33,6 +33,16 @@
 #include "xfs_attr_sf.h"
 #include "xfs_bmap.h"
 
+/* Initializes a xfs_parent_ptr from an xfs_parent_name_rec */
+void
+xfs_init_parent_ptr(struct xfs_parent_ptr		*xpp,
+		     struct xfs_parent_name_rec	*rec)
+{
+	xpp->xpp_ino = be64_to_cpu(rec->p_ino);
+	xpp->xpp_gen = be32_to_cpu(rec->p_gen);
+	xpp->xpp_diroffset = be32_to_cpu(rec->p_diroffset);
+}
+
 /*
  * Parent pointer attribute handling.
  *
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index 10dc576ce693..fa50ada0d6a9 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -28,4 +28,6 @@ void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
 			      uint32_t p_diroffset);
 void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
 			       struct xfs_parent_name_rec *rec);
+void xfs_init_parent_ptr(struct xfs_parent_ptr *xpp,
+			 struct xfs_parent_name_rec *rec);
 #endif	/* __XFS_PARENT_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index e1612e99e0c5..4cd1de3e9d0b 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -37,6 +37,7 @@
 #include "xfs_health.h"
 #include "xfs_reflink.h"
 #include "xfs_ioctl.h"
+#include "xfs_parent_utils.h"
 #include "xfs_xattr.h"
 
 #include <linux/mount.h>
@@ -355,6 +356,8 @@ xfs_attr_filter(
 		return XFS_ATTR_ROOT;
 	if (ioc_flags & XFS_IOC_ATTR_SECURE)
 		return XFS_ATTR_SECURE;
+	if (ioc_flags & XFS_IOC_ATTR_PARENT)
+		return XFS_ATTR_PARENT;
 	return 0;
 }
 
@@ -422,7 +425,8 @@ xfs_ioc_attr_list(
 	/*
 	 * Reject flags, only allow namespaces.
 	 */
-	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
+	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE |
+		      XFS_IOC_ATTR_PARENT))
 		return -EINVAL;
 	if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
 		return -EINVAL;
@@ -1672,6 +1676,87 @@ xfs_ioc_scrub_metadata(
 	return 0;
 }
 
+/*
+ * IOCTL routine to get the parent pointers of an inode and return it to user
+ * space.  Caller must pass a buffer space containing a struct xfs_pptr_info,
+ * followed by a region large enough to contain an array of struct
+ * xfs_parent_ptr of a size specified in pi_ptrs_size.  If the inode contains
+ * more parent pointers than can fit in the buffer space, caller may re-call
+ * the function using the returned pi_cursor to resume iteration.  The
+ * number of xfs_parent_ptr returned will be stored in pi_ptrs_used.
+ *
+ * Returns 0 on success or non-zero on failure
+ */
+STATIC int
+xfs_ioc_get_parent_pointer(
+	struct file			*filp,
+	void				__user *arg)
+{
+	struct xfs_pptr_info		*ppi = NULL;
+	int				error = 0;
+	struct xfs_inode		*ip = XFS_I(file_inode(filp));
+	struct xfs_mount		*mp = ip->i_mount;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	/* Allocate an xfs_pptr_info to put the user data */
+	ppi = kmem_alloc(sizeof(struct xfs_pptr_info), 0);
+	if (!ppi)
+		return -ENOMEM;
+
+	/* Copy the data from the user */
+	error = copy_from_user(ppi, arg, sizeof(struct xfs_pptr_info));
+	if (error)
+		goto out;
+
+	/* Check size of buffer requested by user */
+	if (XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size) > XFS_XATTR_LIST_MAX) {
+		error = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Now that we know how big the trailing buffer is, expand
+	 * our kernel xfs_pptr_info to be the same size
+	 */
+	ppi = krealloc(ppi, XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size),
+		       GFP_NOFS | __GFP_NOFAIL);
+	if (!ppi)
+		return -ENOMEM;
+
+	if (ppi->pi_flags != 0 && ppi->pi_flags != XFS_PPTR_IFLAG_HANDLE) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	if (ppi->pi_flags == XFS_PPTR_IFLAG_HANDLE) {
+		error = xfs_iget(mp, NULL, ppi->pi_handle.ha_fid.fid_ino,
+				0, 0, &ip);
+		if (error)
+			goto out;
+	}
+
+	if (ip->i_ino == mp->m_sb.sb_rootino)
+		ppi->pi_flags |= XFS_PPTR_OFLAG_ROOT;
+
+	/* Get the parent pointers */
+	error = xfs_attr_get_parent_pointer(ip, ppi);
+
+	if (error)
+		goto out;
+
+	/* Copy the parent pointers back to the user */
+	error = copy_to_user(arg, ppi,
+			XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size));
+	if (error)
+		goto out;
+
+out:
+	kmem_free(ppi);
+	return error;
+}
+
 int
 xfs_ioc_swapext(
 	xfs_swapext_t	*sxp)
@@ -1961,7 +2046,8 @@ xfs_file_ioctl(
 
 	case XFS_IOC_FSGETXATTRA:
 		return xfs_ioc_fsgetxattra(ip, arg);
-
+	case XFS_IOC_GETPPOINTER:
+		return xfs_ioc_get_parent_pointer(filp, arg);
 	case XFS_IOC_GETBMAP:
 	case XFS_IOC_GETBMAPA:
 	case XFS_IOC_GETBMAPX:
diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
index 758702b9495f..765eb514a917 100644
--- a/fs/xfs/xfs_ondisk.h
+++ b/fs/xfs/xfs_ondisk.h
@@ -135,6 +135,10 @@ xfs_check_ondisk_structs(void)
 	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
 
+	/* parent pointer ioctls */
+	XFS_CHECK_STRUCT_SIZE(struct xfs_parent_ptr,            280);
+	XFS_CHECK_STRUCT_SIZE(struct xfs_pptr_info,             104);
+
 	/*
 	 * The v5 superblock format extended several v4 header structures with
 	 * additional data. While new fields are only accessible on v5
diff --git a/fs/xfs/xfs_parent_utils.c b/fs/xfs/xfs_parent_utils.c
new file mode 100644
index 000000000000..9880718395c6
--- /dev/null
+++ b/fs/xfs/xfs_parent_utils.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_inode.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_trans.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_ioctl.h"
+#include "xfs_parent.h"
+#include "xfs_da_btree.h"
+
+/*
+ * Get the parent pointers for a given inode
+ *
+ * Returns 0 on success and non zero on error
+ */
+int
+xfs_attr_get_parent_pointer(struct xfs_inode		*ip,
+			    struct xfs_pptr_info	*ppi)
+
+{
+
+	struct xfs_attrlist		*alist;
+	struct xfs_attrlist_ent		*aent;
+	struct xfs_parent_ptr		*xpp;
+	struct xfs_parent_name_rec	*xpnr;
+	char				*namebuf;
+	unsigned int			namebuf_size;
+	int				name_len;
+	int				error = 0;
+	unsigned int			ioc_flags = XFS_IOC_ATTR_PARENT;
+	unsigned int			flags = XFS_ATTR_PARENT;
+	int				i;
+	struct xfs_attr_list_context	context;
+	struct xfs_da_args		args;
+
+	/* Allocate a buffer to store the attribute names */
+	namebuf_size = sizeof(struct xfs_attrlist) +
+		       (ppi->pi_ptrs_size) * sizeof(struct xfs_attrlist_ent);
+	namebuf = kvzalloc(namebuf_size, GFP_KERNEL);
+	if (!namebuf)
+		return -ENOMEM;
+
+	memset(&context, 0, sizeof(struct xfs_attr_list_context));
+	error = xfs_ioc_attr_list_context_init(ip, namebuf, namebuf_size,
+			ioc_flags, &context);
+
+	/* Copy the cursor provided by caller */
+	memcpy(&context.cursor, &ppi->pi_cursor,
+	       sizeof(struct xfs_attrlist_cursor));
+
+	if (error)
+		goto out_kfree;
+
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+	error = xfs_attr_list_ilocked(&context);
+	if (error)
+		goto out_kfree;
+
+	alist = (struct xfs_attrlist *)namebuf;
+	for (i = 0; i < alist->al_count; i++) {
+		xpp = XFS_PPINFO_TO_PP(ppi, i);
+		memset(xpp, 0, sizeof(struct xfs_parent_ptr));
+		aent = (struct xfs_attrlist_ent *)
+			&namebuf[alist->al_offset[i]];
+		xpnr = (struct xfs_parent_name_rec *)(aent->a_name);
+
+		if (aent->a_valuelen > XFS_PPTR_MAXNAMELEN) {
+			error = -ERANGE;
+			goto out_kfree;
+		}
+		name_len = aent->a_valuelen;
+
+		memset(&args, 0, sizeof(args));
+		args.geo = ip->i_mount->m_attr_geo;
+		args.whichfork = XFS_ATTR_FORK;
+		args.dp = ip;
+		args.name = (char *)xpnr;
+		args.namelen = sizeof(struct xfs_parent_name_rec);
+		args.attr_filter = flags;
+		args.hashval = xfs_da_hashname(args.name, args.namelen);
+		args.value = (unsigned char *)(xpp->xpp_name);
+		args.valuelen = name_len;
+		args.op_flags = XFS_DA_OP_OKNOENT;
+
+		error = xfs_attr_get_ilocked(&args);
+		error = (error == -EEXIST ? 0 : error);
+		if (error)
+			goto out_kfree;
+
+		xpp->xpp_namelen = name_len;
+		xfs_init_parent_ptr(xpp, xpnr);
+	}
+	ppi->pi_ptrs_used = alist->al_count;
+	if (!alist->al_more)
+		ppi->pi_flags |= XFS_PPTR_OFLAG_DONE;
+
+	/* Update the caller with the current cursor position */
+	memcpy(&ppi->pi_cursor, &context.cursor,
+		sizeof(struct xfs_attrlist_cursor));
+
+out_kfree:
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	kmem_free(namebuf);
+
+	return error;
+}
+
diff --git a/fs/xfs/xfs_parent_utils.h b/fs/xfs/xfs_parent_utils.h
new file mode 100644
index 000000000000..0e952b2ebd4a
--- /dev/null
+++ b/fs/xfs/xfs_parent_utils.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017 Oracle, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation Inc.
+ */
+#ifndef	__XFS_PARENT_UTILS_H__
+#define	__XFS_PARENT_UTILS_H__
+
+int xfs_attr_get_parent_pointer(struct xfs_inode *ip,
+				struct xfs_pptr_info *ppi);
+#endif	/* __XFS_PARENT_UTILS_H__ */
-- 
2.25.1


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

* Re: [PATCH v1 10/17] xfs: parent pointer attribute creation
  2022-06-11  9:41 ` [PATCH v1 10/17] xfs: parent pointer attribute creation Allison Henderson
@ 2022-06-11 15:10   ` kernel test robot
  2022-06-16  5:49   ` Dave Chinner
  2022-06-29 18:41   ` Darrick J. Wong
  2 siblings, 0 replies; 58+ messages in thread
From: kernel test robot @ 2022-06-11 15:10 UTC (permalink / raw)
  To: Allison Henderson; +Cc: llvm, kbuild-all

Hi Allison,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on xfs-linux/for-next]
[also build test WARNING on v5.19-rc1 next-20220610]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Allison-Henderson/Return-of-the-Parent-Pointers/20220611-174444
base:   https://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git for-next
config: x86_64-randconfig-a005 (https://download.01.org/0day-ci/archive/20220611/202206112311.zPVsFae7-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project ff4abe755279a3a47cc416ef80dbc900d9a98a19)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/f78cf1e945fc44e0022add251db2819f37b8d2ff
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Allison-Henderson/Return-of-the-Parent-Pointers/20220611-174444
        git checkout f78cf1e945fc44e0022add251db2819f37b8d2ff
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash fs/xfs/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/xfs/libxfs/xfs_parent.c:55:1: warning: no previous prototype for function 'xfs_init_parent_name_rec' [-Wmissing-prototypes]
   xfs_init_parent_name_rec(
   ^
   fs/xfs/libxfs/xfs_parent.c:54:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   void
   ^
   static 
>> fs/xfs/libxfs/xfs_parent.c:70:1: warning: no previous prototype for function 'xfs_init_parent_name_irec' [-Wmissing-prototypes]
   xfs_init_parent_name_irec(
   ^
   fs/xfs/libxfs/xfs_parent.c:69:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   void
   ^
   static 
   2 warnings generated.


vim +/xfs_init_parent_name_rec +55 fs/xfs/libxfs/xfs_parent.c

    35	
    36	/*
    37	 * Parent pointer attribute handling.
    38	 *
    39	 * Because the attribute value is a filename component, it will never be longer
    40	 * than 255 bytes. This means the attribute will always be a local format
    41	 * attribute as it is xfs_attr_leaf_entsize_local_max() for v5 filesystems will
    42	 * always be larger than this (max is 75% of block size).
    43	 *
    44	 * Creating a new parent attribute will always create a new attribute - there
    45	 * should never, ever be an existing attribute in the tree for a new inode.
    46	 * ENOSPC behavior is problematic - creating the inode without the parent
    47	 * pointer is effectively a corruption, so we allow parent attribute creation
    48	 * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
    49	 * occurring.
    50	 */
    51	
    52	
    53	/* Initializes a xfs_parent_name_rec to be stored as an attribute name */
    54	void
  > 55	xfs_init_parent_name_rec(
    56		struct xfs_parent_name_rec	*rec,
    57		struct xfs_inode		*ip,
    58		uint32_t			p_diroffset)
    59	{
    60		xfs_ino_t			p_ino = ip->i_ino;
    61		uint32_t			p_gen = VFS_I(ip)->i_generation;
    62	
    63		rec->p_ino = cpu_to_be64(p_ino);
    64		rec->p_gen = cpu_to_be32(p_gen);
    65		rec->p_diroffset = cpu_to_be32(p_diroffset);
    66	}
    67	
    68	/* Initializes a xfs_parent_name_irec from an xfs_parent_name_rec */
    69	void
  > 70	xfs_init_parent_name_irec(

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

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

* Re: [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-11  9:41 ` [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK Allison Henderson
@ 2022-06-15  1:09   ` Dave Chinner
  2022-06-15 23:40     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-15  1:09 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:44AM -0700, Allison Henderson wrote:
> Recent parent pointer testing has exposed a bug in the underlying
> larp state machine.  A replace operation may remove an old attr
> before adding the new one, but if it is the only attr in the fork,
> then the fork is removed.  This later causes a null pointer in
> xfs_attr_try_sf_addname which expects the fork present.  This
> patch adds an extra state to create the fork.

Hmmmm.

I thought I fixed those problems - in xfs_attr_sf_removename() there
is this code:

        if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp) &&
            (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
            !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE))) {
                xfs_attr_fork_remove(dp, args->trans);

A replace operation will have XFS_DA_OP_REPLACE set, and so the
final remove from a sf directory will not remove the attr fork in
this case. There is equivalent checks in the leaf/node remove name
paths to avoid removing the attr fork if the last attr is removed
while the attr fork is in those formats.

How do you reproduce this issue?

> Additionally the new state will be used by parent pointers which
> need to add attributes to newly created inodes that do not yet
> have a fork.

We already have the capability of doing that in xfs_init_new_inode()
by passing in init_xattrs == true. So when we are creating a new
inode with parent pointers enabled, we know that we are going to be
creating an xattr on the inode and so we should always set
init_xattrs in that case.

This should avoid the need for parent pointers to ever need to run
an extra transaction to create the attr fork. Hence, AFAICT, this
new state to handle attr fork creation shouldn't ever be needed for
parent pointers....

What am I missing?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-15  1:09   ` Dave Chinner
@ 2022-06-15 23:40     ` Alli
  2022-06-16  2:08       ` Dave Chinner
  0 siblings, 1 reply; 58+ messages in thread
From: Alli @ 2022-06-15 23:40 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Wed, 2022-06-15 at 11:09 +1000, Dave Chinner wrote:
> On Sat, Jun 11, 2022 at 02:41:44AM -0700, Allison Henderson wrote:
> > Recent parent pointer testing has exposed a bug in the underlying
> > larp state machine.  A replace operation may remove an old attr
> > before adding the new one, but if it is the only attr in the fork,
> > then the fork is removed.  This later causes a null pointer in
> > xfs_attr_try_sf_addname which expects the fork present.  This
> > patch adds an extra state to create the fork.
> 
> Hmmmm.
> 
> I thought I fixed those problems - in xfs_attr_sf_removename() there
> is this code:
> 
>         if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp)
> &&
>             (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
>             !(args->op_flags & (XFS_DA_OP_ADDNAME |
> XFS_DA_OP_REPLACE))) {
>                 xfs_attr_fork_remove(dp, args->trans);
Hmm, ok, let me shuffle in some traces around there to see where things
fall off the rails

> 
> A replace operation will have XFS_DA_OP_REPLACE set, and so the
> final remove from a sf directory will not remove the attr fork in
> this case. There is equivalent checks in the leaf/node remove name
> paths to avoid removing the attr fork if the last attr is removed
> while the attr fork is in those formats.
> 
> How do you reproduce this issue?
> 

Sure, you can apply this kernel set or download it here:
https://github.com/allisonhenderson/xfs/tree/xfs_new_pptrs

Next you'll need this xfsprogs that has the neccassary updates to run
parent pointers
https://github.com/allisonhenderson/xfsprogs/tree/xfsprogs_new_pptrs


To reproduce the bug, you'll need to apply a quick patch on the kernel
side:
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index b86188b63897..f279afd43462 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -741,8 +741,8 @@ xfs_attr_set_iter(
 		fallthrough;
 	case XFS_DAS_SF_ADD:
 		if (!args->dp->i_afp) {
-			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
-			goto next_state;
+//			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
+//			goto next_state;
 		}
 		return xfs_attr_sf_addname(attr);
 	case XFS_DAS_LEAF_ADD:



Lastly, you'll need Catherines parent pointer tests that she sent out a
day or so ago.  Once you have that, just run the parent pointers test:
echo 1 > /sys/fs/xfs/debug/larp; ./check xfs/549

Dmesg below:


==================================================================
[  365.288788] BUG: KASAN: null-ptr-deref in
xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
[  365.289170] Read of size 1 at addr 000000000000002a by task
mount/23669

[  365.289182] CPU: 10 PID: 23669 Comm: mount Tainted:
G            E     5.18.0-rc2 #84
[  365.289196] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS
VirtualBox 12/01/2006
[  365.289202] Call Trace:
[  365.289206]  <TASK>
[  365.289211]  dump_stack_lvl+0x49/0x5f
[  365.289232]  print_report.cold+0x494/0x6b4
[  365.289242]  ? path_mount+0x641/0xfd0
[  365.289258]  ? __x64_sys_mount+0xca/0x110
[  365.289265]  ? do_syscall_64+0x3b/0x90
[  365.289274]  ? xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
[  365.289649]  kasan_report+0xa7/0x120
[  365.289660]  ? xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
[  365.290034]  __asan_load1+0x6a/0x70
[  365.290048]  xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
[  365.290423]  xfs_attr_set_iter+0x2f9/0x1510 [xfs]
[  365.290801]  ? xfs_init_attr_trans+0x130/0x130 [xfs]
[  365.291178]  ? kasan_poison+0x3c/0x50
[  365.291187]  ? kasan_unpoison+0x28/0x50
[  365.291197]  ? xfs_errortag_test+0x57/0x120 [xfs]
[  365.291592]  xfs_xattri_finish_update+0x66/0xd0 [xfs]
[  365.292008]  xfs_attr_finish_item+0x43/0x120 [xfs]
[  365.292410]  xfs_defer_finish_noroll+0x3c2/0xcc0 [xfs]
[  365.292804]  ? xfs_defer_cancel+0xc0/0xc0 [xfs]
[  365.293184]  ? kasan_quarantine_put+0x57/0x180
[  365.293196]  __xfs_trans_commit+0x333/0x610 [xfs]
[  365.293599]  ? xfs_trans_free_items+0x150/0x150 [xfs]
[  365.293995]  ? kvfree+0x28/0x30
[  365.294004]  ? kvfree+0x28/0x30
[  365.294017]  ? xfs_defer_ops_continue+0x1c5/0x280 [xfs]
[  365.294401]  xfs_trans_commit+0x10/0x20 [xfs]
[  365.294797]  xlog_finish_defer_ops+0x133/0x270 [xfs]
[  365.295203]  ? xlog_recover_free_trans+0x1c0/0x1c0 [xfs]
[  365.295609]  ? xfs_attr_finish_item+0x120/0x120 [xfs]
[  365.296036]  ? _raw_spin_lock+0x88/0xd7
[  365.296044]  ? _raw_spin_lock_irqsave+0xf0/0xf0
[  365.296054]  xlog_recover_process_intents+0x1f7/0x3e0 [xfs]
[  365.296469]  ? xlog_do_recover+0x290/0x290 [xfs]
[  365.296757]  ? __queue_delayed_work+0xdc/0x140
[  365.296766]  xlog_recover_finish+0x18/0x150 [xfs]
[  365.296949]  xfs_log_mount_finish+0x194/0x310 [xfs]
[  365.297132]  xfs_mountfs+0x957/0xeb0 [xfs]
[  365.297313]  ? xfs_mount_reset_sbqflags+0xa0/0xa0 [xfs]
[  365.297494]  ? xfs_filestream_put_ag+0x40/0x40 [xfs]
[  365.297674]  ? xfs_mru_cache_create+0x226/0x280 [xfs]
[  365.297855]  xfs_fs_fill_super+0x7f0/0xd20 [xfs]
[  365.298034]  get_tree_bdev+0x22e/0x360
[  365.298041]  ? xfs_fs_sync_fs+0x150/0x150 [xfs]
[  365.298223]  xfs_fs_get_tree+0x15/0x20 [xfs]
[  365.298401]  vfs_get_tree+0x4c/0x120
[  365.298408]  path_mount+0x641/0xfd0
[  365.298411]  ? putname+0x7c/0x90
[  365.298416]  ? finish_automount+0x2e0/0x2e0
[  365.298419]  ? kmem_cache_free+0x104/0x4d0
[  365.298422]  ? putname+0x7c/0x90
[  365.298426]  ? putname+0x7c/0x90
[  365.298430]  do_mount+0xd2/0xf0
[  365.298433]  ? path_mount+0xfd0/0xfd0
[  365.298436]  ? memdup_user+0x52/0x90
[  365.298440]  __x64_sys_mount+0xca/0x110
[  365.298444]  do_syscall_64+0x3b/0x90
[  365.298448]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[  365.298451] RIP: 0033:0x7f7e4e213cae
[  365.298455] Code: 48 8b 0d e5 c1 0c 00 f7 d8 64 89 01 48 83 c8 ff c3
66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 a5 00 00 00 0f
05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d b2 c1 0c 00 f7 d8 64 89 01 48
[  365.298459] RSP: 002b:00007ffee6811418 EFLAGS: 00000246 ORIG_RAX:
00000000000000a5
[  365.298467] RAX: ffffffffffffffda RBX: 00007f7e4e345204 RCX:
00007f7e4e213cae
[  365.298470] RDX: 000056006007db70 RSI: 000056006007dbb0 RDI:
000056006007db90
[  365.298472] RBP: 000056006007d960 R08: 0000000000000000 R09:
00007ffee6810190
[  365.298475] R10: 0000000000000000 R11: 0000000000000246 R12:
0000000000000000
[  365.298477] R13: 000056006007db90 R14: 000056006007db70 R15:
000056006007d960




> > Additionally the new state will be used by parent pointers which
> > need to add attributes to newly created inodes that do not yet
> > have a fork.
> 
> We already have the capability of doing that in xfs_init_new_inode()
> by passing in init_xattrs == true. So when we are creating a new
> inode with parent pointers enabled, we know that we are going to be
> creating an xattr on the inode and so we should always set
> init_xattrs in that case.
Hmm, ok.  I'll add some tracing around in there too, if I back out the
entire first patch, we crash out earlier in recovery path because no
state is set.  If we enter xfs_attri_item_recover with no fork, we end
up in the following switch:


        case
XFS_ATTRI_OP_FLAGS_REPLACE:                                        
                args->value = nv-
>value.i_addr;                                 
                args->valuelen = nv-
>value.i_len;                               
                args->total = xfs_attr_calc_size(args,
&local);                 
                if (xfs_inode_hasattr(args-
>dp))                                
                        attr->xattri_dela_state =
xfs_attr_init_replace_state(args);
                else                                                   
         
                        attr->xattri_dela_state =
xfs_attr_init_add_state(args);
                break;     

Which will leave the state unset if the fork is absent.
> 
> This should avoid the need for parent pointers to ever need to run
> an extra transaction to create the attr fork. Hence, AFAICT, this
> new state to handle attr fork creation shouldn't ever be needed for
> parent pointers....
> 
> What am I missing?
> 
I hope the description helped?  I'll do some more poking around too and
post back if I find anything else.

Allison

> Cheers,
> 
> Dave.


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

* Re: [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-15 23:40     ` Alli
@ 2022-06-16  2:08       ` Dave Chinner
  2022-06-16  5:32         ` Dave Chinner
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16  2:08 UTC (permalink / raw)
  To: Alli; +Cc: linux-xfs

On Wed, Jun 15, 2022 at 04:40:07PM -0700, Alli wrote:
> On Wed, 2022-06-15 at 11:09 +1000, Dave Chinner wrote:
> > On Sat, Jun 11, 2022 at 02:41:44AM -0700, Allison Henderson wrote:
> > > Recent parent pointer testing has exposed a bug in the underlying
> > > larp state machine.  A replace operation may remove an old attr
> > > before adding the new one, but if it is the only attr in the fork,
> > > then the fork is removed.  This later causes a null pointer in
> > > xfs_attr_try_sf_addname which expects the fork present.  This
> > > patch adds an extra state to create the fork.
> > 
> > Hmmmm.
> > 
> > I thought I fixed those problems - in xfs_attr_sf_removename() there
> > is this code:
> > 
> >         if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp)
> > &&
> >             (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
> >             !(args->op_flags & (XFS_DA_OP_ADDNAME |
> > XFS_DA_OP_REPLACE))) {
> >                 xfs_attr_fork_remove(dp, args->trans);
> Hmm, ok, let me shuffle in some traces around there to see where things
> fall off the rails
> 
> > 
> > A replace operation will have XFS_DA_OP_REPLACE set, and so the
> > final remove from a sf directory will not remove the attr fork in
> > this case. There is equivalent checks in the leaf/node remove name
> > paths to avoid removing the attr fork if the last attr is removed
> > while the attr fork is in those formats.
> > 
> > How do you reproduce this issue?
> > 
> 
> Sure, you can apply this kernel set or download it here:
> https://github.com/allisonhenderson/xfs/tree/xfs_new_pptrs
> 
> Next you'll need this xfsprogs that has the neccassary updates to run
> parent pointers
> https://github.com/allisonhenderson/xfsprogs/tree/xfsprogs_new_pptrs
> 
> 
> To reproduce the bug, you'll need to apply a quick patch on the kernel
> side:
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index b86188b63897..f279afd43462 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -741,8 +741,8 @@ xfs_attr_set_iter(
>  		fallthrough;
>  	case XFS_DAS_SF_ADD:
>  		if (!args->dp->i_afp) {
> -			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> -			goto next_state;
> +//			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> +//			goto next_state;
>  		}

Ah, so it's recovery that trips this....

> [  365.290048]  xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
> [  365.290423]  xfs_attr_set_iter+0x2f9/0x1510 [xfs]
> [  365.291592]  xfs_xattri_finish_update+0x66/0xd0 [xfs]
> [  365.292008]  xfs_attr_finish_item+0x43/0x120 [xfs]
> [  365.292410]  xfs_defer_finish_noroll+0x3c2/0xcc0 [xfs]
> [  365.293196]  __xfs_trans_commit+0x333/0x610 [xfs]
> [  365.294401]  xfs_trans_commit+0x10/0x20 [xfs]
> [  365.294797]  xlog_finish_defer_ops+0x133/0x270 [xfs]
> [  365.296054]  xlog_recover_process_intents+0x1f7/0x3e0 [xfs]

ayup.

> > > Additionally the new state will be used by parent pointers which
> > > need to add attributes to newly created inodes that do not yet
> > > have a fork.
> > 
> > We already have the capability of doing that in xfs_init_new_inode()
> > by passing in init_xattrs == true. So when we are creating a new
> > inode with parent pointers enabled, we know that we are going to be
> > creating an xattr on the inode and so we should always set
> > init_xattrs in that case.
> Hmm, ok.  I'll add some tracing around in there too, if I back out the
> entire first patch, we crash out earlier in recovery path because no
> state is set.  If we enter xfs_attri_item_recover with no fork, we end
> up in the following switch:
> 
> 
>         case XFS_ATTRI_OP_FLAGS_REPLACE:
>                 args->value = nv- >value.i_addr;
>                 args->valuelen = nv- >value.i_len;
>                 args->total = xfs_attr_calc_size(args, &local);
>                 if (xfs_inode_hasattr(args- >dp))
>                         attr->xattri_dela_state = xfs_attr_init_replace_state(args);
>                 else
>                         attr->xattri_dela_state = xfs_attr_init_add_state(args);
>                 break;
> 
> Which will leave the state unset if the fork is absent.

Yeah, OK, I think this is because we are combining attribute
creation with inode creation. When log recovery replays inode core
modifications, it replays the inode state into the cluster buffer
and writes it. Then when we go to replay the attr intent at the end
of recovery, the inode is read from disk via xlog_recover_iget(),
but we don't initialise the attr fork because ip->i_forkoff is zero.
i.e. it has no attrs at this point.

I suspect that we could catch that in xlog_recover_iget() when it is
called from attr recovery. i.e. we detect newly created inodes and
initialise the attr fork similar to what we do in
xfs_init_new_inode(). I was thinking something like this:

	if (init_xattrs && xfs_has_attr(mp)) {
		if (!ip->i_forkoff && !ip->i_nextents) {
			ip->i_forkoff = xfs_default_attroffset(ip) >> 3;
			ip->i_afp = xfs_ifork_alloc(XFS_DINODE_FMT_EXTENTS, 0);
		} else {
			ASSERT(ip->i_afp);
		}
	}

Would do the trick, but then I realised that the timing/ordering is
very different to runtime: we don't replay the attr intent until the
end of log recovery and all the inode changes have been replayed
into the inode cluster buffer. That means we could have already
replayed a bunch of data fork extent modifications into the inode,
and so the default attr offset is almost certainly not a safe thing
to be using here. Indeed, there might not be space in the inode for
the attr we want to insert and so we might need to convert the data
fork to a different format before we run the attr intent replay.

That does indeed take us down the path of needing to run a full attr
fork creation operation, because we aren't creating the parent attr
when the data fork is empty in recovery. i.e. recovery is changing
the temporal order of data fork vs attr operations because intent
recovery uses an eventual consistency model rather than the
immediate consistency model that runtime uses.

Hmmmm. I'm going to have to have a think about the implications of
that relevation. I think it does mean we need recovery to be able to
run an attr fork initialisation, but I suspect it also means that
the runtime fork initialisation might also need to include it, too.

I'll need to think on this a bit.

> > This should avoid the need for parent pointers to ever need to run
> > an extra transaction to create the attr fork. Hence, AFAICT, this
> > new state to handle attr fork creation shouldn't ever be needed for
> > parent pointers....
> > 
> > What am I missing?
> > 
> I hope the description helped?  I'll do some more poking around too and
> post back if I find anything else.

Yup, it most definitely helped. :)

You've pointed out something I had completely missed w.r.t. attr
intent replay ordering against replay of data fork modifications.
There's definitely an issue here, I think it might be a fundamental
issue with the recovery mechanism (and not parent pointers), and I
think we'll end up needing  something like this patch to fix it.
Let me bounce this around my head for a bit...

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-16  2:08       ` Dave Chinner
@ 2022-06-16  5:32         ` Dave Chinner
  2022-06-29  6:33           ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16  5:32 UTC (permalink / raw)
  To: Alli; +Cc: linux-xfs

On Thu, Jun 16, 2022 at 12:08:43PM +1000, Dave Chinner wrote:
> On Wed, Jun 15, 2022 at 04:40:07PM -0700, Alli wrote:
> > On Wed, 2022-06-15 at 11:09 +1000, Dave Chinner wrote:
> > > On Sat, Jun 11, 2022 at 02:41:44AM -0700, Allison Henderson wrote:
> > > > Recent parent pointer testing has exposed a bug in the underlying
> > > > larp state machine.  A replace operation may remove an old attr
> > > > before adding the new one, but if it is the only attr in the fork,
> > > > then the fork is removed.  This later causes a null pointer in
> > > > xfs_attr_try_sf_addname which expects the fork present.  This
> > > > patch adds an extra state to create the fork.
> > > 
> > > Hmmmm.
> > > 
> > > I thought I fixed those problems - in xfs_attr_sf_removename() there
> > > is this code:
> > > 
> > >         if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp)
> > > &&
> > >             (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
> > >             !(args->op_flags & (XFS_DA_OP_ADDNAME |
> > > XFS_DA_OP_REPLACE))) {
> > >                 xfs_attr_fork_remove(dp, args->trans);
> > Hmm, ok, let me shuffle in some traces around there to see where things
> > fall off the rails
> > 
> > > 
> > > A replace operation will have XFS_DA_OP_REPLACE set, and so the
> > > final remove from a sf directory will not remove the attr fork in
> > > this case. There is equivalent checks in the leaf/node remove name
> > > paths to avoid removing the attr fork if the last attr is removed
> > > while the attr fork is in those formats.
> > > 
> > > How do you reproduce this issue?
> > > 
> > 
> > Sure, you can apply this kernel set or download it here:
> > https://github.com/allisonhenderson/xfs/tree/xfs_new_pptrs
> > 
> > Next you'll need this xfsprogs that has the neccassary updates to run
> > parent pointers
> > https://github.com/allisonhenderson/xfsprogs/tree/xfsprogs_new_pptrs
> > 
> > 
> > To reproduce the bug, you'll need to apply a quick patch on the kernel
> > side:
> > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > index b86188b63897..f279afd43462 100644
> > --- a/fs/xfs/libxfs/xfs_attr.c
> > +++ b/fs/xfs/libxfs/xfs_attr.c
> > @@ -741,8 +741,8 @@ xfs_attr_set_iter(
> >  		fallthrough;
> >  	case XFS_DAS_SF_ADD:
> >  		if (!args->dp->i_afp) {
> > -			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> > -			goto next_state;
> > +//			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> > +//			goto next_state;
> >  		}
> 
> Ah, so it's recovery that trips this....
> 
> > [  365.290048]  xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
> > [  365.290423]  xfs_attr_set_iter+0x2f9/0x1510 [xfs]
> > [  365.291592]  xfs_xattri_finish_update+0x66/0xd0 [xfs]
> > [  365.292008]  xfs_attr_finish_item+0x43/0x120 [xfs]
> > [  365.292410]  xfs_defer_finish_noroll+0x3c2/0xcc0 [xfs]
> > [  365.293196]  __xfs_trans_commit+0x333/0x610 [xfs]
> > [  365.294401]  xfs_trans_commit+0x10/0x20 [xfs]
> > [  365.294797]  xlog_finish_defer_ops+0x133/0x270 [xfs]
> > [  365.296054]  xlog_recover_process_intents+0x1f7/0x3e0 [xfs]
> 
> ayup.
> 
> > > > Additionally the new state will be used by parent pointers which
> > > > need to add attributes to newly created inodes that do not yet
> > > > have a fork.
> > > 
> > > We already have the capability of doing that in xfs_init_new_inode()
> > > by passing in init_xattrs == true. So when we are creating a new
> > > inode with parent pointers enabled, we know that we are going to be
> > > creating an xattr on the inode and so we should always set
> > > init_xattrs in that case.
> > Hmm, ok.  I'll add some tracing around in there too, if I back out the
> > entire first patch, we crash out earlier in recovery path because no
> > state is set.  If we enter xfs_attri_item_recover with no fork, we end
> > up in the following switch:
> > 
> > 
> >         case XFS_ATTRI_OP_FLAGS_REPLACE:
> >                 args->value = nv- >value.i_addr;
> >                 args->valuelen = nv- >value.i_len;
> >                 args->total = xfs_attr_calc_size(args, &local);
> >                 if (xfs_inode_hasattr(args- >dp))
> >                         attr->xattri_dela_state = xfs_attr_init_replace_state(args);
> >                 else
> >                         attr->xattri_dela_state = xfs_attr_init_add_state(args);
> >                 break;
> > 
> > Which will leave the state unset if the fork is absent.
> 
> Yeah, OK, I think this is because we are combining attribute
> creation with inode creation. When log recovery replays inode core
> modifications, it replays the inode state into the cluster buffer
> and writes it. Then when we go to replay the attr intent at the end
> of recovery, the inode is read from disk via xlog_recover_iget(),
> but we don't initialise the attr fork because ip->i_forkoff is zero.
> i.e. it has no attrs at this point.
> 
> I suspect that we could catch that in xlog_recover_iget() when it is
> called from attr recovery. i.e. we detect newly created inodes and
> initialise the attr fork similar to what we do in
> xfs_init_new_inode(). I was thinking something like this:
> 
> 	if (init_xattrs && xfs_has_attr(mp)) {
> 		if (!ip->i_forkoff && !ip->i_nextents) {
> 			ip->i_forkoff = xfs_default_attroffset(ip) >> 3;
> 			ip->i_afp = xfs_ifork_alloc(XFS_DINODE_FMT_EXTENTS, 0);
> 		} else {
> 			ASSERT(ip->i_afp);
> 		}
> 	}
> 
> Would do the trick, but then I realised that the timing/ordering is
> very different to runtime: we don't replay the attr intent until the
> end of log recovery and all the inode changes have been replayed
> into the inode cluster buffer. That means we could have already
> replayed a bunch of data fork extent modifications into the inode,
> and so the default attr offset is almost certainly not a safe thing
> to be using here. Indeed, there might not be space in the inode for
> the attr we want to insert and so we might need to convert the data
> fork to a different format before we run the attr intent replay.

Ok, so after further thought, I don't think this can happen. If we
are replaying an attr intent, it means we crashed before the intent
done was recorded in the log. At this point in time the inode was
locked and so there could be no racing changes to the inode data
fork in the log. i.e. because of log item ordering, if the intent
done is not in the log, none of the future changes that occurred
after the intent done will be in the log, either. The inode on disk
will not contain them either because the intent done must be in the
log before the inode gets unpinned and is able to be written to
disk.

Hence if we've got an attr intent to replay, it must be the last
active modification to that inode that must be replayed, and the
state of the inode on disk at the time of recovering the attr intent
should match the state of the inode in memory at the time the attr
intent was started.

Hence there isn't a consistency model coherency problem here, and
that means if there's no attr fork at the time the attr recovery is
started, it *must* be a newly created inode. If the inode already
existed and a transaction had to be run to create the attr fork
(i.e. xfs_bmap_add_attrfork() had to be run) then that transaction
would have been recovered from the log before attr replay started,
and so xlog_recovery_iget() should see a non-zero ip->i_forkoff and
initialise the attr fork correctly.

But this makes me wonder further. If the attr intent is logged in
the same transaction as the inode is allocated, then the setting of
ip->i_forkoff in xfs_init_new_inode() should also be logged in that
transaction (because XFS_ILOG_CORE is used) and hence be replayed
into the on-disk inode by recovery before the attr intent recovery
starts. Hence xlog_recover_iget() should be initialising the attr
fork through this path:

xlog_recover_iget
  xfs_iget
    xfs_iget_cache_miss
      xfs_inode_from_disk
	if (ip->i_forkoff)
	  xfs_iformat_attr_fork()

This means the newly allocated inode would have an attr fork
allocated to it, in extent format with zero extents. If we then look
at xfs_inode_hasattr():

int
xfs_inode_hasattr(
        struct xfs_inode        *ip)
{
        if (!XFS_IFORK_Q(ip))
                return 0;
        if (!ip->i_afp)
                return 0;
>>>>>   if (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS &&
>>>>>       ip->i_afp->if_nextents == 0)
>>>>>           return 0;
        return 1;
}

It would still say that it has no attrs even though the fork has
been initialised and we go down the xfs_attr_init_add_state()
branch.  This does:

static inline enum xfs_delattr_state
xfs_attr_init_add_state(struct xfs_da_args *args)
{
        /*
         * When called from the completion of a attr remove to determine the
         * next state, the attribute fork may be null. This can occur only occur
         * on a pure remove, but we grab the next state before we check if a
         * replace operation is being performed. If we are called from any other
         * context, i_afp is guaranteed to exist. Hence if the attr fork is
         * null, we were called from a pure remove operation and so we are done.
         */
>>>>    if (!args->dp->i_afp)
>>>>            return XFS_DAS_DONE;

        args->op_flags |= XFS_DA_OP_ADDNAME;
        if (xfs_attr_is_shortform(args->dp))
                return XFS_DAS_SF_ADD;
        if (xfs_attr_is_leaf(args->dp))
                return XFS_DAS_LEAF_ADD;
        return XFS_DAS_NODE_ADD;
}

Which would return XFS_DAS_DONE if there was no attr fork
initialised and recovery would be skipped completely. Hence for this
change to be required to trigger failures:

> > @@ -741,8 +741,8 @@ xfs_attr_set_iter(
> >             fallthrough;
> >     case XFS_DAS_SF_ADD:
> >             if (!args->dp->i_afp) {
> > -                   attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> > -                   goto next_state;
> > +//                 attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> > +//                 goto next_state;
> >             }

then it implies the only way we can get here is via a replace
operation that has removed the attr fork during the remove and we
hit this on the attr add. Yet, AFAICT, the attr fork does not get
removed when a replace operation is in progress.

Maybe there's a new bug introduced in the PP patchset that triggers
this - I'll do some more looking...

> > > This should avoid the need for parent pointers to ever need to run
> > > an extra transaction to create the attr fork. Hence, AFAICT, this
> > > new state to handle attr fork creation shouldn't ever be needed for
> > > parent pointers....
> > > 
> > > What am I missing?
> > > 
> > I hope the description helped?  I'll do some more poking around too and
> > post back if I find anything else.
> 
> Yup, it most definitely helped. :)
> 
> You've pointed out something I had completely missed w.r.t. attr
> intent replay ordering against replay of data fork modifications.
> There's definitely an issue here, I think it might be a fundamental
> issue with the recovery mechanism (and not parent pointers), and I
> think we'll end up needing  something like this patch to fix it.
> Let me bounce this around my head for a bit...

In summary, after further thought this turns out not to be an issue
at all, so I'm back to "replace doesn't remove the attr fork, so
how does this happen?"....

Cheers,

Dave.

-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes
  2022-06-11  9:41 ` [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes Allison Henderson
@ 2022-06-16  5:38   ` Dave Chinner
  2022-06-18  0:31     ` Alli
  2022-06-29 18:37   ` Darrick J. Wong
  1 sibling, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16  5:38 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:52AM -0700, Allison Henderson wrote:
> We need to add, remove or modify parent pointer attributes during
> create/link/unlink/rename operations atomically with the dirents in the
> parent directories being modified. This means they need to be modified
> in the same transaction as the parent directories, and so we need to add
> the required space for the attribute modifications to the transaction
> reservations.
> 
> [achender: rebased, added xfs_sb_version_hasparent stub]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_format.h     |   5 ++
>  fs/xfs/libxfs/xfs_trans_resv.c | 103 +++++++++++++++++++++++++++------
>  fs/xfs/libxfs/xfs_trans_resv.h |   1 +
>  3 files changed, 90 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index afdfc8108c5f..96976497306c 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -390,6 +390,11 @@ xfs_sb_has_incompat_feature(
>  	return (sbp->sb_features_incompat & feature) != 0;
>  }
>  
> +static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
> +{
> +	return false; /* We'll enable this at the end of the set */
> +}

Just noticed this in passing - I have not looked at the reservation
calculations at all yet. We don't use "xfs_sb_version" feature
checks anymore. This goes into xfs_mount.h as a feature flag and we
use xfs_has_parent_pointers(mp) to check for the feature rather that
superblock bits.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 10/17] xfs: parent pointer attribute creation
  2022-06-11  9:41 ` [PATCH v1 10/17] xfs: parent pointer attribute creation Allison Henderson
  2022-06-11 15:10   ` kernel test robot
@ 2022-06-16  5:49   ` Dave Chinner
  2022-06-18  0:32     ` Alli
  2022-06-29 18:41   ` Darrick J. Wong
  2 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16  5:49 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:53AM -0700, Allison Henderson wrote:
> Add parent pointer attribute during xfs_create, and subroutines to
> initialize attributes
> 
> [bfoster: rebase, use VFS inode generation]
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
>            fixed some null pointer bugs,
>            merged error handling patch,
>            added subroutines to handle attribute initialization,
>            remove unnecessary ENOSPC handling in xfs_attr_set_first_parent]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/Makefile            |  1 +
>  fs/xfs/libxfs/xfs_attr.c   |  2 +-
>  fs/xfs/libxfs/xfs_attr.h   |  1 +
>  fs/xfs/libxfs/xfs_parent.c | 77 +++++++++++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_parent.h | 31 ++++++++++++++
>  fs/xfs/xfs_inode.c         | 88 +++++++++++++++++++++++++++-----------
>  fs/xfs/xfs_xattr.c         |  2 +-
>  fs/xfs/xfs_xattr.h         |  1 +
>  8 files changed, 177 insertions(+), 26 deletions(-)
......
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index b2dfd84e1f62..6b1e4cb11b5c 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -36,6 +36,8 @@
>  #include "xfs_reflink.h"
>  #include "xfs_ag.h"
>  #include "xfs_log_priv.h"
> +#include "xfs_parent.h"
> +#include "xfs_xattr.h"
>  
>  struct kmem_cache *xfs_inode_cache;
>  
> @@ -962,27 +964,40 @@ xfs_bumplink(
>  
>  int
>  xfs_create(
> -	struct user_namespace	*mnt_userns,
> -	xfs_inode_t		*dp,
> -	struct xfs_name		*name,
> -	umode_t			mode,
> -	dev_t			rdev,
> -	bool			init_xattrs,
> -	xfs_inode_t		**ipp)
> -{
> -	int			is_dir = S_ISDIR(mode);
> -	struct xfs_mount	*mp = dp->i_mount;
> -	struct xfs_inode	*ip = NULL;
> -	struct xfs_trans	*tp = NULL;
> -	int			error;
> -	bool                    unlock_dp_on_error = false;
> -	prid_t			prid;
> -	struct xfs_dquot	*udqp = NULL;
> -	struct xfs_dquot	*gdqp = NULL;
> -	struct xfs_dquot	*pdqp = NULL;
> -	struct xfs_trans_res	*tres;
> -	uint			resblks;
> -	xfs_ino_t		ino;
> +	struct user_namespace		*mnt_userns,
> +	xfs_inode_t			*dp,
> +	struct xfs_name			*name,
> +	umode_t				mode,
> +	dev_t				rdev,
> +	bool				init_xattrs,
> +	xfs_inode_t			**ipp)
> +{
> +	int				is_dir = S_ISDIR(mode);
> +	struct xfs_mount		*mp = dp->i_mount;
> +	struct xfs_inode		*ip = NULL;
> +	struct xfs_trans		*tp = NULL;
> +	int				error;
> +	bool				unlock_dp_on_error = false;
> +	prid_t				prid;
> +	struct xfs_dquot		*udqp = NULL;
> +	struct xfs_dquot		*gdqp = NULL;
> +	struct xfs_dquot		*pdqp = NULL;
> +	struct xfs_trans_res		*tres;
> +	uint				resblks;
> +	xfs_ino_t			ino;
> +	xfs_dir2_dataptr_t		diroffset;
> +	struct xfs_parent_name_rec	rec;
> +	struct xfs_da_args		args = {
> +		.dp		= dp,
> +		.geo		= mp->m_attr_geo,
> +		.whichfork	= XFS_ATTR_FORK,
> +		.attr_filter	= XFS_ATTR_PARENT,
> +		.op_flags	= XFS_DA_OP_OKNOENT,
> +		.name		= (const uint8_t *)&rec,
> +		.namelen	= sizeof(rec),
> +		.value		= (void *)name->name,

Why the cast to void?

> +		.valuelen	= name->len,
> +	};
>  
>  	trace_xfs_create(dp, name);
>  
> @@ -1009,6 +1024,12 @@ xfs_create(
>  		tres = &M_RES(mp)->tr_create;
>  	}
>  
> +	if (xfs_has_larp(mp)) {
> +		error = xfs_attr_grab_log_assist(mp);
> +		if (error)
> +			goto out_release_dquots;
> +	}

Parent pointers can only use logged attributes - so this check
should actually be:

	if (xfs_has_parent_pointers(mp)) {
		.....
	}

i.e. having the parent pointer feature bit present on disk turns on
LARP mode unconditionally for that filesystem.

This means you are probably going to have to add a LARP mount
feature bit and xfs_has_larp(mp) should be converted to it. i.e. the
sysfs debug knob should go away once parent pointers are merged
because LARP will be turned on by PP and we won't need a debug mode
for testing LARP anymore...

> +
>  	/*
>  	 * Initially assume that the file does not exist and
>  	 * reserve the resources for that case.  If that is not
> @@ -1024,7 +1045,7 @@ xfs_create(
>  				resblks, &tp);
>  	}
>  	if (error)
> -		goto out_release_dquots;
> +		goto drop_incompat;
>  
>  	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
>  	unlock_dp_on_error = true;
> @@ -1048,11 +1069,12 @@ xfs_create(
>  	 * the transaction cancel unlocking dp so don't do it explicitly in the
>  	 * error path.
>  	 */
> -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, dp, 0);
>  	unlock_dp_on_error = false;
>  
>  	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
> -				   resblks - XFS_IALLOC_SPACE_RES(mp), NULL);
> +				   resblks - XFS_IALLOC_SPACE_RES(mp),
> +				   &diroffset);
>  	if (error) {
>  		ASSERT(error != -ENOSPC);
>  		goto out_trans_cancel;
> @@ -1068,6 +1090,20 @@ xfs_create(
>  		xfs_bumplink(tp, dp);
>  	}
>  
> +	/*
> +	 * If we have parent pointers, we need to add the attribute containing
> +	 * the parent information now.
> +	 */
> +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> +		xfs_init_parent_name_rec(&rec, dp, diroffset);
> +		args.dp	= ip;
> +		args.trans = tp;
> +		args.hashval = xfs_da_hashname(args.name, args.namelen);
> +		error =  xfs_attr_defer_add(&args);

White space.

> +		if (error)
> +			goto out_trans_cancel;
> +	}
> +
>  	/*
>  	 * If this is a synchronous mount, make sure that the
>  	 * create transaction goes to disk before returning to
> @@ -1093,6 +1129,7 @@ xfs_create(
>  
>  	*ipp = ip;
>  	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	xfs_iunlock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
>  	return 0;
>  
>   out_trans_cancel:
> @@ -1107,6 +1144,9 @@ xfs_create(
>  		xfs_finish_inode_setup(ip);
>  		xfs_irele(ip);
>  	}
> + drop_incompat:
> +	if (xfs_has_larp(mp))
> +		xlog_drop_incompat_feat(mp->m_log);

	if (xfs_has_parent_pointers(mp))

>   out_release_dquots:
>  	xfs_qm_dqrele(udqp);
>  	xfs_qm_dqrele(gdqp);
> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> index 35e13e125ec6..6012a6ba512c 100644
> --- a/fs/xfs/xfs_xattr.c
> +++ b/fs/xfs/xfs_xattr.c
> @@ -27,7 +27,7 @@
>   * they must release the permission by calling xlog_drop_incompat_feat
>   * when they're done.
>   */
> -static inline int
> +inline int
>  xfs_attr_grab_log_assist(
>  	struct xfs_mount	*mp)
>  {

Drop the inline, too.

Cheers,

Dave.

-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5.
  2022-06-11  9:41 ` [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5 Allison Henderson
@ 2022-06-16  6:03   ` Dave Chinner
  2022-06-18  0:32     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16  6:03 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:57AM -0700, Allison Henderson wrote:
> [dchinner: forward ported and cleaned up]
> [achender: rebased and added parent pointer attribute to
>            compatible attributes mask]
> 
> Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_format.h | 14 +++++++++-----
>  fs/xfs/libxfs/xfs_fs.h     |  1 +
>  fs/xfs/libxfs/xfs_sb.c     |  2 ++
>  fs/xfs/xfs_super.c         |  4 ++++
>  4 files changed, 16 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index 96976497306c..e85d6b643622 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -83,6 +83,7 @@ struct xfs_ifork;
>  #define	XFS_SB_VERSION2_OKBITS		\
>  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
>  	 XFS_SB_VERSION2_ATTR2BIT	| \
> +	 XFS_SB_VERSION2_PARENTBIT	| \
>  	 XFS_SB_VERSION2_PROJID32BIT	| \
>  	 XFS_SB_VERSION2_FTYPE)

No need for a v4 filesystem format feature bit - this is v4 only.

>  
> @@ -353,11 +354,13 @@ xfs_sb_has_compat_feature(
>  #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)		/* reverse map btree */
>  #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/* reflinked files */
>  #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/* inobt block counts */
> +#define XFS_SB_FEAT_RO_COMPAT_PARENT	(1 << 4)		/* parent inode ptr */
>  #define XFS_SB_FEAT_RO_COMPAT_ALL \
> -		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
> -		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
> -		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
> -		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
> +		(XFS_SB_FEAT_RO_COMPAT_FINOBT  | \
> +		 XFS_SB_FEAT_RO_COMPAT_RMAPBT  | \
> +		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
> +		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT| \
> +		 XFS_SB_FEAT_RO_COMPAT_PARENT)

I'm not sure this is a RO Compat feature - we added an attribute
namespace flag on disk, and the older kernels do not know about
that (i.e. we changed XFS_ATTR_NSP_ONDISK_MASK). This may result in
parent pointer attrs being exposed as user attrs rather than being
hidden, or maybe parent pointer attrs being seen as corrupt because
they have a flag that isn't defined set, etc.

Hence I'm not sure that this classification is correct.

>  #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN	~XFS_SB_FEAT_RO_COMPAT_ALL
>  static inline bool
>  xfs_sb_has_ro_compat_feature(
> @@ -392,7 +395,8 @@ xfs_sb_has_incompat_feature(
>  
>  static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
>  {
> -	return false; /* We'll enable this at the end of the set */
> +	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
> +		(sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_PARENT));
>  }

This should go away and the feature bit in the mount get set by
xfs_sb_version_to_features().

>  #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS   (1 << 0)	/* Delayed Attributes */
> diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
> index 1cfd5bc6520a..b0b4d7a3aa15 100644
> --- a/fs/xfs/libxfs/xfs_fs.h
> +++ b/fs/xfs/libxfs/xfs_fs.h
> @@ -237,6 +237,7 @@ typedef struct xfs_fsop_resblks {
>  #define XFS_FSOP_GEOM_FLAGS_BIGTIME	(1 << 21) /* 64-bit nsec timestamps */
>  #define XFS_FSOP_GEOM_FLAGS_INOBTCNT	(1 << 22) /* inobt btree counter */
>  #define XFS_FSOP_GEOM_FLAGS_NREXT64	(1 << 23) /* large extent counters */
> +#define XFS_FSOP_GEOM_FLAGS_PARENT	(1 << 24) /* parent pointers 	    */
>  
>  /*
>   * Minimum and maximum sizes need for growth checks.
> diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
> index a20cade590e9..d90b05456dba 100644
> --- a/fs/xfs/libxfs/xfs_sb.c
> +++ b/fs/xfs/libxfs/xfs_sb.c
> @@ -1187,6 +1187,8 @@ xfs_fs_geometry(
>  		geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
>  	if (xfs_has_inobtcounts(mp))
>  		geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
> +	if(xfs_sb_version_hasparent(sbp))

	if (xfs_has_parent_pointers(mp))

> +		geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
>  	if (xfs_has_sector(mp)) {
>  		geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
>  		geo->logsectsize = sbp->sb_logsectsize;
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index a6e7b4176faf..cbb492fea4a5 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -1655,6 +1655,10 @@ xfs_fs_fill_super(
>  		xfs_warn(mp,
>  	"EXPERIMENTAL Large extent counts feature in use. Use at your own risk!");
>  
> +	if (xfs_sb_version_hasparent(&mp->m_sb))

	if (xfs_has_parent_pointers(mp))

> +		xfs_alert(mp,
> +	"EXPERIMENTAL parent pointer feature enabled. Use at your own risk!");
> +
>  	error = xfs_mountfs(mp);
>  	if (error)
>  		goto out_filestream_unmount;
> -- 
> 2.25.1
> 
> 

-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 16/17] xfs: Increase  XFS_DEFER_OPS_NR_INODES to 4
  2022-06-11  9:41 ` [PATCH v1 16/17] xfs: Increase XFS_DEFER_OPS_NR_INODES to 4 Allison Henderson
@ 2022-06-16 21:54   ` Dave Chinner
  2022-06-18  0:32     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16 21:54 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:59AM -0700, Allison Henderson wrote:
> Renames that generate parent pointer updates will need to 2 extra defer
> operations. One for the rmap update and another for the parent pointer
> update

Not sure I follow this - defer operation counts are something
tracked in the transaction reservations, whilst this is changing the
number of inodes that are joined and held across defer operations.

These rmap updates already occur on the directory inodes in a rename
(when the dir update changes the dir shape), so I'm guessing that
you are now talking about changing parent attrs for the child inodes
may require attr fork shape changes (hence rmap updates) due to the
deferred parent pointer xattr update?

If so, this should be placed in the series before the modifications
to the rename operation is modified to join 4 ops to it, preferably
at the start of the series....

> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_defer.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
> index 114a3a4930a3..0c2a6e537016 100644
> --- a/fs/xfs/libxfs/xfs_defer.h
> +++ b/fs/xfs/libxfs/xfs_defer.h
> @@ -70,7 +70,7 @@ extern const struct xfs_defer_op_type xfs_attr_defer_type;
>  /*
>   * Deferred operation item relogging limits.
>   */
> -#define XFS_DEFER_OPS_NR_INODES	2	/* join up to two inodes */
> +#define XFS_DEFER_OPS_NR_INODES	4	/* join up to four inodes */

The comment is not useful  - it should desvribe what operation
requires 4 inodes to be joined. e.g.

/*
 * Rename w/ parent pointers requires 4 indoes with defered ops to
 * be joined to the transaction.
 */

Then, if we are changing the maximum number of inodes that are
joined to a deferred operation, then we need to also update the
locking code such as in xfs_defer_ops_continue() that has to order
locking of multiple inodes correctly.

Also, rename can lock and modify 5 inodes, not 4, so the 4 inodes
that get joined here need to be clearly documented somewhere. Also,
xfs_sort_for_rename() that orders all the inodes in rename into
correct locking order in an array, and xfs_lock_inodes() that does
the locking of the inodes in the array.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 11/17] xfs: add parent attributes to link
  2022-06-11  9:41 ` [PATCH v1 11/17] xfs: add parent attributes to link Allison Henderson
@ 2022-06-16 22:39   ` Dave Chinner
  2022-06-18  0:32     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-16 22:39 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:54AM -0700, Allison Henderson wrote:
> This patch modifies xfs_link to add a parent pointer to the inode.
> 
> [bfoster: rebase, use VFS inode fields, fix xfs_bmap_finish() usage]
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
>            fixed null pointer bugs]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/xfs_inode.c | 78 ++++++++++++++++++++++++++++++++++++----------
>  fs/xfs/xfs_trans.c |  7 +++--
>  fs/xfs/xfs_trans.h |  2 +-
>  3 files changed, 67 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 6b1e4cb11b5c..41c58df8e568 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -1254,14 +1254,28 @@ xfs_create_tmpfile(
>  
>  int
>  xfs_link(
> -	xfs_inode_t		*tdp,
> -	xfs_inode_t		*sip,
> -	struct xfs_name		*target_name)
> -{
> -	xfs_mount_t		*mp = tdp->i_mount;
> -	xfs_trans_t		*tp;
> -	int			error, nospace_error = 0;
> -	int			resblks;
> +	xfs_inode_t			*tdp,
> +	xfs_inode_t			*sip,
> +	struct xfs_name			*target_name)
> +{
> +	xfs_mount_t			*mp = tdp->i_mount;
> +	xfs_trans_t			*tp;
> +	int				error, nospace_error = 0;
> +	int				resblks;
> +	struct xfs_parent_name_rec	rec;
> +	xfs_dir2_dataptr_t		diroffset;
> +
> +	struct xfs_da_args		args = {
> +		.dp		= sip,
> +		.geo		= mp->m_attr_geo,
> +		.whichfork	= XFS_ATTR_FORK,
> +		.attr_filter	= XFS_ATTR_PARENT,
> +		.op_flags	= XFS_DA_OP_OKNOENT,
> +		.name		= (const uint8_t *)&rec,
> +		.namelen	= sizeof(rec),
> +		.value		= (void *)target_name->name,
> +		.valuelen	= target_name->len,
> +	};

Now that I've had a bit of a think about this, this pattern of
placing the rec on the stack and then using it as a buffer that is
then accessed in xfs_tran_commit() processing feels like a landmine.

That is, we pass transaction contexts around functions as they are
largely independent constructs, but adding this stack variable to
the defer ops attached to the transaction means that the transaction
cannot be passed back to a caller for it to be committed - that will
corrupt the stack buffer and hence silently corrupt the parent attr
that is going to be logged when the transaction is finally committed.

Hence I think this needs to be wrapped up as a dynamically allocated
structure that is freed when the defer ops are done with it. e.g.

struct xfs_parent_defer {
	struct xfs_parent_name_rec	rec;
	xfs_dir2_dataptr_t		diroffset;
	struct xfs_da_args		args;
};

and then here:

>  
>  	trace_xfs_link(tdp, target_name);
>  
> @@ -1278,11 +1292,17 @@ xfs_link(
>  	if (error)
>  		goto std_return;
>  
> +	if (xfs_has_larp(mp)) {
> +		error = xfs_attr_grab_log_assist(mp);
> +		if (error)
> +			goto std_return;
> +	}

	struct xfs_parent_defer		*parent = NULL;
.....

	error = xfs_parent_init(mp, target_name, &parent);
	if (error)
		goto std_return;

and xfs_parent_init() looks something like this:

int
xfs_parent_init(
	.....
	struct xfs_parent_defer		**parentp)
{
	struct xfs_parent_defer		*parent;

	if (!xfs_has_parent_pointers(mp))
		return 0;

	error = xfs_attr_grab_log_assist(mp);
	if (error)
		return error;

	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
	if (!parent)
		return -ENOMEM;

	/* init parent da_args */

	*parentp = parent;
	return 0;
}

With that in place, we then can wrap all this up:

>  
> +	/*
> +	 * If we have parent pointers, we now need to add the parent record to
> +	 * the attribute fork of the inode. If this is the initial parent
> +	 * attribute, we need to create it correctly, otherwise we can just add
> +	 * the parent to the inode.
> +	 */
> +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> +		args.trans = tp;
> +		xfs_init_parent_name_rec(&rec, tdp, diroffset);
> +		args.hashval = xfs_da_hashname(args.name,
> +					       args.namelen);
> +		error = xfs_attr_defer_add(&args);
> +		if (error)
> +			goto out_defer_cancel;
> +	}

with:

	if (parent) {
		error = xfs_parent_defer_add(tp, tdp, parent, diroffset);
		if (error)
			goto out_defer_cancel;
	}

and implement it something like:

int
xfs_parent_defer_add(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	struct xfs_parent_defer	*parent,
	xfs_dir2_dataptr_t	diroffset)
{
	struct xfs_da_args	*args = &parent->args;

	xfs_init_parent_name_rec(&parent->rec, ip, diroffset)
	args->trans = tp;
	args->hashval = xfs_da_hashname(args->name, args->namelen);
	return xfs_attr_defer_add(args);
}


> +
>  	/*
>  	 * If this is a synchronous mount, make sure that the
>  	 * link transaction goes to disk before returning to
> @@ -1331,11 +1367,21 @@ xfs_link(
>  	if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))
>  		xfs_trans_set_sync(tp);
>  
> -	return xfs_trans_commit(tp);
> +	error = xfs_trans_commit(tp);
> +	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
> +	xfs_iunlock(sip, XFS_ILOCK_EXCL);

with a xfs_parent_free(parent) added here now that we are done with
the parent update.

> +	return error;
>  
> - error_return:
> +out_defer_cancel:
> +	xfs_defer_cancel(tp);
> +error_return:
>  	xfs_trans_cancel(tp);
> - std_return:
> +	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
> +	xfs_iunlock(sip, XFS_ILOCK_EXCL);
> +drop_incompat:
> +	if (xfs_has_larp(mp))
> +		xlog_drop_incompat_feat(mp->m_log);

And this can be replace with  xfs_parent_cancel(mp, parent); that
drops the log incompat featuer and frees the parent if it is not
null.

> +std_return:
>  	if (error == -ENOSPC && nospace_error)
>  		error = nospace_error;
>  	return error;
> @@ -2819,7 +2865,7 @@ xfs_remove(
>  	 */
>  	resblks = XFS_REMOVE_SPACE_RES(mp);
>  	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,
> -			&tp, &dontcare);
> +			&tp, &dontcare, XFS_ILOCK_EXCL);

So you add this flag here so that link and remove can do different
things in xfs_trans_alloc_dir(), but in the very next patch
this gets changed to zero, so both callers only pass 0 to the
function.

Ideally there should be a patch prior to this one that converts
the locking and joining of both link and remove to use external
inode locking in a single patch, similar to the change in the second
patch that changed the inode locking around xfs_init_new_inode() to
require manual unlock. Then all the locking mods in this and the
next patch go away, leaving just the parent pointer mods in this
patch....

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes
  2022-06-16  5:38   ` Dave Chinner
@ 2022-06-18  0:31     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-18  0:31 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Thu, 2022-06-16 at 15:38 +1000, Dave Chinner wrote:
> On Sat, Jun 11, 2022 at 02:41:52AM -0700, Allison Henderson wrote:
> > We need to add, remove or modify parent pointer attributes during
> > create/link/unlink/rename operations atomically with the dirents in
> > the
> > parent directories being modified. This means they need to be
> > modified
> > in the same transaction as the parent directories, and so we need
> > to add
> > the required space for the attribute modifications to the
> > transaction
> > reservations.
> > 
> > [achender: rebased, added xfs_sb_version_hasparent stub]
> > 
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_format.h     |   5 ++
> >  fs/xfs/libxfs/xfs_trans_resv.c | 103 +++++++++++++++++++++++++++
> > ------
> >  fs/xfs/libxfs/xfs_trans_resv.h |   1 +
> >  3 files changed, 90 insertions(+), 19 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_format.h
> > b/fs/xfs/libxfs/xfs_format.h
> > index afdfc8108c5f..96976497306c 100644
> > --- a/fs/xfs/libxfs/xfs_format.h
> > +++ b/fs/xfs/libxfs/xfs_format.h
> > @@ -390,6 +390,11 @@ xfs_sb_has_incompat_feature(
> >  	return (sbp->sb_features_incompat & feature) != 0;
> >  }
> >  
> > +static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
> > +{
> > +	return false; /* We'll enable this at the end of the set */
> > +}
> 
> Just noticed this in passing - I have not looked at the reservation
> calculations at all yet. We don't use "xfs_sb_version" feature
> checks anymore. This goes into xfs_mount.h as a feature flag and we
> use xfs_has_parent_pointers(mp) to check for the feature rather that
> superblock bits.
Sure, will move.  Thx for the reviews!

Allison
> 
> Cheers,
> 
> Dave.


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

* Re: [PATCH v1 10/17] xfs: parent pointer attribute creation
  2022-06-16  5:49   ` Dave Chinner
@ 2022-06-18  0:32     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-18  0:32 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Thu, 2022-06-16 at 15:49 +1000, Dave Chinner wrote:
> On Sat, Jun 11, 2022 at 02:41:53AM -0700, Allison Henderson wrote:
> > Add parent pointer attribute during xfs_create, and subroutines to
> > initialize attributes
> > 
> > [bfoster: rebase, use VFS inode generation]
> > [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
> >            fixed some null pointer bugs,
> >            merged error handling patch,
> >            added subroutines to handle attribute initialization,
> >            remove unnecessary ENOSPC handling in
> > xfs_attr_set_first_parent]
> > 
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/Makefile            |  1 +
> >  fs/xfs/libxfs/xfs_attr.c   |  2 +-
> >  fs/xfs/libxfs/xfs_attr.h   |  1 +
> >  fs/xfs/libxfs/xfs_parent.c | 77 +++++++++++++++++++++++++++++++++
> >  fs/xfs/libxfs/xfs_parent.h | 31 ++++++++++++++
> >  fs/xfs/xfs_inode.c         | 88 +++++++++++++++++++++++++++-------
> > ----
> >  fs/xfs/xfs_xattr.c         |  2 +-
> >  fs/xfs/xfs_xattr.h         |  1 +
> >  8 files changed, 177 insertions(+), 26 deletions(-)
> ......
> > diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> > index b2dfd84e1f62..6b1e4cb11b5c 100644
> > --- a/fs/xfs/xfs_inode.c
> > +++ b/fs/xfs/xfs_inode.c
> > @@ -36,6 +36,8 @@
> >  #include "xfs_reflink.h"
> >  #include "xfs_ag.h"
> >  #include "xfs_log_priv.h"
> > +#include "xfs_parent.h"
> > +#include "xfs_xattr.h"
> >  
> >  struct kmem_cache *xfs_inode_cache;
> >  
> > @@ -962,27 +964,40 @@ xfs_bumplink(
> >  
> >  int
> >  xfs_create(
> > -	struct user_namespace	*mnt_userns,
> > -	xfs_inode_t		*dp,
> > -	struct xfs_name		*name,
> > -	umode_t			mode,
> > -	dev_t			rdev,
> > -	bool			init_xattrs,
> > -	xfs_inode_t		**ipp)
> > -{
> > -	int			is_dir = S_ISDIR(mode);
> > -	struct xfs_mount	*mp = dp->i_mount;
> > -	struct xfs_inode	*ip = NULL;
> > -	struct xfs_trans	*tp = NULL;
> > -	int			error;
> > -	bool                    unlock_dp_on_error = false;
> > -	prid_t			prid;
> > -	struct xfs_dquot	*udqp = NULL;
> > -	struct xfs_dquot	*gdqp = NULL;
> > -	struct xfs_dquot	*pdqp = NULL;
> > -	struct xfs_trans_res	*tres;
> > -	uint			resblks;
> > -	xfs_ino_t		ino;
> > +	struct user_namespace		*mnt_userns,
> > +	xfs_inode_t			*dp,
> > +	struct xfs_name			*name,
> > +	umode_t				mode,
> > +	dev_t				rdev,
> > +	bool				init_xattrs,
> > +	xfs_inode_t			**ipp)
> > +{
> > +	int				is_dir = S_ISDIR(mode);
> > +	struct xfs_mount		*mp = dp->i_mount;
> > +	struct xfs_inode		*ip = NULL;
> > +	struct xfs_trans		*tp = NULL;
> > +	int				error;
> > +	bool				unlock_dp_on_error = false;
> > +	prid_t				prid;
> > +	struct xfs_dquot		*udqp = NULL;
> > +	struct xfs_dquot		*gdqp = NULL;
> > +	struct xfs_dquot		*pdqp = NULL;
> > +	struct xfs_trans_res		*tres;
> > +	uint				resblks;
> > +	xfs_ino_t			ino;
> > +	xfs_dir2_dataptr_t		diroffset;
> > +	struct xfs_parent_name_rec	rec;
> > +	struct xfs_da_args		args = {
> > +		.dp		= dp,
> > +		.geo		= mp->m_attr_geo,
> > +		.whichfork	= XFS_ATTR_FORK,
> > +		.attr_filter	= XFS_ATTR_PARENT,
> > +		.op_flags	= XFS_DA_OP_OKNOENT,
> > +		.name		= (const uint8_t *)&rec,
> > +		.namelen	= sizeof(rec),
> > +		.value		= (void *)name->name,
> 
> Why the cast to void?
.value is a void*, but name is a const char *, so we get a compiler
warning with out the cast

> 
> > +		.valuelen	= name->len,
> > +	};
> >  
> >  	trace_xfs_create(dp, name);
> >  
> > @@ -1009,6 +1024,12 @@ xfs_create(
> >  		tres = &M_RES(mp)->tr_create;
> >  	}
> >  
> > +	if (xfs_has_larp(mp)) {
> > +		error = xfs_attr_grab_log_assist(mp);
> > +		if (error)
> > +			goto out_release_dquots;
> > +	}
> 
> Parent pointers can only use logged attributes - so this check
> should actually be:
> 
> 	if (xfs_has_parent_pointers(mp)) {
> 		.....
> 	}
> 
> i.e. having the parent pointer feature bit present on disk turns on
> LARP mode unconditionally for that filesystem.
> 
> This means you are probably going to have to add a LARP mount
> feature bit and xfs_has_larp(mp) should be converted to it. i.e. the
> sysfs debug knob should go away once parent pointers are merged
> because LARP will be turned on by PP and we won't need a debug mode
> for testing LARP anymore...Ok, that makes sense 

Ok, as I recall initially larp was a mount option.  Then later in the
reviews we decided to make it a debug option.  I /think/ the reason was
that debug options are easier to deprecate than mount options?  Since
you dont have to deal with user space changes or breaking peoples mount
commands?  And the sysfs knob can be toggled without a remount.

Also, I seem to recall the reason pptrs was stuck as a mkfs option was
because it cant be toggled right?  Or we end up with some inodes that
have it, and some that don't.  So to be clear: we're proposing a mkfs
option that turns on a mount option.

I guess I'm struggling with why we have to loose the debug option?  It
doesnt seem inappropriate to me to have a mkfs option that alters a
debug option.  Other file systems that dont have pptrs on can still use
the larp knob.  And then the ones that do can just assume it on and
ignore the knob.  Maybe print a warning that clarifies that pptr
enabled file systems cannot disable larp.  Doesnt that retain all the
reasons we put the debug knob in?  With less surgery to larp, userspace
and the test cases?


> 
> > +
> >  	/*
> >  	 * Initially assume that the file does not exist and
> >  	 * reserve the resources for that case.  If that is not
> > @@ -1024,7 +1045,7 @@ xfs_create(
> >  				resblks, &tp);
> >  	}
> >  	if (error)
> > -		goto out_release_dquots;
> > +		goto drop_incompat;
> >  
> >  	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
> >  	unlock_dp_on_error = true;
> > @@ -1048,11 +1069,12 @@ xfs_create(
> >  	 * the transaction cancel unlocking dp so don't do it
> > explicitly in the
> >  	 * error path.
> >  	 */
> > -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
> > +	xfs_trans_ijoin(tp, dp, 0);
> >  	unlock_dp_on_error = false;
> >  
> >  	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
> > -				   resblks - XFS_IALLOC_SPACE_RES(mp),
> > NULL);
> > +				   resblks - XFS_IALLOC_SPACE_RES(mp),
> > +				   &diroffset);
> >  	if (error) {
> >  		ASSERT(error != -ENOSPC);
> >  		goto out_trans_cancel;
> > @@ -1068,6 +1090,20 @@ xfs_create(
> >  		xfs_bumplink(tp, dp);
> >  	}
> >  
> > +	/*
> > +	 * If we have parent pointers, we need to add the attribute
> > containing
> > +	 * the parent information now.
> > +	 */
> > +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> > +		xfs_init_parent_name_rec(&rec, dp, diroffset);
> > +		args.dp	= ip;
> > +		args.trans = tp;
> > +		args.hashval = xfs_da_hashname(args.name,
> > args.namelen);
> > +		error =  xfs_attr_defer_add(&args);
> 
> White space.
will clean out

> 
> > +		if (error)
> > +			goto out_trans_cancel;
> > +	}
> > +
> >  	/*
> >  	 * If this is a synchronous mount, make sure that the
> >  	 * create transaction goes to disk before returning to
> > @@ -1093,6 +1129,7 @@ xfs_create(
> >  
> >  	*ipp = ip;
> >  	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> > +	xfs_iunlock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
> >  	return 0;
> >  
> >   out_trans_cancel:
> > @@ -1107,6 +1144,9 @@ xfs_create(
> >  		xfs_finish_inode_setup(ip);
> >  		xfs_irele(ip);
> >  	}
> > + drop_incompat:
> > +	if (xfs_has_larp(mp))
> > +		xlog_drop_incompat_feat(mp->m_log);
> 
> 	if (xfs_has_parent_pointers(mp))
Will update

> 
> >   out_release_dquots:
> >  	xfs_qm_dqrele(udqp);
> >  	xfs_qm_dqrele(gdqp);
> > diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> > index 35e13e125ec6..6012a6ba512c 100644
> > --- a/fs/xfs/xfs_xattr.c
> > +++ b/fs/xfs/xfs_xattr.c
> > @@ -27,7 +27,7 @@
> >   * they must release the permission by calling
> > xlog_drop_incompat_feat
> >   * when they're done.
> >   */
> > -static inline int
> > +inline int
> >  xfs_attr_grab_log_assist(
> >  	struct xfs_mount	*mp)
> >  {
> 
> Drop the inline, too.
> 
Alrighty, thx for the reviews!

Allison

> Cheers,
> 
> Dave.
> 


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

* Re: [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5.
  2022-06-16  6:03   ` Dave Chinner
@ 2022-06-18  0:32     ` Alli
  2022-06-20  0:21       ` Dave Chinner
  0 siblings, 1 reply; 58+ messages in thread
From: Alli @ 2022-06-18  0:32 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Thu, 2022-06-16 at 16:03 +1000, Dave Chinner wrote:
> On Sat, Jun 11, 2022 at 02:41:57AM -0700, Allison Henderson wrote:
> > [dchinner: forward ported and cleaned up]
> > [achender: rebased and added parent pointer attribute to
> >            compatible attributes mask]
> > 
> > Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_format.h | 14 +++++++++-----
> >  fs/xfs/libxfs/xfs_fs.h     |  1 +
> >  fs/xfs/libxfs/xfs_sb.c     |  2 ++
> >  fs/xfs/xfs_super.c         |  4 ++++
> >  4 files changed, 16 insertions(+), 5 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_format.h
> > b/fs/xfs/libxfs/xfs_format.h
> > index 96976497306c..e85d6b643622 100644
> > --- a/fs/xfs/libxfs/xfs_format.h
> > +++ b/fs/xfs/libxfs/xfs_format.h
> > @@ -83,6 +83,7 @@ struct xfs_ifork;
> >  #define	XFS_SB_VERSION2_OKBITS		\
> >  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> >  	 XFS_SB_VERSION2_ATTR2BIT	| \
> > +	 XFS_SB_VERSION2_PARENTBIT	| \
> >  	 XFS_SB_VERSION2_PROJID32BIT	| \
> >  	 XFS_SB_VERSION2_FTYPE)
> 
> No need for a v4 filesystem format feature bit - this is v4 only.
Ok, I ended up having to add this in the rebase or we get an "SB
validate failed".  I think it has to go over in
xfs_sb_validate_v5_features next to the manual crc bit check.  Will
move

> 
> >  
> > @@ -353,11 +354,13 @@ xfs_sb_has_compat_feature(
> >  #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)		/*
> > reverse map btree */
> >  #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/*
> > reflinked files */
> >  #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/*
> > inobt block counts */
> > +#define XFS_SB_FEAT_RO_COMPAT_PARENT	(1 << 4)		/*
> > parent inode ptr */
> >  #define XFS_SB_FEAT_RO_COMPAT_ALL \
> > -		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
> > -		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
> > -		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
> > -		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
> > +		(XFS_SB_FEAT_RO_COMPAT_FINOBT  | \
> > +		 XFS_SB_FEAT_RO_COMPAT_RMAPBT  | \
> > +		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
> > +		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT| \
> > +		 XFS_SB_FEAT_RO_COMPAT_PARENT)
> 
> I'm not sure this is a RO Compat feature - we added an attribute
> namespace flag on disk, and the older kernels do not know about
> that (i.e. we changed XFS_ATTR_NSP_ONDISK_MASK). This may result in
> parent pointer attrs being exposed as user attrs rather than being
> hidden, or maybe parent pointer attrs being seen as corrupt because
> they have a flag that isn't defined set, etc.
> 
> Hence I'm not sure that this classification is correct.

Gosh, I'm sure there was a reason we did this, but what ever it was
goes all the way back in the first re-appearance of the set back in
2018 and I just cant remember the discussion at the time.  It may have
just been done to get mkfs working and we just never got to reviewing
it.

Should we drop it and just use XFS_SB_VERSION2_PARENTBIT?

> 
> >  #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN	~XFS_SB_FEAT_RO_COMPAT_
> > ALL
> >  static inline bool
> >  xfs_sb_has_ro_compat_feature(
> > @@ -392,7 +395,8 @@ xfs_sb_has_incompat_feature(
> >  
> >  static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
> >  {
> > -	return false; /* We'll enable this at the end of the set */
> > +	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
> > +		(sbp->sb_features_ro_compat &
> > XFS_SB_FEAT_RO_COMPAT_PARENT));
> >  }
> 
> This should go away and the feature bit in the mount get set by
> xfs_sb_version_to_features().
> 
Alrighty

> >  #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS   (1 << 0)	/* Delayed
> > Attributes */
> > diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
> > index 1cfd5bc6520a..b0b4d7a3aa15 100644
> > --- a/fs/xfs/libxfs/xfs_fs.h
> > +++ b/fs/xfs/libxfs/xfs_fs.h
> > @@ -237,6 +237,7 @@ typedef struct xfs_fsop_resblks {
> >  #define XFS_FSOP_GEOM_FLAGS_BIGTIME	(1 << 21) /* 64-bit
> > nsec timestamps */
> >  #define XFS_FSOP_GEOM_FLAGS_INOBTCNT	(1 << 22) /* inobt
> > btree counter */
> >  #define XFS_FSOP_GEOM_FLAGS_NREXT64	(1 << 23) /* large
> > extent counters */
> > +#define XFS_FSOP_GEOM_FLAGS_PARENT	(1 << 24) /* parent pointers 	
> >     */
> >  
> >  /*
> >   * Minimum and maximum sizes need for growth checks.
> > diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
> > index a20cade590e9..d90b05456dba 100644
> > --- a/fs/xfs/libxfs/xfs_sb.c
> > +++ b/fs/xfs/libxfs/xfs_sb.c
> > @@ -1187,6 +1187,8 @@ xfs_fs_geometry(
> >  		geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
> >  	if (xfs_has_inobtcounts(mp))
> >  		geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
> > +	if(xfs_sb_version_hasparent(sbp))
> 
> 	if (xfs_has_parent_pointers(mp))
> 
Will update

> > +		geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
> >  	if (xfs_has_sector(mp)) {
> >  		geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
> >  		geo->logsectsize = sbp->sb_logsectsize;
> > diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> > index a6e7b4176faf..cbb492fea4a5 100644
> > --- a/fs/xfs/xfs_super.c
> > +++ b/fs/xfs/xfs_super.c
> > @@ -1655,6 +1655,10 @@ xfs_fs_fill_super(
> >  		xfs_warn(mp,
> >  	"EXPERIMENTAL Large extent counts feature in use. Use at your
> > own risk!");
> >  
> > +	if (xfs_sb_version_hasparent(&mp->m_sb))
> 
> 	if (xfs_has_parent_pointers(mp))
> 
Will update

Thanks for the reviews!
Allison

> > +		xfs_alert(mp,
> > +	"EXPERIMENTAL parent pointer feature enabled. Use at your own
> > risk!");
> > +
> >  	error = xfs_mountfs(mp);
> >  	if (error)
> >  		goto out_filestream_unmount;
> > -- 
> > 2.25.1
> > 
> > 


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

* Re: [PATCH v1 16/17] xfs: Increase  XFS_DEFER_OPS_NR_INODES to 4
  2022-06-16 21:54   ` Dave Chinner
@ 2022-06-18  0:32     ` Alli
  2022-06-29 18:43       ` Darrick J. Wong
  0 siblings, 1 reply; 58+ messages in thread
From: Alli @ 2022-06-18  0:32 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, 2022-06-17 at 07:54 +1000, Dave Chinner wrote:
> On Sat, Jun 11, 2022 at 02:41:59AM -0700, Allison Henderson wrote:
> > Renames that generate parent pointer updates will need to 2 extra
> > defer
> > operations. One for the rmap update and another for the parent
> > pointer
> > update
> 
> Not sure I follow this - defer operation counts are something
> tracked in the transaction reservations, whilst this is changing the
> number of inodes that are joined and held across defer operations.
> 
> These rmap updates already occur on the directory inodes in a rename
> (when the dir update changes the dir shape), so I'm guessing that
> you are now talking about changing parent attrs for the child inodes
> may require attr fork shape changes (hence rmap updates) due to the
> deferred parent pointer xattr update?
> 
> If so, this should be placed in the series before the modifications
> to the rename operation is modified to join 4 ops to it, preferably
> at the start of the series....

I see, sure, I can move this patch down to the beginning of the set
> 
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_defer.h | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
> > index 114a3a4930a3..0c2a6e537016 100644
> > --- a/fs/xfs/libxfs/xfs_defer.h
> > +++ b/fs/xfs/libxfs/xfs_defer.h
> > @@ -70,7 +70,7 @@ extern const struct xfs_defer_op_type
> > xfs_attr_defer_type;
> >  /*
> >   * Deferred operation item relogging limits.
> >   */
> > -#define XFS_DEFER_OPS_NR_INODES	2	/* join up to two inodes */
> > +#define XFS_DEFER_OPS_NR_INODES	4	/* join up to four inodes
> > */
> 
> The comment is not useful  - it should desvribe what operation
> requires 4 inodes to be joined. e.g.
> 
> /*
>  * Rename w/ parent pointers requires 4 indoes with defered ops to
>  * be joined to the transaction.
>  */
Sure, will update

> 
> Then, if we are changing the maximum number of inodes that are
> joined to a deferred operation, then we need to also update the
> locking code such as in xfs_defer_ops_continue() that has to order
> locking of multiple inodes correctly.
Ok, I see it, I will take a look at updating that

> 
> Also, rename can lock and modify 5 inodes, not 4, so the 4 inodes
> that get joined here need to be clearly documented somewhere. 
Ok, I think its src dir, target dir, src inode, target inode, and then
wip.  Do we want the documenting in xfs_defer_ops_continue?  Or just
the commit description?

> Also,
> xfs_sort_for_rename() that orders all the inodes in rename into
> correct locking order in an array, and xfs_lock_inodes() that does
> the locking of the inodes in the array.
Yes, I see it.  You want a comment in xfs_defer_ops_continue referring
to the order?

Thanks!
Allison

> 
> Cheers,
> 
> Dave.


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

* Re: [PATCH v1 11/17] xfs: add parent attributes to link
  2022-06-16 22:39   ` Dave Chinner
@ 2022-06-18  0:32     ` Alli
  2022-06-29 18:09       ` Darrick J. Wong
  0 siblings, 1 reply; 58+ messages in thread
From: Alli @ 2022-06-18  0:32 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, 2022-06-17 at 08:39 +1000, Dave Chinner wrote:
> On Sat, Jun 11, 2022 at 02:41:54AM -0700, Allison Henderson wrote:
> > This patch modifies xfs_link to add a parent pointer to the inode.
> > 
> > [bfoster: rebase, use VFS inode fields, fix xfs_bmap_finish()
> > usage]
> > [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
> >            fixed null pointer bugs]
> > 
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/xfs_inode.c | 78 ++++++++++++++++++++++++++++++++++++----
> > ------
> >  fs/xfs/xfs_trans.c |  7 +++--
> >  fs/xfs/xfs_trans.h |  2 +-
> >  3 files changed, 67 insertions(+), 20 deletions(-)
> > 
> > diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> > index 6b1e4cb11b5c..41c58df8e568 100644
> > --- a/fs/xfs/xfs_inode.c
> > +++ b/fs/xfs/xfs_inode.c
> > @@ -1254,14 +1254,28 @@ xfs_create_tmpfile(
> >  
> >  int
> >  xfs_link(
> > -	xfs_inode_t		*tdp,
> > -	xfs_inode_t		*sip,
> > -	struct xfs_name		*target_name)
> > -{
> > -	xfs_mount_t		*mp = tdp->i_mount;
> > -	xfs_trans_t		*tp;
> > -	int			error, nospace_error = 0;
> > -	int			resblks;
> > +	xfs_inode_t			*tdp,
> > +	xfs_inode_t			*sip,
> > +	struct xfs_name			*target_name)
> > +{
> > +	xfs_mount_t			*mp = tdp->i_mount;
> > +	xfs_trans_t			*tp;
> > +	int				error, nospace_error = 0;
> > +	int				resblks;
> > +	struct xfs_parent_name_rec	rec;
> > +	xfs_dir2_dataptr_t		diroffset;
> > +
> > +	struct xfs_da_args		args = {
> > +		.dp		= sip,
> > +		.geo		= mp->m_attr_geo,
> > +		.whichfork	= XFS_ATTR_FORK,
> > +		.attr_filter	= XFS_ATTR_PARENT,
> > +		.op_flags	= XFS_DA_OP_OKNOENT,
> > +		.name		= (const uint8_t *)&rec,
> > +		.namelen	= sizeof(rec),
> > +		.value		= (void *)target_name->name,
> > +		.valuelen	= target_name->len,
> > +	};
> 
> Now that I've had a bit of a think about this, this pattern of
> placing the rec on the stack and then using it as a buffer that is
> then accessed in xfs_tran_commit() processing feels like a landmine.
> 
> That is, we pass transaction contexts around functions as they are
> largely independent constructs, but adding this stack variable to
> the defer ops attached to the transaction means that the transaction
> cannot be passed back to a caller for it to be committed - that will
> corrupt the stack buffer and hence silently corrupt the parent attr
> that is going to be logged when the transaction is finally committed.
> 
> Hence I think this needs to be wrapped up as a dynamically allocated
> structure that is freed when the defer ops are done with it. e.g.
> 
> struct xfs_parent_defer {
> 	struct xfs_parent_name_rec	rec;
> 	xfs_dir2_dataptr_t		diroffset;
> 	struct xfs_da_args		args;
> };
> 
> and then here:
> 
> >  
> >  	trace_xfs_link(tdp, target_name);
> >  
> > @@ -1278,11 +1292,17 @@ xfs_link(
> >  	if (error)
> >  		goto std_return;
> >  
> > +	if (xfs_has_larp(mp)) {
> > +		error = xfs_attr_grab_log_assist(mp);
> > +		if (error)
> > +			goto std_return;
> > +	}
> 
> 	struct xfs_parent_defer		*parent = NULL;
> .....
> 
> 	error = xfs_parent_init(mp, target_name, &parent);
> 	if (error)
> 		goto std_return;
> 
> and xfs_parent_init() looks something like this:
> 
> int
> xfs_parent_init(
> 	.....
> 	struct xfs_parent_defer		**parentp)
> {
> 	struct xfs_parent_defer		*parent;
> 
> 	if (!xfs_has_parent_pointers(mp))
> 		return 0;
> 
> 	error = xfs_attr_grab_log_assist(mp);
> 	if (error)
> 		return error;
> 
> 	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
> 	if (!parent)
> 		return -ENOMEM;
> 
> 	/* init parent da_args */
> 
> 	*parentp = parent;
> 	return 0;
> }
> 
> With that in place, we then can wrap all this up:
> 
> >  
> > +	/*
> > +	 * If we have parent pointers, we now need to add the parent
> > record to
> > +	 * the attribute fork of the inode. If this is the initial
> > parent
> > +	 * attribute, we need to create it correctly, otherwise we can
> > just add
> > +	 * the parent to the inode.
> > +	 */
> > +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> > +		args.trans = tp;
> > +		xfs_init_parent_name_rec(&rec, tdp, diroffset);
> > +		args.hashval = xfs_da_hashname(args.name,
> > +					       args.namelen);
> > +		error = xfs_attr_defer_add(&args);
> > +		if (error)
> > +			goto out_defer_cancel;
> > +	}
> 
> with:
> 
> 	if (parent) {
> 		error = xfs_parent_defer_add(tp, tdp, parent,
> diroffset);
> 		if (error)
> 			goto out_defer_cancel;
> 	}
> 
> and implement it something like:
> 
> int
> xfs_parent_defer_add(
> 	struct xfs_trans	*tp,
> 	struct xfs_inode	*ip,
> 	struct xfs_parent_defer	*parent,
> 	xfs_dir2_dataptr_t	diroffset)
> {
> 	struct xfs_da_args	*args = &parent->args;
> 
> 	xfs_init_parent_name_rec(&parent->rec, ip, diroffset)
> 	args->trans = tp;
> 	args->hashval = xfs_da_hashname(args->name, args->namelen);
> 	return xfs_attr_defer_add(args);
> }
> 
> 
> > +
> >  	/*
> >  	 * If this is a synchronous mount, make sure that the
> >  	 * link transaction goes to disk before returning to
> > @@ -1331,11 +1367,21 @@ xfs_link(
> >  	if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))
> >  		xfs_trans_set_sync(tp);
> >  
> > -	return xfs_trans_commit(tp);
> > +	error = xfs_trans_commit(tp);
> > +	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
> > +	xfs_iunlock(sip, XFS_ILOCK_EXCL);
> 
> with a xfs_parent_free(parent) added here now that we are done with
> the parent update.
> 
> > +	return error;
> >  
> > - error_return:
> > +out_defer_cancel:
> > +	xfs_defer_cancel(tp);
> > +error_return:
> >  	xfs_trans_cancel(tp);
> > - std_return:
> > +	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
> > +	xfs_iunlock(sip, XFS_ILOCK_EXCL);
> > +drop_incompat:
> > +	if (xfs_has_larp(mp))
> > +		xlog_drop_incompat_feat(mp->m_log);
> 
> And this can be replace with  xfs_parent_cancel(mp, parent); that
> drops the log incompat featuer and frees the parent if it is not
> null.

Sure, that sounds reasonable.  Let me punch it up and see how it does
int the tests.

> 
> > +std_return:
> >  	if (error == -ENOSPC && nospace_error)
> >  		error = nospace_error;
> >  	return error;
> > @@ -2819,7 +2865,7 @@ xfs_remove(
> >  	 */
> >  	resblks = XFS_REMOVE_SPACE_RES(mp);
> >  	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip,
> > &resblks,
> > -			&tp, &dontcare);
> > +			&tp, &dontcare, XFS_ILOCK_EXCL);
> 
> So you add this flag here so that link and remove can do different
> things in xfs_trans_alloc_dir(), but in the very next patch
> this gets changed to zero, so both callers only pass 0 to the
> function.
> 
> Ideally there should be a patch prior to this one that converts
> the locking and joining of both link and remove to use external
> inode locking in a single patch, similar to the change in the second
> patch that changed the inode locking around xfs_init_new_inode() to
> require manual unlock. Then all the locking mods in this and the
> next patch go away, leaving just the parent pointer mods in this
> patch....
Sure, I can do it that way too.

Thanks for the reviews!
Allison

> 
> Cheers,
> 
> Dave.


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

* Re: [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5.
  2022-06-18  0:32     ` Alli
@ 2022-06-20  0:21       ` Dave Chinner
  2022-06-29 18:16         ` Darrick J. Wong
  0 siblings, 1 reply; 58+ messages in thread
From: Dave Chinner @ 2022-06-20  0:21 UTC (permalink / raw)
  To: Alli; +Cc: linux-xfs

On Fri, Jun 17, 2022 at 05:32:36PM -0700, Alli wrote:
> On Thu, 2022-06-16 at 16:03 +1000, Dave Chinner wrote:
> > On Sat, Jun 11, 2022 at 02:41:57AM -0700, Allison Henderson wrote:
> > > [dchinner: forward ported and cleaned up]
> > > [achender: rebased and added parent pointer attribute to
> > >            compatible attributes mask]
> > > 
> > > Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> > > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > ---
> > >  fs/xfs/libxfs/xfs_format.h | 14 +++++++++-----
> > >  fs/xfs/libxfs/xfs_fs.h     |  1 +
> > >  fs/xfs/libxfs/xfs_sb.c     |  2 ++
> > >  fs/xfs/xfs_super.c         |  4 ++++
> > >  4 files changed, 16 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_format.h
> > > b/fs/xfs/libxfs/xfs_format.h
> > > index 96976497306c..e85d6b643622 100644
> > > --- a/fs/xfs/libxfs/xfs_format.h
> > > +++ b/fs/xfs/libxfs/xfs_format.h
> > > @@ -83,6 +83,7 @@ struct xfs_ifork;
> > >  #define	XFS_SB_VERSION2_OKBITS		\
> > >  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> > >  	 XFS_SB_VERSION2_ATTR2BIT	| \
> > > +	 XFS_SB_VERSION2_PARENTBIT	| \
> > >  	 XFS_SB_VERSION2_PROJID32BIT	| \
> > >  	 XFS_SB_VERSION2_FTYPE)
> > 
> > No need for a v4 filesystem format feature bit - this is v4 only.
> Ok, I ended up having to add this in the rebase or we get an "SB
> validate failed".  I think it has to go over in
> xfs_sb_validate_v5_features next to the manual crc bit check.  Will
> move

Ah, I meant that parent pointers are a v5 only feature, and so we
don't need a "v4 only" feature bit for it. As it is, we can't use
that specific bit because SGI shipped a version of parent pointers
on v4 filesystems on IRIX under that feature bit that was broken and
subsequently recalled and killed. Essentially, that means
XFS_SB_VERSION2_PARENTBIT is blacklisted and cannot ever be used by
upstream kernels.

> > > @@ -353,11 +354,13 @@ xfs_sb_has_compat_feature(
> > >  #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)		/*
> > > reverse map btree */
> > >  #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/*
> > > reflinked files */
> > >  #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/*
> > > inobt block counts */
> > > +#define XFS_SB_FEAT_RO_COMPAT_PARENT	(1 << 4)		/*
> > > parent inode ptr */
> > >  #define XFS_SB_FEAT_RO_COMPAT_ALL \
> > > -		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
> > > -		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
> > > -		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
> > > -		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
> > > +		(XFS_SB_FEAT_RO_COMPAT_FINOBT  | \
> > > +		 XFS_SB_FEAT_RO_COMPAT_RMAPBT  | \
> > > +		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
> > > +		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT| \
> > > +		 XFS_SB_FEAT_RO_COMPAT_PARENT)
> > 
> > I'm not sure this is a RO Compat feature - we added an attribute
> > namespace flag on disk, and the older kernels do not know about
> > that (i.e. we changed XFS_ATTR_NSP_ONDISK_MASK). This may result in
> > parent pointer attrs being exposed as user attrs rather than being
> > hidden, or maybe parent pointer attrs being seen as corrupt because
> > they have a flag that isn't defined set, etc.
> > 
> > Hence I'm not sure that this classification is correct.
> 
> Gosh, I'm sure there was a reason we did this, but what ever it was
> goes all the way back in the first re-appearance of the set back in
> 2018 and I just cant remember the discussion at the time.  It may have
> just been done to get mkfs working and we just never got to reviewing
> it.
> 
> Should we drop it and just use XFS_SB_VERSION2_PARENTBIT?

No, it needs to be a v5 feature bit - create a v5 parent pointer
filesystem, create some files on it, and then go an mount it on a
kernel that doesn't have PP support. If you can see the parent
pointer attributes from userspace as "user.<binary garbage>"
attributes, then we need to use an INCOMPAT feature bit rather than
a RO_COMPAT bit.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-16  5:32         ` Dave Chinner
@ 2022-06-29  6:33           ` Alli
  2022-06-30  0:40             ` Darrick J. Wong
  0 siblings, 1 reply; 58+ messages in thread
From: Alli @ 2022-06-29  6:33 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Thu, 2022-06-16 at 15:32 +1000, Dave Chinner wrote:
> On Thu, Jun 16, 2022 at 12:08:43PM +1000, Dave Chinner wrote:
> > On Wed, Jun 15, 2022 at 04:40:07PM -0700, Alli wrote:
> > > On Wed, 2022-06-15 at 11:09 +1000, Dave Chinner wrote:
> > > > On Sat, Jun 11, 2022 at 02:41:44AM -0700, Allison Henderson
> > > > wrote:
> > > > > Recent parent pointer testing has exposed a bug in the
> > > > > underlying
> > > > > larp state machine.  A replace operation may remove an old
> > > > > attr
> > > > > before adding the new one, but if it is the only attr in the
> > > > > fork,
> > > > > then the fork is removed.  This later causes a null pointer
> > > > > in
> > > > > xfs_attr_try_sf_addname which expects the fork present.  This
> > > > > patch adds an extra state to create the fork.
> > > > 
> > > > Hmmmm.
> > > > 
> > > > I thought I fixed those problems - in xfs_attr_sf_removename()
> > > > there
> > > > is this code:
> > > > 
> > > >         if (totsize == sizeof(xfs_attr_sf_hdr_t) &&
> > > > xfs_has_attr2(mp)
> > > > &&
> > > >             (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
> > > >             !(args->op_flags & (XFS_DA_OP_ADDNAME |
> > > > XFS_DA_OP_REPLACE))) {
> > > >                 xfs_attr_fork_remove(dp, args->trans);
> > > Hmm, ok, let me shuffle in some traces around there to see where
> > > things
> > > fall off the rails
> > > 
> > > > A replace operation will have XFS_DA_OP_REPLACE set, and so the
> > > > final remove from a sf directory will not remove the attr fork
> > > > in
> > > > this case. There is equivalent checks in the leaf/node remove
> > > > name
> > > > paths to avoid removing the attr fork if the last attr is
> > > > removed
> > > > while the attr fork is in those formats.
> > > > 
> > > > How do you reproduce this issue?
> > > > 
> > > 
> > > Sure, you can apply this kernel set or download it here:
> > > https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs/tree/xfs_new_pptrs__;!!ACWV5N9M2RV99hQ!O7bhq9bR_z2xjlhlwWb78ZXwzigOh6P8V3_EkeL9AHFOpdYdr_irAwUocygT_G8LI-lKDL5_01Df49lTy27a$ 
> > > 
> > > Next you'll need this xfsprogs that has the neccassary updates to
> > > run
> > > parent pointers
> > > https://urldefense.com/v3/__https://github.com/allisonhenderson/xfsprogs/tree/xfsprogs_new_pptrs__;!!ACWV5N9M2RV99hQ!O7bhq9bR_z2xjlhlwWb78ZXwzigOh6P8V3_EkeL9AHFOpdYdr_irAwUocygT_G8LI-lKDL5_01Df4w3Mct8F$ 
> > > 
> > > 
> > > To reproduce the bug, you'll need to apply a quick patch on the
> > > kernel
> > > side:
> > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > index b86188b63897..f279afd43462 100644
> > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > @@ -741,8 +741,8 @@ xfs_attr_set_iter(
> > >  		fallthrough;
> > >  	case XFS_DAS_SF_ADD:
> > >  		if (!args->dp->i_afp) {
> > > -			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> > > -			goto next_state;
> > > +//			attr->xattri_dela_state =
> > > XFS_DAS_CREATE_FORK;
> > > +//			goto next_state;
> > >  		}
> > 
> > Ah, so it's recovery that trips this....
> > 
> > > [  365.290048]  xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
> > > [  365.290423]  xfs_attr_set_iter+0x2f9/0x1510 [xfs]
> > > [  365.291592]  xfs_xattri_finish_update+0x66/0xd0 [xfs]
> > > [  365.292008]  xfs_attr_finish_item+0x43/0x120 [xfs]
> > > [  365.292410]  xfs_defer_finish_noroll+0x3c2/0xcc0 [xfs]
> > > [  365.293196]  __xfs_trans_commit+0x333/0x610 [xfs]
> > > [  365.294401]  xfs_trans_commit+0x10/0x20 [xfs]
> > > [  365.294797]  xlog_finish_defer_ops+0x133/0x270 [xfs]
> > > [  365.296054]  xlog_recover_process_intents+0x1f7/0x3e0 [xfs]
> > 
> > ayup.
> > 
> > > > > Additionally the new state will be used by parent pointers
> > > > > which
> > > > > need to add attributes to newly created inodes that do not
> > > > > yet
> > > > > have a fork.
> > > > 
> > > > We already have the capability of doing that in
> > > > xfs_init_new_inode()
> > > > by passing in init_xattrs == true. So when we are creating a
> > > > new
> > > > inode with parent pointers enabled, we know that we are going
> > > > to be
> > > > creating an xattr on the inode and so we should always set
> > > > init_xattrs in that case.
> > > Hmm, ok.  I'll add some tracing around in there too, if I back
> > > out the
> > > entire first patch, we crash out earlier in recovery path because
> > > no
> > > state is set.  If we enter xfs_attri_item_recover with no fork,
> > > we end
> > > up in the following switch:
> > > 
> > > 
> > >         case XFS_ATTRI_OP_FLAGS_REPLACE:
> > >                 args->value = nv- >value.i_addr;
> > >                 args->valuelen = nv- >value.i_len;
> > >                 args->total = xfs_attr_calc_size(args, &local);
> > >                 if (xfs_inode_hasattr(args- >dp))
> > >                         attr->xattri_dela_state =
> > > xfs_attr_init_replace_state(args);
> > >                 else
> > >                         attr->xattri_dela_state =
> > > xfs_attr_init_add_state(args);
> > >                 break;
> > > 
> > > Which will leave the state unset if the fork is absent.
> > 
> > Yeah, OK, I think this is because we are combining attribute
> > creation with inode creation. When log recovery replays inode core
> > modifications, it replays the inode state into the cluster buffer
> > and writes it. Then when we go to replay the attr intent at the end
> > of recovery, the inode is read from disk via xlog_recover_iget(),
> > but we don't initialise the attr fork because ip->i_forkoff is
> > zero.
> > i.e. it has no attrs at this point.
> > 
> > I suspect that we could catch that in xlog_recover_iget() when it
> > is
> > called from attr recovery. i.e. we detect newly created inodes and
> > initialise the attr fork similar to what we do in
> > xfs_init_new_inode(). I was thinking something like this:
> > 
> > 	if (init_xattrs && xfs_has_attr(mp)) {
> > 		if (!ip->i_forkoff && !ip->i_nextents) {
> > 			ip->i_forkoff = xfs_default_attroffset(ip) >>
> > 3;
> > 			ip->i_afp =
> > xfs_ifork_alloc(XFS_DINODE_FMT_EXTENTS, 0);
> > 		} else {
> > 			ASSERT(ip->i_afp);
> > 		}
> > 	}
> > 
> > Would do the trick, but then I realised that the timing/ordering is
> > very different to runtime: we don't replay the attr intent until
> > the
> > end of log recovery and all the inode changes have been replayed
> > into the inode cluster buffer. That means we could have already
> > replayed a bunch of data fork extent modifications into the inode,
> > and so the default attr offset is almost certainly not a safe thing
> > to be using here. Indeed, there might not be space in the inode for
> > the attr we want to insert and so we might need to convert the data
> > fork to a different format before we run the attr intent replay.
> 
> Ok, so after further thought, I don't think this can happen. If we
> are replaying an attr intent, it means we crashed before the intent
> done was recorded in the log. At this point in time the inode was
> locked and so there could be no racing changes to the inode data
> fork in the log. i.e. because of log item ordering, if the intent
> done is not in the log, none of the future changes that occurred
> after the intent done will be in the log, either. The inode on disk
> will not contain them either because the intent done must be in the
> log before the inode gets unpinned and is able to be written to
> disk.
> 
> Hence if we've got an attr intent to replay, it must be the last
> active modification to that inode that must be replayed, and the
> state of the inode on disk at the time of recovering the attr intent
> should match the state of the inode in memory at the time the attr
> intent was started.
> 
> Hence there isn't a consistency model coherency problem here, and
> that means if there's no attr fork at the time the attr recovery is
> started, it *must* be a newly created inode. If the inode already
> existed and a transaction had to be run to create the attr fork
> (i.e. xfs_bmap_add_attrfork() had to be run) then that transaction
> would have been recovered from the log before attr replay started,
> and so xlog_recovery_iget() should see a non-zero ip->i_forkoff and
> initialise the attr fork correctly.
> 
> But this makes me wonder further. If the attr intent is logged in
> the same transaction as the inode is allocated, then the setting of
> ip->i_forkoff in xfs_init_new_inode() should also be logged in that
> transaction (because XFS_ILOG_CORE is used) and hence be replayed
> into the on-disk inode by recovery before the attr intent recovery
> starts. Hence xlog_recover_iget() should be initialising the attr
> fork through this path:
> 
> xlog_recover_iget
>   xfs_iget
>     xfs_iget_cache_miss
>       xfs_inode_from_disk
> 	if (ip->i_forkoff)
> 	  xfs_iformat_attr_fork()
> 
> This means the newly allocated inode would have an attr fork
> allocated to it, in extent format with zero extents. If we then look
> at xfs_inode_hasattr():
> 
> int
> xfs_inode_hasattr(
>         struct xfs_inode        *ip)
> {
>         if (!XFS_IFORK_Q(ip))
>                 return 0;
>         if (!ip->i_afp)
>                 return 0;
> > > > > >   if (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS &&
> > > > > >       ip->i_afp->if_nextents == 0)
> > > > > >           return 0;
>         return 1;
> }
> 
> It would still say that it has no attrs even though the fork has
> been initialised and we go down the xfs_attr_init_add_state()
> branch.  This does:
> 
> static inline enum xfs_delattr_state
> xfs_attr_init_add_state(struct xfs_da_args *args)
> {
>         /*
>          * When called from the completion of a attr remove to
> determine the
>          * next state, the attribute fork may be null. This can occur
> only occur
>          * on a pure remove, but we grab the next state before we
> check if a
>          * replace operation is being performed. If we are called
> from any other
>          * context, i_afp is guaranteed to exist. Hence if the attr
> fork is
>          * null, we were called from a pure remove operation and so
> we are done.
>          */
> > > > >    if (!args->dp->i_afp)
> > > > >            return XFS_DAS_DONE;
> 
>         args->op_flags |= XFS_DA_OP_ADDNAME;
>         if (xfs_attr_is_shortform(args->dp))
>                 return XFS_DAS_SF_ADD;
>         if (xfs_attr_is_leaf(args->dp))
>                 return XFS_DAS_LEAF_ADD;
>         return XFS_DAS_NODE_ADD;
> }
> 
> Which would return XFS_DAS_DONE if there was no attr fork
> initialised and recovery would be skipped completely. Hence for this
> change to be required to trigger failures:
> 
> > > @@ -741,8 +741,8 @@ xfs_attr_set_iter(
> > >             fallthrough;
> > >     case XFS_DAS_SF_ADD:
> > >             if (!args->dp->i_afp) {
> > > -                   attr->xattri_dela_state =
> > > XFS_DAS_CREATE_FORK;
> > > -                   goto next_state;
> > > +//                 attr->xattri_dela_state =
> > > XFS_DAS_CREATE_FORK;
> > > +//                 goto next_state;
> > >             }
> 
> then it implies the only way we can get here is via a replace
> operation that has removed the attr fork during the remove and we
> hit this on the attr add. Yet, AFAICT, the attr fork does not get
> removed when a replace operation is in progress.
> 
> Maybe there's a new bug introduced in the PP patchset that triggers
> this - I'll do some more looking...
> 
> > > > This should avoid the need for parent pointers to ever need to
> > > > run
> > > > an extra transaction to create the attr fork. Hence, AFAICT,
> > > > this
> > > > new state to handle attr fork creation shouldn't ever be needed
> > > > for
> > > > parent pointers....
> > > > 
> > > > What am I missing?
> > > > 
> > > I hope the description helped?  I'll do some more poking around
> > > too and
> > > post back if I find anything else.
> > 
> > Yup, it most definitely helped. :)
> > 
> > You've pointed out something I had completely missed w.r.t. attr
> > intent replay ordering against replay of data fork modifications.
> > There's definitely an issue here, I think it might be a fundamental
> > issue with the recovery mechanism (and not parent pointers), and I
> > think we'll end up needing  something like this patch to fix it.
> > Let me bounce this around my head for a bit...
> 
> In summary, after further thought this turns out not to be an issue
> at all, so I'm back to "replace doesn't remove the attr fork, so
> how does this happen?"....
Just a follow up here...

So, I think what is happening is we are getting interleaved replays.
When a parent changes, the parent pointer attribute needs to be
updated.  But since this changes the attribute name, it cant be an attr
replace.  So we queue up an attr set and then a remove (in xfs_rename).
The test case sets the inject which starts a replay of a set and
remove.  The set expands the leaf, and bounces out the EAGAIN
into xfs_attri_item_recover.  We then fall into this error handling:

        ret = xfs_xattri_finish_update(attr,
done_item);                        
        if (ret == -EAGAIN)
{                                                   
                /* There's more work to do, so add it to this
transaction */    
                xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr-
>xattri_list);
        }

Which I think is queuing up the rest of the set operation, but on the
other side of the remove operation.  The remove operation removes the
fork, and we get the null pointer when the set operation resumes.

I havnt quite worked out a fix yet, but I'm pretty sure that's the
problem description....  I think what we're going to need is some sort
of defer_push operation or something similar.

Allison

> 
> Cheers,
> 
> Dave.
> 


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

* Re: [PATCH v1 12/17] xfs: remove parent pointers in unlink
  2022-06-11  9:41 ` [PATCH v1 12/17] xfs: remove parent pointers in unlink Allison Henderson
@ 2022-06-29 17:35   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 17:35 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:55AM -0700, Allison Henderson wrote:
> This patch removes the parent pointer attribute during unlink
> 
> [bfoster: rebase, use VFS inode generation]
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t
>            implemented xfs_attr_remove_parent]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c |  2 +-
>  fs/xfs/libxfs/xfs_attr.h |  1 +
>  fs/xfs/xfs_inode.c       | 63 +++++++++++++++++++++++++++++++---------
>  3 files changed, 51 insertions(+), 15 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index f814a9177237..b86188b63897 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -966,7 +966,7 @@ xfs_attr_defer_replace(
>  }
>  
>  /* Removes an attribute for an inode as a deferred operation */
> -static int
> +int
>  xfs_attr_defer_remove(
>  	struct xfs_da_args	*args)
>  {
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 576062e37d11..386dfc8d6053 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -560,6 +560,7 @@ bool xfs_attr_is_leaf(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_defer_add(struct xfs_da_args *args);
> +int xfs_attr_defer_remove(struct xfs_da_args *args);
>  int xfs_attr_set(struct xfs_da_args *args);
>  int xfs_attr_set_iter(struct xfs_attr_intent *attr);
>  int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 41c58df8e568..160f57df6d58 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -2828,16 +2828,27 @@ xfs_iunpin_wait(
>   */
>  int
>  xfs_remove(
> -	xfs_inode_t             *dp,
> -	struct xfs_name		*name,
> -	xfs_inode_t		*ip)
> -{
> -	xfs_mount_t		*mp = dp->i_mount;
> -	xfs_trans_t             *tp = NULL;
> -	int			is_dir = S_ISDIR(VFS_I(ip)->i_mode);
> -	int			dontcare;
> -	int                     error = 0;
> -	uint			resblks;
> +	xfs_inode_t             	*dp,

Please convert these to 'struct xfs_inode	*dp' and fix the
whitespace between the type name and the variable.

> +	struct xfs_name			*name,
> +	xfs_inode_t			*ip)
> +{
> +	xfs_mount_t			*mp = dp->i_mount;
> +	xfs_trans_t             	*tp = NULL;
> +	int				is_dir = S_ISDIR(VFS_I(ip)->i_mode);
> +	int				dontcare;
> +	int                     	error = 0;
> +	uint				resblks;
> +	xfs_dir2_dataptr_t		dir_offset;
> +	struct xfs_parent_name_rec	rec;
> +	struct xfs_da_args		args = {
> +		.dp		= ip,
> +		.geo		= mp->m_attr_geo,
> +		.whichfork	= XFS_ATTR_FORK,
> +		.attr_filter	= XFS_ATTR_PARENT,
> +		.op_flags	= XFS_DA_OP_OKNOENT,
> +		.name		= (const uint8_t *)&rec,
> +		.namelen	= sizeof(rec),
> +	};
>  
>  	trace_xfs_remove(dp, name);
>  
> @@ -2852,6 +2863,12 @@ xfs_remove(
>  	if (error)
>  		goto std_return;
>  
> +	if (xfs_has_larp(mp)) {
> +		error = xfs_attr_grab_log_assist(mp);
> +		if (error)
> +			goto std_return;
> +	}
> +
>  	/*
>  	 * We try to get the real space reservation first, allowing for
>  	 * directory btree deletion(s) implying possible bmap insert(s).  If we
> @@ -2865,10 +2882,10 @@ xfs_remove(
>  	 */
>  	resblks = XFS_REMOVE_SPACE_RES(mp);
>  	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,
> -			&tp, &dontcare, XFS_ILOCK_EXCL);
> +			&tp, &dontcare, 0);
>  	if (error) {
>  		ASSERT(error != -ENOSPC);
> -		goto std_return;
> +		goto drop_incompat;
>  	}
>  
>  	/*
> @@ -2922,12 +2939,22 @@ xfs_remove(
>  	if (error)
>  		goto out_trans_cancel;
>  
> -	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks, NULL);
> +	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks, &dir_offset);
>  	if (error) {
>  		ASSERT(error != -ENOENT);
>  		goto out_trans_cancel;
>  	}
>  
> +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> +		xfs_init_parent_name_rec(&rec, dp, dir_offset);
> +		args.hashval = xfs_da_hashname(args.name, args.namelen);
> +		args.trans = tp;
> +
> +		error = xfs_attr_defer_remove(&args);
> +		if (error)
> +			goto out_trans_cancel;

Why not queue the pptr removal inside xfs_dir_removename?  Is there ever
going to be a case where we want to remove a dirent but not update the
parent pointers?

OH.  Right.  Online repair makes this messy, since it builds all the new
directory structures in an O_TMPFILE directory, without bumping the link
counts of any of the children or changing dotdot entries of child dirs.
Then the contents are swapext'd.

So yes, it's perhaps best to keep those separate....?

--D

> +	}
> +
>  	/*
>  	 * If this is a synchronous mount, make sure that the
>  	 * remove transaction goes to disk before returning to
> @@ -2938,15 +2965,23 @@ xfs_remove(
>  
>  	error = xfs_trans_commit(tp);
>  	if (error)
> -		goto std_return;
> +		goto out_unlock;
>  
>  	if (is_dir && xfs_inode_is_filestream(ip))
>  		xfs_filestream_deassociate(ip);
>  
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	xfs_iunlock(dp, XFS_ILOCK_EXCL);
>  	return 0;
>  
>   out_trans_cancel:
>  	xfs_trans_cancel(tp);
> + out_unlock:
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	xfs_iunlock(dp, XFS_ILOCK_EXCL);
> + drop_incompat:
> +	if (xfs_has_larp(mp))
> +		xlog_drop_incompat_feat(mp->m_log);
>   std_return:
>  	return error;
>  }
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 13/17] xfs: Add parent pointers to rename
  2022-06-11  9:41 ` [PATCH v1 13/17] xfs: Add parent pointers to rename Allison Henderson
@ 2022-06-29 18:02   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:02 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:56AM -0700, Allison Henderson wrote:
> This patch removes the old parent pointer attribute during the rename
> operation, and re-adds the updated parent pointer.  In the case of
> xfs_cross_rename, we modify the routine not to roll the transaction just
> yet.  We will do this after the parent pointer is added in the calling
> xfs_rename function.
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/xfs_inode.c | 137 +++++++++++++++++++++++++++++++++------------
>  1 file changed, 101 insertions(+), 36 deletions(-)
> 
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 160f57df6d58..4566613c6a71 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -3153,7 +3153,7 @@ xfs_cross_rename(
>  	}
>  	xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
>  	xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
> -	return xfs_finish_rename(tp);
> +	return 0;
>  
>  out_trans_abort:
>  	xfs_trans_cancel(tp);
> @@ -3200,26 +3200,52 @@ xfs_rename_alloc_whiteout(
>   */
>  int
>  xfs_rename(
> -	struct user_namespace	*mnt_userns,
> -	struct xfs_inode	*src_dp,
> -	struct xfs_name		*src_name,
> -	struct xfs_inode	*src_ip,
> -	struct xfs_inode	*target_dp,
> -	struct xfs_name		*target_name,
> -	struct xfs_inode	*target_ip,
> -	unsigned int		flags)
> -{
> -	struct xfs_mount	*mp = src_dp->i_mount;
> -	struct xfs_trans	*tp;
> -	struct xfs_inode	*wip = NULL;		/* whiteout inode */
> -	struct xfs_inode	*inodes[__XFS_SORT_INODES];
> -	int			i;
> -	int			num_inodes = __XFS_SORT_INODES;
> -	bool			new_parent = (src_dp != target_dp);
> -	bool			src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
> -	int			spaceres;
> -	bool			retried = false;
> -	int			error, nospace_error = 0;
> +	struct user_namespace		*mnt_userns,
> +	struct xfs_inode		*src_dp,
> +	struct xfs_name			*src_name,
> +	struct xfs_inode		*src_ip,
> +	struct xfs_inode		*target_dp,
> +	struct xfs_name			*target_name,
> +	struct xfs_inode		*target_ip,
> +	unsigned int			flags)
> +{
> +	struct xfs_mount		*mp = src_dp->i_mount;
> +	struct xfs_trans		*tp;
> +	struct xfs_inode		*wip = NULL;		/* whiteout inode */
> +	struct xfs_inode		*inodes[__XFS_SORT_INODES];
> +	int				i;
> +	int				num_inodes = __XFS_SORT_INODES;
> +	bool				new_parent = (src_dp != target_dp);
> +	bool				src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
> +	int				spaceres;
> +	bool				retried = false;
> +	int				error, nospace_error = 0;
> +	struct xfs_parent_name_rec	new_rec;
> +	struct xfs_parent_name_rec	old_rec;
> +	xfs_dir2_dataptr_t		new_diroffset;
> +	xfs_dir2_dataptr_t		old_diroffset;
> +	struct xfs_da_args		new_args = {
> +		.dp		= src_ip,
> +		.geo		= mp->m_attr_geo,
> +		.whichfork	= XFS_ATTR_FORK,
> +		.attr_filter	= XFS_ATTR_PARENT,
> +		.op_flags	= XFS_DA_OP_OKNOENT,
> +		.name		= (const uint8_t *)&new_rec,
> +		.namelen	= sizeof(new_rec),
> +		.value		= (void *)target_name->name,
> +		.valuelen	= target_name->len,
> +	};
> +	struct xfs_da_args		old_args = {
> +		.dp		= src_ip,
> +		.geo		= mp->m_attr_geo,
> +		.whichfork	= XFS_ATTR_FORK,
> +		.attr_filter	= XFS_ATTR_PARENT,
> +		.op_flags	= XFS_DA_OP_OKNOENT,
> +		.name		= (const uint8_t *)&old_rec,
> +		.namelen	= sizeof(old_rec),
> +		.value		= NULL,
> +		.valuelen	= 0,
> +	};
>  
>  	trace_xfs_rename(src_dp, target_dp, src_name, target_name);
>  
> @@ -3242,6 +3268,11 @@ xfs_rename(
>  
>  	xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
>  				inodes, &num_inodes);
> +	if (xfs_has_larp(mp)) {
> +		error = xfs_attr_grab_log_assist(mp);
> +		if (error)
> +			goto out_release_wip;
> +	}
>  
>  retry:
>  	nospace_error = 0;
> @@ -3254,7 +3285,7 @@ xfs_rename(
>  				&tp);
>  	}
>  	if (error)
> -		goto out_release_wip;
> +		goto drop_incompat;
>  
>  	/*
>  	 * Attach the dquots to the inodes
> @@ -3276,14 +3307,14 @@ xfs_rename(
>  	 * we can rely on either trans_commit or trans_cancel to unlock
>  	 * them.
>  	 */
> -	xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, src_dp, 0);
>  	if (new_parent)
> -		xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL);
> -	xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);
> +		xfs_trans_ijoin(tp, target_dp, 0);
> +	xfs_trans_ijoin(tp, src_ip, 0);
>  	if (target_ip)
> -		xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);
> +		xfs_trans_ijoin(tp, target_ip, 0);
>  	if (wip)
> -		xfs_trans_ijoin(tp, wip, XFS_ILOCK_EXCL);
> +		xfs_trans_ijoin(tp, wip, 0);
>  
>  	/*
>  	 * If we are using project inheritance, we only allow renames
> @@ -3293,15 +3324,16 @@ xfs_rename(
>  	if (unlikely((target_dp->i_diflags & XFS_DIFLAG_PROJINHERIT) &&
>  		     target_dp->i_projid != src_ip->i_projid)) {
>  		error = -EXDEV;
> -		goto out_trans_cancel;
> +		goto out_unlock;
>  	}
>  
>  	/* RENAME_EXCHANGE is unique from here on. */
> -	if (flags & RENAME_EXCHANGE)
> -		return xfs_cross_rename(tp, src_dp, src_name, src_ip,
> +	if (flags & RENAME_EXCHANGE) {
> +		error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
>  					target_dp, target_name, target_ip,
>  					spaceres);
> -
> +		goto out_pptr;
> +	}
>  	/*
>  	 * Try to reserve quota to handle an expansion of the target directory.
>  	 * We'll allow the rename to continue in reservationless mode if we hit
> @@ -3415,7 +3447,7 @@ xfs_rename(
>  		 * to account for the ".." reference from the new entry.
>  		 */
>  		error = xfs_dir_createname(tp, target_dp, target_name,
> -					   src_ip->i_ino, spaceres, NULL);
> +					   src_ip->i_ino, spaceres, &new_diroffset);
>  		if (error)
>  			goto out_trans_cancel;
>  
> @@ -3436,7 +3468,7 @@ xfs_rename(
>  		 * name at the destination directory, remove it first.
>  		 */
>  		error = xfs_dir_replace(tp, target_dp, target_name,
> -					src_ip->i_ino, spaceres, NULL);
> +					src_ip->i_ino, spaceres, &new_diroffset);
>  		if (error)
>  			goto out_trans_cancel;
>  
> @@ -3470,7 +3502,7 @@ xfs_rename(
>  		 * directory.
>  		 */
>  		error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
> -					target_dp->i_ino, spaceres, NULL);
> +					target_dp->i_ino, spaceres, &new_diroffset);
>  		ASSERT(error != -EEXIST);
>  		if (error)
>  			goto out_trans_cancel;
> @@ -3509,26 +3541,59 @@ xfs_rename(
>  	 */
>  	if (wip)
>  		error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino,
> -					spaceres, NULL);
> +					spaceres, &old_diroffset);
>  	else
>  		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
> -					   spaceres, NULL);
> +					   spaceres, &old_diroffset);
>  
>  	if (error)
>  		goto out_trans_cancel;
>  
> +out_pptr:
> +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> +		new_args.trans	= tp;
> +		xfs_init_parent_name_rec(&new_rec, target_dp, new_diroffset);
> +		new_args.hashval = xfs_da_hashname(new_args.name,
> +						   new_args.namelen);
> +		error =  xfs_attr_defer_add(&new_args);
> +		if (error)
> +			goto out_trans_cancel;
> +
> +		old_args.trans	= tp;
> +		xfs_init_parent_name_rec(&old_rec, src_dp, old_diroffset);
> +		old_args.hashval = xfs_da_hashname(old_args.name,
> +						   old_args.namelen);
> +		error = xfs_attr_defer_remove(&old_args);
> +		if (error)
> +			goto out_trans_cancel;

Isn't this missing a xfs_attr_defer_remove call for target_ip if the
rename causes target_ip to be unlinked from target_dp?

If you had pptr helper functions, it would be easier to validate that
the code sequence is correct if we could see something like:

	if (want to add something) {
		xfs_dir_createname(..., &newoffset);
		xfs_dir_createpptr(..., newoffset);
	} else if (want to remove something) {
		xfs_dir_removename(..., &oldoffset);
		xfs_dir_removepptr(..., oldoffset);
	} else if (replacing something) {
		xfs_dir_replace(..., &oldoffset, &newoffset);
		xfs_dir_replacepptr(..., oldoffset, newoffset);
	}

The point being that the function call that updates the directory should
be right before the function call that schedules the corresponding pptr
xattr update.

--D

> +	}
> +
>  	xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
>  	xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
>  	if (new_parent)
>  		xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
>  
>  	error = xfs_finish_rename(tp);
> +
> +out_unlock:
>  	if (wip)
>  		xfs_irele(wip);
> +	if (wip)
> +		xfs_iunlock(wip, XFS_ILOCK_EXCL);
> +	if (target_ip)
> +		xfs_iunlock(target_ip, XFS_ILOCK_EXCL);
> +	xfs_iunlock(src_ip, XFS_ILOCK_EXCL);
> +	if (new_parent)
> +		xfs_iunlock(target_dp, XFS_ILOCK_EXCL);
> +	xfs_iunlock(src_dp, XFS_ILOCK_EXCL);
> +
>  	return error;
>  
>  out_trans_cancel:
>  	xfs_trans_cancel(tp);
> +drop_incompat:
> +	if (xfs_has_larp(mp))
> +		xlog_drop_incompat_feat(mp->m_log);
>  out_release_wip:
>  	if (wip)
>  		xfs_irele(wip);
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 11/17] xfs: add parent attributes to link
  2022-06-18  0:32     ` Alli
@ 2022-06-29 18:09       ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:09 UTC (permalink / raw)
  To: Alli; +Cc: Dave Chinner, linux-xfs

On Fri, Jun 17, 2022 at 05:32:47PM -0700, Alli wrote:
> On Fri, 2022-06-17 at 08:39 +1000, Dave Chinner wrote:
> > On Sat, Jun 11, 2022 at 02:41:54AM -0700, Allison Henderson wrote:
> > > This patch modifies xfs_link to add a parent pointer to the inode.
> > > 
> > > [bfoster: rebase, use VFS inode fields, fix xfs_bmap_finish()
> > > usage]
> > > [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
> > >            fixed null pointer bugs]
> > > 
> > > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > ---
> > >  fs/xfs/xfs_inode.c | 78 ++++++++++++++++++++++++++++++++++++----
> > > ------
> > >  fs/xfs/xfs_trans.c |  7 +++--
> > >  fs/xfs/xfs_trans.h |  2 +-
> > >  3 files changed, 67 insertions(+), 20 deletions(-)
> > > 
> > > diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> > > index 6b1e4cb11b5c..41c58df8e568 100644
> > > --- a/fs/xfs/xfs_inode.c
> > > +++ b/fs/xfs/xfs_inode.c
> > > @@ -1254,14 +1254,28 @@ xfs_create_tmpfile(
> > >  
> > >  int
> > >  xfs_link(
> > > -	xfs_inode_t		*tdp,
> > > -	xfs_inode_t		*sip,
> > > -	struct xfs_name		*target_name)
> > > -{
> > > -	xfs_mount_t		*mp = tdp->i_mount;
> > > -	xfs_trans_t		*tp;
> > > -	int			error, nospace_error = 0;
> > > -	int			resblks;
> > > +	xfs_inode_t			*tdp,
> > > +	xfs_inode_t			*sip,
> > > +	struct xfs_name			*target_name)
> > > +{
> > > +	xfs_mount_t			*mp = tdp->i_mount;
> > > +	xfs_trans_t			*tp;
> > > +	int				error, nospace_error = 0;
> > > +	int				resblks;
> > > +	struct xfs_parent_name_rec	rec;
> > > +	xfs_dir2_dataptr_t		diroffset;
> > > +
> > > +	struct xfs_da_args		args = {
> > > +		.dp		= sip,
> > > +		.geo		= mp->m_attr_geo,
> > > +		.whichfork	= XFS_ATTR_FORK,
> > > +		.attr_filter	= XFS_ATTR_PARENT,
> > > +		.op_flags	= XFS_DA_OP_OKNOENT,
> > > +		.name		= (const uint8_t *)&rec,
> > > +		.namelen	= sizeof(rec),
> > > +		.value		= (void *)target_name->name,
> > > +		.valuelen	= target_name->len,
> > > +	};
> > 
> > Now that I've had a bit of a think about this, this pattern of
> > placing the rec on the stack and then using it as a buffer that is
> > then accessed in xfs_tran_commit() processing feels like a landmine.
> > 
> > That is, we pass transaction contexts around functions as they are
> > largely independent constructs, but adding this stack variable to
> > the defer ops attached to the transaction means that the transaction
> > cannot be passed back to a caller for it to be committed - that will
> > corrupt the stack buffer and hence silently corrupt the parent attr
> > that is going to be logged when the transaction is finally committed.
> > 
> > Hence I think this needs to be wrapped up as a dynamically allocated
> > structure that is freed when the defer ops are done with it. e.g.
> > 
> > struct xfs_parent_defer {
> > 	struct xfs_parent_name_rec	rec;
> > 	xfs_dir2_dataptr_t		diroffset;
> > 	struct xfs_da_args		args;
> > };
> > 
> > and then here:
> > 
> > >  
> > >  	trace_xfs_link(tdp, target_name);
> > >  
> > > @@ -1278,11 +1292,17 @@ xfs_link(
> > >  	if (error)
> > >  		goto std_return;
> > >  
> > > +	if (xfs_has_larp(mp)) {
> > > +		error = xfs_attr_grab_log_assist(mp);
> > > +		if (error)
> > > +			goto std_return;
> > > +	}
> > 
> > 	struct xfs_parent_defer		*parent = NULL;
> > .....
> > 
> > 	error = xfs_parent_init(mp, target_name, &parent);
> > 	if (error)
> > 		goto std_return;
> > 
> > and xfs_parent_init() looks something like this:
> > 
> > int
> > xfs_parent_init(
> > 	.....
> > 	struct xfs_parent_defer		**parentp)
> > {
> > 	struct xfs_parent_defer		*parent;
> > 
> > 	if (!xfs_has_parent_pointers(mp))
> > 		return 0;
> > 
> > 	error = xfs_attr_grab_log_assist(mp);

I've wondered if filesystems with parent pointers should just turn on
XFS_SB_FEAT_INCOMPAT_LOG_XATTRS at mkfs time and leave it set forever?
It would save a lot of log and sb update overhead, since turning on a
log incompat bit requires a synchronous primary sb write.  I /think/
that's doable if we add a "never turn off these log incompat bits" mask
to xfs_mount and update xfs_clear_incompat_log_features to leave certain
things set.

> > 	if (error)
> > 		return error;
> > 
> > 	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
> > 	if (!parent)
> > 		return -ENOMEM;
> > 
> > 	/* init parent da_args */
> > 
> > 	*parentp = parent;
> > 	return 0;
> > }
> > 
> > With that in place, we then can wrap all this up:
> > 
> > >  
> > > +	/*
> > > +	 * If we have parent pointers, we now need to add the parent
> > > record to
> > > +	 * the attribute fork of the inode. If this is the initial
> > > parent
> > > +	 * attribute, we need to create it correctly, otherwise we can
> > > just add
> > > +	 * the parent to the inode.
> > > +	 */
> > > +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> > > +		args.trans = tp;
> > > +		xfs_init_parent_name_rec(&rec, tdp, diroffset);
> > > +		args.hashval = xfs_da_hashname(args.name,
> > > +					       args.namelen);
> > > +		error = xfs_attr_defer_add(&args);
> > > +		if (error)
> > > +			goto out_defer_cancel;
> > > +	}
> > 
> > with:
> > 
> > 	if (parent) {
> > 		error = xfs_parent_defer_add(tp, tdp, parent,
> > diroffset);
> > 		if (error)
> > 			goto out_defer_cancel;
> > 	}
> > 
> > and implement it something like:
> > 
> > int
> > xfs_parent_defer_add(

Suggestion: Call this xfs_dir_createpptr?

Then we can easily verify that we're doing a directory operation and
immediately scheduling the parent pointer update:

	error = xfs_dir_createname(tp, dp, name, ip->i_ino, spaceres,
			&newoff);

	error = xfs_dir_createpptr(tp, dp, parent, newoff);

--D

> > 	struct xfs_trans	*tp,
> > 	struct xfs_inode	*ip,
> > 	struct xfs_parent_defer	*parent,
> > 	xfs_dir2_dataptr_t	diroffset)
> > {
> > 	struct xfs_da_args	*args = &parent->args;
> > 
> > 	xfs_init_parent_name_rec(&parent->rec, ip, diroffset)
> > 	args->trans = tp;
> > 	args->hashval = xfs_da_hashname(args->name, args->namelen);
> > 	return xfs_attr_defer_add(args);
> > }
> > 
> > 
> > > +
> > >  	/*
> > >  	 * If this is a synchronous mount, make sure that the
> > >  	 * link transaction goes to disk before returning to
> > > @@ -1331,11 +1367,21 @@ xfs_link(
> > >  	if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))
> > >  		xfs_trans_set_sync(tp);
> > >  
> > > -	return xfs_trans_commit(tp);
> > > +	error = xfs_trans_commit(tp);
> > > +	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
> > > +	xfs_iunlock(sip, XFS_ILOCK_EXCL);
> > 
> > with a xfs_parent_free(parent) added here now that we are done with
> > the parent update.
> > 
> > > +	return error;
> > >  
> > > - error_return:
> > > +out_defer_cancel:
> > > +	xfs_defer_cancel(tp);
> > > +error_return:
> > >  	xfs_trans_cancel(tp);
> > > - std_return:
> > > +	xfs_iunlock(tdp, XFS_ILOCK_EXCL);
> > > +	xfs_iunlock(sip, XFS_ILOCK_EXCL);
> > > +drop_incompat:
> > > +	if (xfs_has_larp(mp))
> > > +		xlog_drop_incompat_feat(mp->m_log);
> > 
> > And this can be replace with  xfs_parent_cancel(mp, parent); that
> > drops the log incompat featuer and frees the parent if it is not
> > null.
> 
> Sure, that sounds reasonable.  Let me punch it up and see how it does
> int the tests.
> 
> > 
> > > +std_return:
> > >  	if (error == -ENOSPC && nospace_error)
> > >  		error = nospace_error;
> > >  	return error;
> > > @@ -2819,7 +2865,7 @@ xfs_remove(
> > >  	 */
> > >  	resblks = XFS_REMOVE_SPACE_RES(mp);
> > >  	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip,
> > > &resblks,
> > > -			&tp, &dontcare);
> > > +			&tp, &dontcare, XFS_ILOCK_EXCL);
> > 
> > So you add this flag here so that link and remove can do different
> > things in xfs_trans_alloc_dir(), but in the very next patch
> > this gets changed to zero, so both callers only pass 0 to the
> > function.
> > 
> > Ideally there should be a patch prior to this one that converts
> > the locking and joining of both link and remove to use external
> > inode locking in a single patch, similar to the change in the second
> > patch that changed the inode locking around xfs_init_new_inode() to
> > require manual unlock. Then all the locking mods in this and the
> > next patch go away, leaving just the parent pointer mods in this
> > patch....
> Sure, I can do it that way too.
> 
> Thanks for the reviews!
> Allison
> 
> > 
> > Cheers,
> > 
> > Dave.
> 

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

* Re: [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5.
  2022-06-20  0:21       ` Dave Chinner
@ 2022-06-29 18:16         ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:16 UTC (permalink / raw)
  To: Dave Chinner; +Cc: Alli, linux-xfs

On Mon, Jun 20, 2022 at 10:21:26AM +1000, Dave Chinner wrote:
> On Fri, Jun 17, 2022 at 05:32:36PM -0700, Alli wrote:
> > On Thu, 2022-06-16 at 16:03 +1000, Dave Chinner wrote:
> > > On Sat, Jun 11, 2022 at 02:41:57AM -0700, Allison Henderson wrote:
> > > > [dchinner: forward ported and cleaned up]
> > > > [achender: rebased and added parent pointer attribute to
> > > >            compatible attributes mask]
> > > > 
> > > > Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> > > > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > > Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > > ---
> > > >  fs/xfs/libxfs/xfs_format.h | 14 +++++++++-----
> > > >  fs/xfs/libxfs/xfs_fs.h     |  1 +
> > > >  fs/xfs/libxfs/xfs_sb.c     |  2 ++
> > > >  fs/xfs/xfs_super.c         |  4 ++++
> > > >  4 files changed, 16 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/fs/xfs/libxfs/xfs_format.h
> > > > b/fs/xfs/libxfs/xfs_format.h
> > > > index 96976497306c..e85d6b643622 100644
> > > > --- a/fs/xfs/libxfs/xfs_format.h
> > > > +++ b/fs/xfs/libxfs/xfs_format.h
> > > > @@ -83,6 +83,7 @@ struct xfs_ifork;
> > > >  #define	XFS_SB_VERSION2_OKBITS		\
> > > >  	(XFS_SB_VERSION2_LAZYSBCOUNTBIT	| \
> > > >  	 XFS_SB_VERSION2_ATTR2BIT	| \
> > > > +	 XFS_SB_VERSION2_PARENTBIT	| \
> > > >  	 XFS_SB_VERSION2_PROJID32BIT	| \
> > > >  	 XFS_SB_VERSION2_FTYPE)
> > > 
> > > No need for a v4 filesystem format feature bit - this is v4 only.
> > Ok, I ended up having to add this in the rebase or we get an "SB
> > validate failed".  I think it has to go over in
> > xfs_sb_validate_v5_features next to the manual crc bit check.  Will
> > move
> 
> Ah, I meant that parent pointers are a v5 only feature, and so we
> don't need a "v4 only" feature bit for it. As it is, we can't use
> that specific bit because SGI shipped a version of parent pointers
> on v4 filesystems on IRIX under that feature bit that was broken and
> subsequently recalled and killed. Essentially, that means
> XFS_SB_VERSION2_PARENTBIT is blacklisted and cannot ever be used by
> upstream kernels.
> 
> > > > @@ -353,11 +354,13 @@ xfs_sb_has_compat_feature(
> > > >  #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)		/*
> > > > reverse map btree */
> > > >  #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/*
> > > > reflinked files */
> > > >  #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/*
> > > > inobt block counts */
> > > > +#define XFS_SB_FEAT_RO_COMPAT_PARENT	(1 << 4)		/*
> > > > parent inode ptr */
> > > >  #define XFS_SB_FEAT_RO_COMPAT_ALL \
> > > > -		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
> > > > -		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
> > > > -		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
> > > > -		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
> > > > +		(XFS_SB_FEAT_RO_COMPAT_FINOBT  | \
> > > > +		 XFS_SB_FEAT_RO_COMPAT_RMAPBT  | \
> > > > +		 XFS_SB_FEAT_RO_COMPAT_REFLINK | \
> > > > +		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT| \
> > > > +		 XFS_SB_FEAT_RO_COMPAT_PARENT)
> > > 
> > > I'm not sure this is a RO Compat feature - we added an attribute
> > > namespace flag on disk, and the older kernels do not know about
> > > that (i.e. we changed XFS_ATTR_NSP_ONDISK_MASK). This may result in
> > > parent pointer attrs being exposed as user attrs rather than being
> > > hidden, or maybe parent pointer attrs being seen as corrupt because
> > > they have a flag that isn't defined set, etc.
> > > 
> > > Hence I'm not sure that this classification is correct.
> > 
> > Gosh, I'm sure there was a reason we did this, but what ever it was
> > goes all the way back in the first re-appearance of the set back in
> > 2018 and I just cant remember the discussion at the time.  It may have
> > just been done to get mkfs working and we just never got to reviewing
> > it.
> > 
> > Should we drop it and just use XFS_SB_VERSION2_PARENTBIT?
> 
> No, it needs to be a v5 feature bit - create a v5 parent pointer
> filesystem, create some files on it, and then go an mount it on a
> kernel that doesn't have PP support. If you can see the parent
> pointer attributes from userspace as "user.<binary garbage>"
> attributes, then we need to use an INCOMPAT feature bit rather than
> a RO_COMPAT bit.

Agreed, this needs to be a v5 feature bit.

If the current kernel ignores xattr leaf entries with namespaces it
doesn't know about, *then* this could be an rocompat feature.
Otherwise, it has to be incompat.

As it is, I think the attr list functions will return *every* xattr
regardless of namespace, so I think it's not safe to let old kernels
mount pptr filesystems even in readonly mode.

--D

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com

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

* Re: [PATCH v1 02/17] xfs: Hold inode locks in xfs_ialloc
  2022-06-11  9:41 ` [PATCH v1 02/17] xfs: Hold inode locks in xfs_ialloc Allison Henderson
@ 2022-06-29 18:28   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:28 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:45AM -0700, Allison Henderson wrote:
> Modify xfs_ialloc to hold locks after return.  Caller will be
> responsible for manual unlock.  We will need this later to hold locks
> across parent pointer operations
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

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

--D

> ---
>  fs/xfs/xfs_inode.c   | 6 +++++-
>  fs/xfs/xfs_qm.c      | 4 +++-
>  fs/xfs/xfs_symlink.c | 3 +++
>  3 files changed, 11 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 52d6f2c7d58b..23b93403a330 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -787,6 +787,8 @@ xfs_inode_inherit_flags2(
>  /*
>   * Initialise a newly allocated inode and return the in-core inode to the
>   * caller locked exclusively.
> + *
> + * Caller is responsible for unlocking the inode manually upon return
>   */
>  int
>  xfs_init_new_inode(
> @@ -913,7 +915,7 @@ xfs_init_new_inode(
>  	/*
>  	 * Log the new values stuffed into the inode.
>  	 */
> -	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, ip, 0);
>  	xfs_trans_log_inode(tp, ip, flags);
>  
>  	/* now that we have an i_mode we can setup the inode structure */
> @@ -1090,6 +1092,7 @@ xfs_create(
>  	xfs_qm_dqrele(pdqp);
>  
>  	*ipp = ip;
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
>  	return 0;
>  
>   out_trans_cancel:
> @@ -1186,6 +1189,7 @@ xfs_create_tmpfile(
>  	xfs_qm_dqrele(pdqp);
>  
>  	*ipp = ip;
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
>  	return 0;
>  
>   out_trans_cancel:
> diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
> index abf08bbf34a9..fa8321f74c13 100644
> --- a/fs/xfs/xfs_qm.c
> +++ b/fs/xfs/xfs_qm.c
> @@ -817,8 +817,10 @@ xfs_qm_qino_alloc(
>  		ASSERT(xfs_is_shutdown(mp));
>  		xfs_alert(mp, "%s failed (error %d)!", __func__, error);
>  	}
> -	if (need_alloc)
> +	if (need_alloc) {
>  		xfs_finish_inode_setup(*ipp);
> +		xfs_iunlock(*ipp, XFS_ILOCK_EXCL);
> +	}
>  	return error;
>  }
>  
> diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
> index 4145ba872547..18f71fc90dd0 100644
> --- a/fs/xfs/xfs_symlink.c
> +++ b/fs/xfs/xfs_symlink.c
> @@ -337,6 +337,7 @@ xfs_symlink(
>  	xfs_qm_dqrele(pdqp);
>  
>  	*ipp = ip;
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
>  	return 0;
>  
>  out_trans_cancel:
> @@ -358,6 +359,8 @@ xfs_symlink(
>  
>  	if (unlock_dp_on_error)
>  		xfs_iunlock(dp, XFS_ILOCK_EXCL);
> +	if (ip)
> +		xfs_iunlock(ip, XFS_ILOCK_EXCL);
>  	return error;
>  }
>  
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 03/17] xfs: get directory offset when adding directory name
  2022-06-11  9:41 ` [PATCH v1 03/17] xfs: get directory offset when adding directory name Allison Henderson
@ 2022-06-29 18:29   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:29 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:46AM -0700, Allison Henderson wrote:
> Return the directory offset information when adding an entry to the
> directory.
> 
> This offset will be used as the parent pointer offset in xfs_create,
> xfs_symlink, xfs_link and xfs_rename.
> 
> [dchinner: forward ported and cleaned up]
> [dchinner: no s-o-b from Mark]
> [bfoster: rebased, use args->geo in dir code]
> [achender: rebased, chaged __uint32_t to xfs_dir2_dataptr_t]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

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

--D

> ---
>  fs/xfs/libxfs/xfs_da_btree.h   | 1 +
>  fs/xfs/libxfs/xfs_dir2.c       | 9 +++++++--
>  fs/xfs/libxfs/xfs_dir2.h       | 2 +-
>  fs/xfs/libxfs/xfs_dir2_block.c | 1 +
>  fs/xfs/libxfs/xfs_dir2_leaf.c  | 2 ++
>  fs/xfs/libxfs/xfs_dir2_node.c  | 2 ++
>  fs/xfs/libxfs/xfs_dir2_sf.c    | 2 ++
>  fs/xfs/xfs_inode.c             | 6 +++---
>  fs/xfs/xfs_symlink.c           | 3 ++-
>  9 files changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h
> index d33b7686a0b3..e07eeecbe8a9 100644
> --- a/fs/xfs/libxfs/xfs_da_btree.h
> +++ b/fs/xfs/libxfs/xfs_da_btree.h
> @@ -79,6 +79,7 @@ typedef struct xfs_da_args {
>  	int		rmtvaluelen2;	/* remote attr value length in bytes */
>  	uint32_t	op_flags;	/* operation flags */
>  	enum xfs_dacmp	cmpresult;	/* name compare result for lookups */
> +	xfs_dir2_dataptr_t offset;	/* OUT: offset in directory */
>  } xfs_da_args_t;
>  
>  /*
> diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
> index 3cd51fa3837b..f7f7fa79593f 100644
> --- a/fs/xfs/libxfs/xfs_dir2.c
> +++ b/fs/xfs/libxfs/xfs_dir2.c
> @@ -257,7 +257,8 @@ xfs_dir_createname(
>  	struct xfs_inode	*dp,
>  	const struct xfs_name	*name,
>  	xfs_ino_t		inum,		/* new entry inode number */
> -	xfs_extlen_t		total)		/* bmap's total block count */
> +	xfs_extlen_t		total,		/* bmap's total block count */
> +	xfs_dir2_dataptr_t	*offset)	/* OUT entry's dir offset */
>  {
>  	struct xfs_da_args	*args;
>  	int			rval;
> @@ -312,6 +313,10 @@ xfs_dir_createname(
>  		rval = xfs_dir2_node_addname(args);
>  
>  out_free:
> +	/* return the location that this entry was place in the parent inode */
> +	if (offset)
> +		*offset = args->offset;
> +
>  	kmem_free(args);
>  	return rval;
>  }
> @@ -550,7 +555,7 @@ xfs_dir_canenter(
>  	xfs_inode_t	*dp,
>  	struct xfs_name	*name)		/* name of entry to add */
>  {
> -	return xfs_dir_createname(tp, dp, name, 0, 0);
> +	return xfs_dir_createname(tp, dp, name, 0, 0, NULL);
>  }
>  
>  /*
> diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> index b6df3c34b26a..4d1c2570b833 100644
> --- a/fs/xfs/libxfs/xfs_dir2.h
> +++ b/fs/xfs/libxfs/xfs_dir2.h
> @@ -40,7 +40,7 @@ extern int xfs_dir_init(struct xfs_trans *tp, struct xfs_inode *dp,
>  				struct xfs_inode *pdp);
>  extern int xfs_dir_createname(struct xfs_trans *tp, struct xfs_inode *dp,
>  				const struct xfs_name *name, xfs_ino_t inum,
> -				xfs_extlen_t tot);
> +				xfs_extlen_t tot, xfs_dir2_dataptr_t *offset);
>  extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
>  				const struct xfs_name *name, xfs_ino_t *inum,
>  				struct xfs_name *ci_name);
> diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> index df0869bba275..85869f604960 100644
> --- a/fs/xfs/libxfs/xfs_dir2_block.c
> +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> @@ -573,6 +573,7 @@ xfs_dir2_block_addname(
>  	xfs_dir2_data_put_ftype(dp->i_mount, dep, args->filetype);
>  	tagp = xfs_dir2_data_entry_tag_p(dp->i_mount, dep);
>  	*tagp = cpu_to_be16((char *)dep - (char *)hdr);
> +	args->offset = xfs_dir2_byte_to_dataptr((char *)dep - (char *)hdr);
>  	/*
>  	 * Clean up the bestfree array and log the header, tail, and entry.
>  	 */
> diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
> index d9b66306a9a7..bd0c2f963545 100644
> --- a/fs/xfs/libxfs/xfs_dir2_leaf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
> @@ -865,6 +865,8 @@ xfs_dir2_leaf_addname(
>  	xfs_dir2_data_put_ftype(dp->i_mount, dep, args->filetype);
>  	tagp = xfs_dir2_data_entry_tag_p(dp->i_mount, dep);
>  	*tagp = cpu_to_be16((char *)dep - (char *)hdr);
> +	args->offset = xfs_dir2_db_off_to_dataptr(args->geo, use_block,
> +						(char *)dep - (char *)hdr);
>  	/*
>  	 * Need to scan fix up the bestfree table.
>  	 */
> diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> index 7a03aeb9f4c9..5a9513c036b8 100644
> --- a/fs/xfs/libxfs/xfs_dir2_node.c
> +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> @@ -1974,6 +1974,8 @@ xfs_dir2_node_addname_int(
>  	xfs_dir2_data_put_ftype(dp->i_mount, dep, args->filetype);
>  	tagp = xfs_dir2_data_entry_tag_p(dp->i_mount, dep);
>  	*tagp = cpu_to_be16((char *)dep - (char *)hdr);
> +	args->offset = xfs_dir2_db_off_to_dataptr(args->geo, dbno,
> +						  (char *)dep - (char *)hdr);
>  	xfs_dir2_data_log_entry(args, dbp, dep);
>  
>  	/* Rescan the freespace and log the data block if needed. */
> diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c
> index 5a97a87eaa20..c6c06e8ab54b 100644
> --- a/fs/xfs/libxfs/xfs_dir2_sf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_sf.c
> @@ -485,6 +485,7 @@ xfs_dir2_sf_addname_easy(
>  	memcpy(sfep->name, args->name, sfep->namelen);
>  	xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber);
>  	xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
> +	args->offset = xfs_dir2_byte_to_dataptr(offset);
>  
>  	/*
>  	 * Update the header and inode.
> @@ -575,6 +576,7 @@ xfs_dir2_sf_addname_hard(
>  	memcpy(sfep->name, args->name, sfep->namelen);
>  	xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber);
>  	xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
> +	args->offset = xfs_dir2_byte_to_dataptr(offset);
>  	sfp->count++;
>  	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
>  		sfp->i8count++;
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 23b93403a330..05be02f6f62b 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -1052,7 +1052,7 @@ xfs_create(
>  	unlock_dp_on_error = false;
>  
>  	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
> -					resblks - XFS_IALLOC_SPACE_RES(mp));
> +				   resblks - XFS_IALLOC_SPACE_RES(mp), NULL);
>  	if (error) {
>  		ASSERT(error != -ENOSPC);
>  		goto out_trans_cancel;
> @@ -1275,7 +1275,7 @@ xfs_link(
>  	}
>  
>  	error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
> -				   resblks);
> +				   resblks, NULL);
>  	if (error)
>  		goto error_return;
>  	xfs_trans_ichgtime(tp, tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
> @@ -3294,7 +3294,7 @@ xfs_rename(
>  		 * to account for the ".." reference from the new entry.
>  		 */
>  		error = xfs_dir_createname(tp, target_dp, target_name,
> -					   src_ip->i_ino, spaceres);
> +					   src_ip->i_ino, spaceres, NULL);
>  		if (error)
>  			goto out_trans_cancel;
>  
> diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
> index 18f71fc90dd0..c8b252fa98ff 100644
> --- a/fs/xfs/xfs_symlink.c
> +++ b/fs/xfs/xfs_symlink.c
> @@ -314,7 +314,8 @@ xfs_symlink(
>  	/*
>  	 * Create the directory entry for the symlink.
>  	 */
> -	error = xfs_dir_createname(tp, dp, link_name, ip->i_ino, resblks);
> +	error = xfs_dir_createname(tp, dp, link_name,
> +			ip->i_ino, resblks, NULL);
>  	if (error)
>  		goto out_trans_cancel;
>  	xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 04/17] xfs: get directory offset when removing directory name
  2022-06-11  9:41 ` [PATCH v1 04/17] xfs: get directory offset when removing " Allison Henderson
@ 2022-06-29 18:30   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:30 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:47AM -0700, Allison Henderson wrote:
> Return the directory offset information when removing an entry to the
> directory.
> 
> This offset will be used as the parent pointer offset in xfs_remove.
> 
> [dchinner: forward ported and cleaned up]
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
>            Changed typedefs to raw struct types]
> 
> Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

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

--D

> ---
>  fs/xfs/libxfs/xfs_dir2.c       | 6 +++++-
>  fs/xfs/libxfs/xfs_dir2.h       | 3 ++-
>  fs/xfs/libxfs/xfs_dir2_block.c | 4 ++--
>  fs/xfs/libxfs/xfs_dir2_leaf.c  | 5 +++--
>  fs/xfs/libxfs/xfs_dir2_node.c  | 5 +++--
>  fs/xfs/libxfs/xfs_dir2_sf.c    | 2 ++
>  fs/xfs/xfs_inode.c             | 4 ++--
>  7 files changed, 19 insertions(+), 10 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
> index f7f7fa79593f..c3fa1bd1c370 100644
> --- a/fs/xfs/libxfs/xfs_dir2.c
> +++ b/fs/xfs/libxfs/xfs_dir2.c
> @@ -436,7 +436,8 @@ xfs_dir_removename(
>  	struct xfs_inode	*dp,
>  	struct xfs_name		*name,
>  	xfs_ino_t		ino,
> -	xfs_extlen_t		total)		/* bmap's total block count */
> +	xfs_extlen_t		total,		/* bmap's total block count */
> +	xfs_dir2_dataptr_t	*offset)	/* OUT: offset in directory */
>  {
>  	struct xfs_da_args	*args;
>  	int			rval;
> @@ -481,6 +482,9 @@ xfs_dir_removename(
>  	else
>  		rval = xfs_dir2_node_removename(args);
>  out_free:
> +	if (offset)
> +		*offset = args->offset;
> +
>  	kmem_free(args);
>  	return rval;
>  }
> diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> index 4d1c2570b833..c581d3b19bc6 100644
> --- a/fs/xfs/libxfs/xfs_dir2.h
> +++ b/fs/xfs/libxfs/xfs_dir2.h
> @@ -46,7 +46,8 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
>  				struct xfs_name *ci_name);
>  extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
>  				struct xfs_name *name, xfs_ino_t ino,
> -				xfs_extlen_t tot);
> +				xfs_extlen_t tot,
> +				xfs_dir2_dataptr_t *offset);
>  extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
>  				const struct xfs_name *name, xfs_ino_t inum,
>  				xfs_extlen_t tot);
> diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> index 85869f604960..4579e9be5d1a 100644
> --- a/fs/xfs/libxfs/xfs_dir2_block.c
> +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> @@ -810,9 +810,9 @@ xfs_dir2_block_removename(
>  	/*
>  	 * Point to the data entry using the leaf entry.
>  	 */
> +	args->offset = be32_to_cpu(blp[ent].address);
>  	dep = (xfs_dir2_data_entry_t *)((char *)hdr +
> -			xfs_dir2_dataptr_to_off(args->geo,
> -						be32_to_cpu(blp[ent].address)));
> +			xfs_dir2_dataptr_to_off(args->geo, args->offset));
>  	/*
>  	 * Mark the data entry's space free.
>  	 */
> diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
> index bd0c2f963545..c13763c16095 100644
> --- a/fs/xfs/libxfs/xfs_dir2_leaf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
> @@ -1381,9 +1381,10 @@ xfs_dir2_leaf_removename(
>  	 * Point to the leaf entry, use that to point to the data entry.
>  	 */
>  	lep = &leafhdr.ents[index];
> -	db = xfs_dir2_dataptr_to_db(geo, be32_to_cpu(lep->address));
> +	args->offset = be32_to_cpu(lep->address);
> +	db = xfs_dir2_dataptr_to_db(args->geo, args->offset);
>  	dep = (xfs_dir2_data_entry_t *)((char *)hdr +
> -		xfs_dir2_dataptr_to_off(geo, be32_to_cpu(lep->address)));
> +		xfs_dir2_dataptr_to_off(args->geo, args->offset));
>  	needscan = needlog = 0;
>  	oldbest = be16_to_cpu(bf[0].length);
>  	ltp = xfs_dir2_leaf_tail_p(geo, leaf);
> diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> index 5a9513c036b8..39cbdeafa0f6 100644
> --- a/fs/xfs/libxfs/xfs_dir2_node.c
> +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> @@ -1296,9 +1296,10 @@ xfs_dir2_leafn_remove(
>  	/*
>  	 * Extract the data block and offset from the entry.
>  	 */
> -	db = xfs_dir2_dataptr_to_db(geo, be32_to_cpu(lep->address));
> +	args->offset = be32_to_cpu(lep->address);
> +	db = xfs_dir2_dataptr_to_db(args->geo, args->offset);
>  	ASSERT(dblk->blkno == db);
> -	off = xfs_dir2_dataptr_to_off(geo, be32_to_cpu(lep->address));
> +	off = xfs_dir2_dataptr_to_off(args->geo, args->offset);
>  	ASSERT(dblk->index == off);
>  
>  	/*
> diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c
> index c6c06e8ab54b..51d42faabb18 100644
> --- a/fs/xfs/libxfs/xfs_dir2_sf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_sf.c
> @@ -971,6 +971,8 @@ xfs_dir2_sf_removename(
>  								XFS_CMP_EXACT) {
>  			ASSERT(xfs_dir2_sf_get_ino(mp, sfp, sfep) ==
>  			       args->inumber);
> +			args->offset = xfs_dir2_byte_to_dataptr(
> +						xfs_dir2_sf_get_offset(sfep));
>  			break;
>  		}
>  	}
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 05be02f6f62b..0c0c82e5dc59 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -2836,7 +2836,7 @@ xfs_remove(
>  	if (error)
>  		goto out_trans_cancel;
>  
> -	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks);
> +	error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks, NULL);
>  	if (error) {
>  		ASSERT(error != -ENOENT);
>  		goto out_trans_cancel;
> @@ -3391,7 +3391,7 @@ xfs_rename(
>  					spaceres);
>  	else
>  		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
> -					   spaceres);
> +					   spaceres, NULL);
>  
>  	if (error)
>  		goto out_trans_cancel;
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 05/17] xfs: get directory offset when replacing a directory name
  2022-06-11  9:41 ` [PATCH v1 05/17] xfs: get directory offset when replacing a " Allison Henderson
@ 2022-06-29 18:30   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:30 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:48AM -0700, Allison Henderson wrote:
> Return the directory offset information when replacing an entry to the
> directory.
> 
> This offset will be used as the parent pointer offset in xfs_rename.
> 
> [dchinner: forward ported and cleaned up]
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
>            Changed typedefs to raw struct types]
> 
> Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

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

--D

> ---
>  fs/xfs/libxfs/xfs_dir2.c       |  8 ++++++--
>  fs/xfs/libxfs/xfs_dir2.h       |  2 +-
>  fs/xfs/libxfs/xfs_dir2_block.c |  4 ++--
>  fs/xfs/libxfs/xfs_dir2_leaf.c  |  1 +
>  fs/xfs/libxfs/xfs_dir2_node.c  |  1 +
>  fs/xfs/libxfs/xfs_dir2_sf.c    |  2 ++
>  fs/xfs/xfs_inode.c             | 16 ++++++++--------
>  7 files changed, 21 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
> index c3fa1bd1c370..6af2f5a8e627 100644
> --- a/fs/xfs/libxfs/xfs_dir2.c
> +++ b/fs/xfs/libxfs/xfs_dir2.c
> @@ -482,7 +482,7 @@ xfs_dir_removename(
>  	else
>  		rval = xfs_dir2_node_removename(args);
>  out_free:
> -	if (offset)
> +	if (!rval && offset)
>  		*offset = args->offset;
>  
>  	kmem_free(args);
> @@ -498,7 +498,8 @@ xfs_dir_replace(
>  	struct xfs_inode	*dp,
>  	const struct xfs_name	*name,		/* name of entry to replace */
>  	xfs_ino_t		inum,		/* new inode number */
> -	xfs_extlen_t		total)		/* bmap's total block count */
> +	xfs_extlen_t		total,		/* bmap's total block count */
> +	xfs_dir2_dataptr_t	*offset)	/* OUT: offset in directory */
>  {
>  	struct xfs_da_args	*args;
>  	int			rval;
> @@ -546,6 +547,9 @@ xfs_dir_replace(
>  	else
>  		rval = xfs_dir2_node_replace(args);
>  out_free:
> +	if (offset)
> +		*offset = args->offset;
> +
>  	kmem_free(args);
>  	return rval;
>  }
> diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> index c581d3b19bc6..fd943c0c00a0 100644
> --- a/fs/xfs/libxfs/xfs_dir2.h
> +++ b/fs/xfs/libxfs/xfs_dir2.h
> @@ -50,7 +50,7 @@ extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
>  				xfs_dir2_dataptr_t *offset);
>  extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
>  				const struct xfs_name *name, xfs_ino_t inum,
> -				xfs_extlen_t tot);
> +				xfs_extlen_t tot, xfs_dir2_dataptr_t *offset);
>  extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
>  				struct xfs_name *name);
>  
> diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> index 4579e9be5d1a..ee8905d16223 100644
> --- a/fs/xfs/libxfs/xfs_dir2_block.c
> +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> @@ -885,9 +885,9 @@ xfs_dir2_block_replace(
>  	/*
>  	 * Point to the data entry we need to change.
>  	 */
> +	args->offset = be32_to_cpu(blp[ent].address);
>  	dep = (xfs_dir2_data_entry_t *)((char *)hdr +
> -			xfs_dir2_dataptr_to_off(args->geo,
> -						be32_to_cpu(blp[ent].address)));
> +			xfs_dir2_dataptr_to_off(args->geo, args->offset));
>  	ASSERT(be64_to_cpu(dep->inumber) != args->inumber);
>  	/*
>  	 * Change the inode number to the new value.
> diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
> index c13763c16095..958b9fea64bd 100644
> --- a/fs/xfs/libxfs/xfs_dir2_leaf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
> @@ -1518,6 +1518,7 @@ xfs_dir2_leaf_replace(
>  	/*
>  	 * Point to the data entry.
>  	 */
> +	args->offset = be32_to_cpu(lep->address);
>  	dep = (xfs_dir2_data_entry_t *)
>  	      ((char *)dbp->b_addr +
>  	       xfs_dir2_dataptr_to_off(args->geo, be32_to_cpu(lep->address)));
> diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> index 39cbdeafa0f6..53cd0d5d94f7 100644
> --- a/fs/xfs/libxfs/xfs_dir2_node.c
> +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> @@ -2242,6 +2242,7 @@ xfs_dir2_node_replace(
>  		hdr = state->extrablk.bp->b_addr;
>  		ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
>  		       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC));
> +		args->offset = be32_to_cpu(leafhdr.ents[blk->index].address);
>  		dep = (xfs_dir2_data_entry_t *)
>  		      ((char *)hdr +
>  		       xfs_dir2_dataptr_to_off(args->geo,
> diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c
> index 51d42faabb18..1de51eded26b 100644
> --- a/fs/xfs/libxfs/xfs_dir2_sf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_sf.c
> @@ -1109,6 +1109,8 @@ xfs_dir2_sf_replace(
>  				xfs_dir2_sf_put_ino(mp, sfp, sfep,
>  						args->inumber);
>  				xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
> +				args->offset = xfs_dir2_byte_to_dataptr(
> +						  xfs_dir2_sf_get_offset(sfep));
>  				break;
>  			}
>  		}
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 0c0c82e5dc59..b2dfd84e1f62 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -2817,7 +2817,7 @@ xfs_remove(
>  		 */
>  		if (dp->i_ino != tp->t_mountp->m_sb.sb_rootino) {
>  			error = xfs_dir_replace(tp, ip, &xfs_name_dotdot,
> -					tp->t_mountp->m_sb.sb_rootino, 0);
> +					tp->t_mountp->m_sb.sb_rootino, 0, NULL);
>  			if (error)
>  				return error;
>  		}
> @@ -2952,12 +2952,12 @@ xfs_cross_rename(
>  	int		dp2_flags = 0;
>  
>  	/* Swap inode number for dirent in first parent */
> -	error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres);
> +	error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres, NULL);
>  	if (error)
>  		goto out_trans_abort;
>  
>  	/* Swap inode number for dirent in second parent */
> -	error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, spaceres);
> +	error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, spaceres, NULL);
>  	if (error)
>  		goto out_trans_abort;
>  
> @@ -2971,7 +2971,7 @@ xfs_cross_rename(
>  
>  		if (S_ISDIR(VFS_I(ip2)->i_mode)) {
>  			error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
> -						dp1->i_ino, spaceres);
> +						dp1->i_ino, spaceres, NULL);
>  			if (error)
>  				goto out_trans_abort;
>  
> @@ -2995,7 +2995,7 @@ xfs_cross_rename(
>  
>  		if (S_ISDIR(VFS_I(ip1)->i_mode)) {
>  			error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
> -						dp2->i_ino, spaceres);
> +						dp2->i_ino, spaceres, NULL);
>  			if (error)
>  				goto out_trans_abort;
>  
> @@ -3315,7 +3315,7 @@ xfs_rename(
>  		 * name at the destination directory, remove it first.
>  		 */
>  		error = xfs_dir_replace(tp, target_dp, target_name,
> -					src_ip->i_ino, spaceres);
> +					src_ip->i_ino, spaceres, NULL);
>  		if (error)
>  			goto out_trans_cancel;
>  
> @@ -3349,7 +3349,7 @@ xfs_rename(
>  		 * directory.
>  		 */
>  		error = xfs_dir_replace(tp, src_ip, &xfs_name_dotdot,
> -					target_dp->i_ino, spaceres);
> +					target_dp->i_ino, spaceres, NULL);
>  		ASSERT(error != -EEXIST);
>  		if (error)
>  			goto out_trans_cancel;
> @@ -3388,7 +3388,7 @@ xfs_rename(
>  	 */
>  	if (wip)
>  		error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino,
> -					spaceres);
> +					spaceres, NULL);
>  	else
>  		error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino,
>  					   spaceres, NULL);
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 06/17] xfs: add parent pointer support to attribute code
  2022-06-11  9:41 ` [PATCH v1 06/17] xfs: add parent pointer support to attribute code Allison Henderson
@ 2022-06-29 18:33   ` Darrick J. Wong
  2022-06-29 18:58     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:33 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:49AM -0700, Allison Henderson wrote:
> Add the new parent attribute type. XFS_ATTR_PARENT is used only for parent pointer
> entries; it uses reserved blocks like XFS_ATTR_ROOT.
> 
> [dchinner: forward ported and cleaned up]
> [achender: rebased]
> 
> Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_attr.c       | 4 +++-
>  fs/xfs/libxfs/xfs_da_format.h  | 5 ++++-
>  fs/xfs/libxfs/xfs_log_format.h | 1 +
>  3 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index a94850d9b8b1..ee5dfebcf163 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -996,11 +996,13 @@ xfs_attr_set(
>  	struct xfs_inode	*dp = args->dp;
>  	struct xfs_mount	*mp = dp->i_mount;
>  	struct xfs_trans_res	tres;
> -	bool			rsvd = (args->attr_filter & XFS_ATTR_ROOT);
> +	bool			rsvd;
>  	int			error, local;
>  	int			rmt_blks = 0;
>  	unsigned int		total;
>  
> +	rsvd = (args->attr_filter & (XFS_ATTR_ROOT | XFS_ATTR_PARENT)) != 0;
> +
>  	if (xfs_is_shutdown(dp->i_mount))
>  		return -EIO;
>  
> diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
> index 25e2841084e1..2d771e6429f2 100644
> --- a/fs/xfs/libxfs/xfs_da_format.h
> +++ b/fs/xfs/libxfs/xfs_da_format.h
> @@ -688,12 +688,15 @@ struct xfs_attr3_leafblock {
>  #define	XFS_ATTR_LOCAL_BIT	0	/* attr is stored locally */
>  #define	XFS_ATTR_ROOT_BIT	1	/* limit access to trusted attrs */
>  #define	XFS_ATTR_SECURE_BIT	2	/* limit access to secure attrs */
> +#define 	XFS_ATTR_PARENT_BIT	3	/* parent pointer secure attrs */

          ^ whitespace

What is 'secure' about parent pointers?  Could the comment simply read:

	/* parent pointer attrs */

?

(The rest looks fine...)

--D

>  #define	XFS_ATTR_INCOMPLETE_BIT	7	/* attr in middle of create/delete */
>  #define XFS_ATTR_LOCAL		(1u << XFS_ATTR_LOCAL_BIT)
>  #define XFS_ATTR_ROOT		(1u << XFS_ATTR_ROOT_BIT)
>  #define XFS_ATTR_SECURE		(1u << XFS_ATTR_SECURE_BIT)
> +#define XFS_ATTR_PARENT		(1u << XFS_ATTR_PARENT_BIT)
>  #define XFS_ATTR_INCOMPLETE	(1u << XFS_ATTR_INCOMPLETE_BIT)
> -#define XFS_ATTR_NSP_ONDISK_MASK	(XFS_ATTR_ROOT | XFS_ATTR_SECURE)
> +#define XFS_ATTR_NSP_ONDISK_MASK \
> +			(XFS_ATTR_ROOT | XFS_ATTR_SECURE | XFS_ATTR_PARENT)
>  
>  /*
>   * Alignment for namelist and valuelist entries (since they are mixed
> diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
> index b351b9dc6561..eea53874fde8 100644
> --- a/fs/xfs/libxfs/xfs_log_format.h
> +++ b/fs/xfs/libxfs/xfs_log_format.h
> @@ -917,6 +917,7 @@ struct xfs_icreate_log {
>   */
>  #define XFS_ATTRI_FILTER_MASK		(XFS_ATTR_ROOT | \
>  					 XFS_ATTR_SECURE | \
> +					 XFS_ATTR_PARENT | \
>  					 XFS_ATTR_INCOMPLETE)
>  
>  /*
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 07/17] xfs: define parent pointer xattr format
  2022-06-11  9:41 ` [PATCH v1 07/17] xfs: define parent pointer xattr format Allison Henderson
@ 2022-06-29 18:34   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:34 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:50AM -0700, Allison Henderson wrote:
> We need to define the parent pointer attribute format before we start
> adding support for it into all the code that needs to use it. The EA
> format we will use encodes the following information:
> 
>         name={parent inode #, parent inode generation, dirent offset}
>         value={dirent filename}
> 
> The inode/gen gives all the information we need to reliably identify the
> parent without requiring child->parent lock ordering, and allows
> userspace to do pathname component level reconstruction without the
> kernel ever needing to verify the parent itself as part of ioctl calls.
> 
> By using the dirent offset in the EA name, we have a method of knowing
> the exact parent pointer EA we need to modify/remove in rename/unlink
> without an unbound EA name search.
> 
> By keeping the dirent name in the value, we have enough information to
> be able to validate and reconstruct damaged directory trees. While the
> diroffset of a filename alone is not unique enough to identify the
> child, the {diroffset,filename,child_inode} tuple is sufficient. That
> is, if the diroffset gets reused and points to a different filename, we
> can detect that from the contents of EA. If a link of the same name is
> created, then we can check whether it points at the same inode as the
> parent EA we current have.
> 
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
>            changed p_ino to xfs_ino_t and p_namelen to uint8_t,
>            moved to xfs_da_format for xfs_dir2_dataptr_t]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> Reviewed-by: Darrick J. Wong<darrick.wong@oracle.com>

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

--D

> ---
>  fs/xfs/libxfs/xfs_da_format.h | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
> index 2d771e6429f2..6ac8c8dd4aab 100644
> --- a/fs/xfs/libxfs/xfs_da_format.h
> +++ b/fs/xfs/libxfs/xfs_da_format.h
> @@ -805,4 +805,29 @@ static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
>  xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
>  				      struct xfs_da3_blkinfo *hdr3);
>  
> +/*
> + * Parent pointer attribute format definition
> + *
> + * EA name encodes the parent inode number, generation and the offset of
> + * the dirent that points to the child inode. The EA value contains the
> + * same name as the dirent in the parent directory.
> + */
> +struct xfs_parent_name_rec {
> +	__be64  p_ino;
> +	__be32  p_gen;
> +	__be32  p_diroffset;
> +};
> +
> +/*
> + * incore version of the above, also contains name pointers so callers
> + * can pass/obtain all the parent pointer information in a single structure
> + */
> +struct xfs_parent_name_irec {
> +	xfs_ino_t		p_ino;
> +	uint32_t		p_gen;
> +	xfs_dir2_dataptr_t	p_diroffset;
> +	const char		*p_name;
> +	uint8_t			p_namelen;
> +};
> +
>  #endif /* __XFS_DA_FORMAT_H__ */
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 08/17] xfs: Add xfs_verify_pptr
  2022-06-11  9:41 ` [PATCH v1 08/17] xfs: Add xfs_verify_pptr Allison Henderson
@ 2022-06-29 18:35   ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:35 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:51AM -0700, Allison Henderson wrote:
> Attribute names of parent pointers are not strings.  So we need to modify
> attr_namecheck to verify parent pointer records when the XFS_ATTR_PARENT flag is
> set.
> 
> 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 | 43 +++++++++++++++++++++++++++++++++++++---
>  fs/xfs/libxfs/xfs_attr.h |  3 ++-
>  fs/xfs/scrub/attr.c      |  2 +-
>  fs/xfs/xfs_attr_item.c   |  6 ++++--
>  fs/xfs/xfs_attr_list.c   | 17 +++++++++++-----
>  5 files changed, 59 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index ee5dfebcf163..30c8d9e9c2f1 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -1606,9 +1606,29 @@ xfs_attr_node_get(
>  	return error;
>  }
>  
> -/* Returns true if the attribute entry name is valid. */
> -bool
> -xfs_attr_namecheck(
> +/*
> + * Verify parent pointer attribute is valid.
> + * Return true on success or false on failure
> + */
> +STATIC bool
> +xfs_verify_pptr(struct xfs_mount *mp, struct xfs_parent_name_rec *rec)
> +{
> +	xfs_ino_t p_ino = (xfs_ino_t)be64_to_cpu(rec->p_ino);
> +	xfs_dir2_dataptr_t p_diroffset =
> +		(xfs_dir2_dataptr_t)be32_to_cpu(rec->p_diroffset);
> +
> +	if (!xfs_verify_ino(mp, p_ino))
> +		return false;
> +
> +	if (p_diroffset > XFS_DIR2_MAX_DATAPTR)
> +		return false;
> +
> +	return true;
> +}
> +
> +/* Returns true if the string attribute entry name is valid. */
> +static bool
> +xfs_str_attr_namecheck(
>  	const void	*name,
>  	size_t		length)
>  {
> @@ -1623,6 +1643,23 @@ xfs_attr_namecheck(
>  	return !memchr(name, 0, length);
>  }
>  
> +/* Returns true if the attribute entry name is valid. */
> +bool
> +xfs_attr_namecheck(
> +	struct xfs_mount	*mp,
> +	const void		*name,
> +	size_t			length,
> +	int			flags)
> +{
> +	if (flags & XFS_ATTR_PARENT) {
> +		if (length != sizeof(struct xfs_parent_name_rec))
> +			return false;
> +		return xfs_verify_pptr(mp, (struct xfs_parent_name_rec *)name);
> +	}
> +
> +	return xfs_str_attr_namecheck(name, length);
> +}
> +
>  int __init
>  xfs_attr_intent_init_cache(void)
>  {
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index 7600eac74db7..a87bc503976b 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -562,7 +562,8 @@ 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_attr_intent *attr);
>  int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
> -bool xfs_attr_namecheck(const void *name, size_t length);
> +bool xfs_attr_namecheck(struct xfs_mount *mp, const void *name, size_t length,
> +			int flags);
>  int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
>  void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
>  			 unsigned int *total);
> diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
> index b6f0c9f3f124..d3e75c077fab 100644
> --- a/fs/xfs/scrub/attr.c
> +++ b/fs/xfs/scrub/attr.c
> @@ -128,7 +128,7 @@ xchk_xattr_listent(
>  	}
>  
>  	/* Does this name make sense? */
> -	if (!xfs_attr_namecheck(name, namelen)) {
> +	if (!xfs_attr_namecheck(sx->sc->mp, name, namelen, flags)) {
>  		xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno);
>  		return;
>  	}
> diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
> index f524dbbb42d3..60fee5814655 100644
> --- a/fs/xfs/xfs_attr_item.c
> +++ b/fs/xfs/xfs_attr_item.c
> @@ -587,7 +587,8 @@ xfs_attri_item_recover(
>  	 */
>  	attrp = &attrip->attri_format;
>  	if (!xfs_attri_validate(mp, attrp) ||
> -	    !xfs_attr_namecheck(nv->name.i_addr, nv->name.i_len))
> +	    !xfs_attr_namecheck(mp, nv->name.i_addr, nv->name.i_len,
> +				attrp->alfi_attr_filter))
>  		return -EFSCORRUPTED;
>  
>  	error = xlog_recover_iget(mp,  attrp->alfi_ino, &ip);
> @@ -742,7 +743,8 @@ xlog_recover_attri_commit_pass2(
>  		return -EFSCORRUPTED;
>  	}
>  
> -	if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
> +	if (!xfs_attr_namecheck(mp, attr_name, attri_formatp->alfi_name_len,
> +				attri_formatp->alfi_attr_filter)) {
>  		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
>  		return -EFSCORRUPTED;
>  	}
> diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
> index 90a14e85e76d..3bac0647a927 100644
> --- a/fs/xfs/xfs_attr_list.c
> +++ b/fs/xfs/xfs_attr_list.c
> @@ -58,9 +58,13 @@ xfs_attr_shortform_list(
>  	struct xfs_attr_sf_sort		*sbuf, *sbp;
>  	struct xfs_attr_shortform	*sf;
>  	struct xfs_attr_sf_entry	*sfe;
> +	struct xfs_mount		*mp;
>  	int				sbsize, nsbuf, count, i;
>  	int				error = 0;
>  
> +	ASSERT(context != NULL);
> +	ASSERT(dp != NULL);
> +	mp = dp->i_mount;
>  	ASSERT(dp->i_afp != NULL);
>  	sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data;
>  	ASSERT(sf != NULL);
> @@ -83,8 +87,9 @@ xfs_attr_shortform_list(
>  	     (dp->i_afp->if_bytes + sf->hdr.count * 16) < context->bufsize)) {
>  		for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
>  			if (XFS_IS_CORRUPT(context->dp->i_mount,
> -					   !xfs_attr_namecheck(sfe->nameval,
> -							       sfe->namelen)))
> +					   !xfs_attr_namecheck(mp, sfe->nameval,
> +							       sfe->namelen,
> +							       sfe->flags)))
>  				return -EFSCORRUPTED;
>  			context->put_listent(context,
>  					     sfe->flags,
> @@ -175,8 +180,9 @@ xfs_attr_shortform_list(
>  			cursor->offset = 0;
>  		}
>  		if (XFS_IS_CORRUPT(context->dp->i_mount,
> -				   !xfs_attr_namecheck(sbp->name,
> -						       sbp->namelen))) {
> +				   !xfs_attr_namecheck(mp, sbp->name,
> +						       sbp->namelen,
> +						       sbp->flags))) {
>  			error = -EFSCORRUPTED;
>  			goto out;
>  		}
> @@ -466,7 +472,8 @@ xfs_attr3_leaf_list_int(
>  		}
>  
>  		if (XFS_IS_CORRUPT(context->dp->i_mount,
> -				   !xfs_attr_namecheck(name, namelen)))
> +				   !xfs_attr_namecheck(mp, name, namelen,
> +						       entry->flags)))
>  			return -EFSCORRUPTED;
>  		context->put_listent(context, entry->flags,
>  					      name, namelen, valuelen);
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes
  2022-06-11  9:41 ` [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes Allison Henderson
  2022-06-16  5:38   ` Dave Chinner
@ 2022-06-29 18:37   ` Darrick J. Wong
  2022-06-29 19:23     ` Alli
  1 sibling, 1 reply; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:37 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

> Subject: xfs: extent transaction reservations for parent attributeso

s/extent/extend/?


On Sat, Jun 11, 2022 at 02:41:52AM -0700, Allison Henderson wrote:
> We need to add, remove or modify parent pointer attributes during
> create/link/unlink/rename operations atomically with the dirents in the
> parent directories being modified. This means they need to be modified
> in the same transaction as the parent directories, and so we need to add
> the required space for the attribute modifications to the transaction
> reservations.
> 
> [achender: rebased, added xfs_sb_version_hasparent stub]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_format.h     |   5 ++
>  fs/xfs/libxfs/xfs_trans_resv.c | 103 +++++++++++++++++++++++++++------
>  fs/xfs/libxfs/xfs_trans_resv.h |   1 +
>  3 files changed, 90 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index afdfc8108c5f..96976497306c 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -390,6 +390,11 @@ xfs_sb_has_incompat_feature(
>  	return (sbp->sb_features_incompat & feature) != 0;
>  }
>  
> +static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
> +{
> +	return false; /* We'll enable this at the end of the set */
> +}
> +
>  #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS   (1 << 0)	/* Delayed Attributes */
>  #define XFS_SB_FEAT_INCOMPAT_LOG_ALL \
>  	(XFS_SB_FEAT_INCOMPAT_LOG_XATTRS)
> diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c
> index e9913c2c5a24..fbe46fd3b722 100644
> --- a/fs/xfs/libxfs/xfs_trans_resv.c
> +++ b/fs/xfs/libxfs/xfs_trans_resv.c
> @@ -909,24 +909,30 @@ xfs_calc_sb_reservation(
>  	return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
>  }
>  
> -void
> -xfs_trans_resv_calc(
> +/*
> + * Namespace reservations.
> + *
> + * These get tricky when parent pointers are enabled as we have attribute
> + * modifications occurring from within these transactions. Rather than confuse
> + * each of these reservation calculations with the conditional attribute
> + * reservations, add them here in a clear and concise manner. This assumes that
> + * the attribute reservations have already been calculated.
> + *
> + * Note that we only include the static attribute reservation here; the runtime
> + * reservation will have to be modified by the size of the attributes being
> + * added/removed/modified. See the comments on the attribute reservation
> + * calculations for more details.
> + *
> + * Note for rename: rename will vastly overestimate requirements. This will be
> + * addressed later when modifications are made to ensure parent attribute
> + * modifications can be done atomically with the rename operation.
> + */
> +STATIC void
> +xfs_calc_namespace_reservations(
>  	struct xfs_mount	*mp,
>  	struct xfs_trans_resv	*resp)
>  {
> -	int			logcount_adj = 0;
> -
> -	/*
> -	 * The following transactions are logged in physical format and
> -	 * require a permanent reservation on space.
> -	 */
> -	resp->tr_write.tr_logres = xfs_calc_write_reservation(mp, false);
> -	resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
> -	resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> -
> -	resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp, false);
> -	resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
> -	resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> +	ASSERT(resp->tr_attrsetm.tr_logres > 0);
>  
>  	resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
>  	resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
> @@ -948,15 +954,72 @@ xfs_trans_resv_calc(
>  	resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
>  	resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
>  
> +	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
> +	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
> +	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> +
> +	xfs_calc_parent_ptr_reservations(mp);
> +}
> +
> +void xfs_calc_parent_ptr_reservations(struct xfs_mount     *mp)

Indenting, etc.

Also vaguely wondering if this should be static inline?  Or does
something else call this?

--D

> +{
> +	struct xfs_trans_resv   *resp = M_RES(mp);
> +
> +	/* Calculate extra space needed for parent pointer attributes */
> +	if (!xfs_sb_version_hasparent(&mp->m_sb))
> +		return;
> +
> +	/* rename can add/remove/modify 4 parent attributes */
> +	resp->tr_rename.tr_logres += 4 * max(resp->tr_attrsetm.tr_logres,
> +					 resp->tr_attrrm.tr_logres);
> +	resp->tr_rename.tr_logcount += 4 * max(resp->tr_attrsetm.tr_logcount,
> +					   resp->tr_attrrm.tr_logcount);
> +
> +	/* create will add 1 parent attribute */
> +	resp->tr_create.tr_logres += resp->tr_attrsetm.tr_logres;
> +	resp->tr_create.tr_logcount += resp->tr_attrsetm.tr_logcount;
> +
> +	/* mkdir will add 1 parent attribute */
> +	resp->tr_mkdir.tr_logres += resp->tr_attrsetm.tr_logres;
> +	resp->tr_mkdir.tr_logcount += resp->tr_attrsetm.tr_logcount;
> +
> +	/* link will add 1 parent attribute */
> +	resp->tr_link.tr_logres += resp->tr_attrsetm.tr_logres;
> +	resp->tr_link.tr_logcount += resp->tr_attrsetm.tr_logcount;
> +
> +	/* symlink will add 1 parent attribute */
> +	resp->tr_symlink.tr_logres += resp->tr_attrsetm.tr_logres;
> +	resp->tr_symlink.tr_logcount += resp->tr_attrsetm.tr_logcount;
> +
> +	/* remove will remove 1 parent attribute */
> +	resp->tr_remove.tr_logres += resp->tr_attrrm.tr_logres;
> +	resp->tr_remove.tr_logcount += resp->tr_attrrm.tr_logcount;
> +}
> +
> +void
> +xfs_trans_resv_calc(
> +	struct xfs_mount	*mp,
> +	struct xfs_trans_resv	*resp)
> +{
> +	int			logcount_adj = 0;
> +
> +	/*
> +	 * The following transactions are logged in physical format and
> +	 * require a permanent reservation on space.
> +	 */
> +	resp->tr_write.tr_logres = xfs_calc_write_reservation(mp, false);
> +	resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
> +	resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> +
> +	resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp, false);
> +	resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
> +	resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> +
>  	resp->tr_create_tmpfile.tr_logres =
>  			xfs_calc_create_tmpfile_reservation(mp);
>  	resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
>  	resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
>  
> -	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
> -	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
> -	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> -
>  	resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
>  	resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
>  	resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> @@ -986,6 +1049,8 @@ xfs_trans_resv_calc(
>  	resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
>  	resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
>  
> +	xfs_calc_namespace_reservations(mp, resp);
> +
>  	/*
>  	 * The following transactions are logged in logical format with
>  	 * a default log count.
> diff --git a/fs/xfs/libxfs/xfs_trans_resv.h b/fs/xfs/libxfs/xfs_trans_resv.h
> index 0554b9d775d2..cab8084a84d6 100644
> --- a/fs/xfs/libxfs/xfs_trans_resv.h
> +++ b/fs/xfs/libxfs/xfs_trans_resv.h
> @@ -101,5 +101,6 @@ uint xfs_allocfree_block_count(struct xfs_mount *mp, uint num_ops);
>  unsigned int xfs_calc_itruncate_reservation_minlogsize(struct xfs_mount *mp);
>  unsigned int xfs_calc_write_reservation_minlogsize(struct xfs_mount *mp);
>  unsigned int xfs_calc_qm_dqalloc_reservation_minlogsize(struct xfs_mount *mp);
> +void xfs_calc_parent_ptr_reservations(struct xfs_mount *mp);
>  
>  #endif	/* __XFS_TRANS_RESV_H__ */
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 10/17] xfs: parent pointer attribute creation
  2022-06-11  9:41 ` [PATCH v1 10/17] xfs: parent pointer attribute creation Allison Henderson
  2022-06-11 15:10   ` kernel test robot
  2022-06-16  5:49   ` Dave Chinner
@ 2022-06-29 18:41   ` Darrick J. Wong
  2022-06-30  1:29     ` Alli
  2 siblings, 1 reply; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:41 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:53AM -0700, Allison Henderson wrote:
> Add parent pointer attribute during xfs_create, and subroutines to
> initialize attributes
> 
> [bfoster: rebase, use VFS inode generation]
> [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
>            fixed some null pointer bugs,
>            merged error handling patch,
>            added subroutines to handle attribute initialization,
>            remove unnecessary ENOSPC handling in xfs_attr_set_first_parent]
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/Makefile            |  1 +
>  fs/xfs/libxfs/xfs_attr.c   |  2 +-
>  fs/xfs/libxfs/xfs_attr.h   |  1 +
>  fs/xfs/libxfs/xfs_parent.c | 77 +++++++++++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_parent.h | 31 ++++++++++++++
>  fs/xfs/xfs_inode.c         | 88 +++++++++++++++++++++++++++-----------
>  fs/xfs/xfs_xattr.c         |  2 +-
>  fs/xfs/xfs_xattr.h         |  1 +
>  8 files changed, 177 insertions(+), 26 deletions(-)
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index b056cfc6398e..fc717dc3470c 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -40,6 +40,7 @@ xfs-y				+= $(addprefix libxfs/, \
>  				   xfs_inode_fork.o \
>  				   xfs_inode_buf.o \
>  				   xfs_log_rlimit.o \
> +				   xfs_parent.o \
>  				   xfs_ag_resv.o \
>  				   xfs_rmap.o \
>  				   xfs_rmap_btree.o \
> diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> index 30c8d9e9c2f1..f814a9177237 100644
> --- a/fs/xfs/libxfs/xfs_attr.c
> +++ b/fs/xfs/libxfs/xfs_attr.c
> @@ -926,7 +926,7 @@ xfs_attr_intent_init(
>  }
>  
>  /* Sets an attribute for an inode as a deferred operation */
> -static int
> +int
>  xfs_attr_defer_add(
>  	struct xfs_da_args	*args)
>  {
> diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> index a87bc503976b..576062e37d11 100644
> --- a/fs/xfs/libxfs/xfs_attr.h
> +++ b/fs/xfs/libxfs/xfs_attr.h
> @@ -559,6 +559,7 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
>  bool xfs_attr_is_leaf(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_defer_add(struct xfs_da_args *args);
>  int xfs_attr_set(struct xfs_da_args *args);
>  int xfs_attr_set_iter(struct xfs_attr_intent *attr);
>  int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
> diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
> new file mode 100644
> index 000000000000..cb546652bde9
> --- /dev/null
> +++ b/fs/xfs/libxfs/xfs_parent.c
> @@ -0,0 +1,77 @@
> +/*

New files need an SPDX header.

> + * Copyright (c) 2015 Red Hat, Inc.
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation

No need for all this boilerplate once you've switched to SPDX tags.

> + */
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_format.h"
> +#include "xfs_da_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_shared.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_bmap_btree.h"
> +#include "xfs_inode.h"
> +#include "xfs_error.h"
> +#include "xfs_trace.h"
> +#include "xfs_trans.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_attr.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_attr_sf.h"
> +#include "xfs_bmap.h"
> +
> +/*
> + * Parent pointer attribute handling.
> + *
> + * Because the attribute value is a filename component, it will never be longer
> + * than 255 bytes. This means the attribute will always be a local format
> + * attribute as it is xfs_attr_leaf_entsize_local_max() for v5 filesystems will
> + * always be larger than this (max is 75% of block size).
> + *
> + * Creating a new parent attribute will always create a new attribute - there
> + * should never, ever be an existing attribute in the tree for a new inode.
> + * ENOSPC behavior is problematic - creating the inode without the parent
> + * pointer is effectively a corruption, so we allow parent attribute creation
> + * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
> + * occurring.
> + */
> +
> +
> +/* Initializes a xfs_parent_name_rec to be stored as an attribute name */
> +void
> +xfs_init_parent_name_rec(
> +	struct xfs_parent_name_rec	*rec,
> +	struct xfs_inode		*ip,
> +	uint32_t			p_diroffset)
> +{
> +	xfs_ino_t			p_ino = ip->i_ino;
> +	uint32_t			p_gen = VFS_I(ip)->i_generation;
> +
> +	rec->p_ino = cpu_to_be64(p_ino);
> +	rec->p_gen = cpu_to_be32(p_gen);
> +	rec->p_diroffset = cpu_to_be32(p_diroffset);
> +}
> +
> +/* Initializes a xfs_parent_name_irec from an xfs_parent_name_rec */
> +void
> +xfs_init_parent_name_irec(
> +	struct xfs_parent_name_irec	*irec,
> +	struct xfs_parent_name_rec	*rec)

Should this second arg be const struct xfs_parent_name_rec* ?

> +{
> +	irec->p_ino = be64_to_cpu(rec->p_ino);
> +	irec->p_gen = be32_to_cpu(rec->p_gen);
> +	irec->p_diroffset = be32_to_cpu(rec->p_diroffset);
> +}
> diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
> new file mode 100644
> index 000000000000..10dc576ce693
> --- /dev/null
> +++ b/fs/xfs/libxfs/xfs_parent.h
> @@ -0,0 +1,31 @@
> +/*
> + * Copyright (c) 2018 Oracle, Inc.

New file needs an SPDX header, and you should probably update the
copyright to be 2018-2022.

> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation Inc.

No need for all this boilerplate once you've switched to SPDX tags.

> + */
> +#ifndef	__XFS_PARENT_H__
> +#define	__XFS_PARENT_H__
> +
> +#include "xfs_da_format.h"
> +#include "xfs_format.h"

Don't include headers in headers.  If it's really a mess to add these
two to every single .c file, then just add:

struct xfs_inode;
struct xfs_parent_name_rec;
struct xfs_parent_name_irec;

and that'll do for pointers.

> +
> +/*
> + * Parent pointer attribute prototypes
> + */
> +void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
> +			      struct xfs_inode *ip,
> +			      uint32_t p_diroffset);
> +void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
> +			       struct xfs_parent_name_rec *rec);
> +#endif	/* __XFS_PARENT_H__ */
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index b2dfd84e1f62..6b1e4cb11b5c 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -36,6 +36,8 @@
>  #include "xfs_reflink.h"
>  #include "xfs_ag.h"
>  #include "xfs_log_priv.h"
> +#include "xfs_parent.h"
> +#include "xfs_xattr.h"
>  
>  struct kmem_cache *xfs_inode_cache;
>  
> @@ -962,27 +964,40 @@ xfs_bumplink(
>  
>  int
>  xfs_create(
> -	struct user_namespace	*mnt_userns,
> -	xfs_inode_t		*dp,
> -	struct xfs_name		*name,
> -	umode_t			mode,
> -	dev_t			rdev,
> -	bool			init_xattrs,
> -	xfs_inode_t		**ipp)
> -{
> -	int			is_dir = S_ISDIR(mode);
> -	struct xfs_mount	*mp = dp->i_mount;
> -	struct xfs_inode	*ip = NULL;
> -	struct xfs_trans	*tp = NULL;
> -	int			error;
> -	bool                    unlock_dp_on_error = false;
> -	prid_t			prid;
> -	struct xfs_dquot	*udqp = NULL;
> -	struct xfs_dquot	*gdqp = NULL;
> -	struct xfs_dquot	*pdqp = NULL;
> -	struct xfs_trans_res	*tres;
> -	uint			resblks;
> -	xfs_ino_t		ino;
> +	struct user_namespace		*mnt_userns,
> +	xfs_inode_t			*dp,

Convert the struct typedefs...

--D

> +	struct xfs_name			*name,
> +	umode_t				mode,
> +	dev_t				rdev,
> +	bool				init_xattrs,
> +	xfs_inode_t			**ipp)
> +{
> +	int				is_dir = S_ISDIR(mode);
> +	struct xfs_mount		*mp = dp->i_mount;
> +	struct xfs_inode		*ip = NULL;
> +	struct xfs_trans		*tp = NULL;
> +	int				error;
> +	bool				unlock_dp_on_error = false;
> +	prid_t				prid;
> +	struct xfs_dquot		*udqp = NULL;
> +	struct xfs_dquot		*gdqp = NULL;
> +	struct xfs_dquot		*pdqp = NULL;
> +	struct xfs_trans_res		*tres;
> +	uint				resblks;
> +	xfs_ino_t			ino;
> +	xfs_dir2_dataptr_t		diroffset;
> +	struct xfs_parent_name_rec	rec;
> +	struct xfs_da_args		args = {
> +		.dp		= dp,
> +		.geo		= mp->m_attr_geo,
> +		.whichfork	= XFS_ATTR_FORK,
> +		.attr_filter	= XFS_ATTR_PARENT,
> +		.op_flags	= XFS_DA_OP_OKNOENT,
> +		.name		= (const uint8_t *)&rec,
> +		.namelen	= sizeof(rec),
> +		.value		= (void *)name->name,
> +		.valuelen	= name->len,
> +	};
>  
>  	trace_xfs_create(dp, name);
>  
> @@ -1009,6 +1024,12 @@ xfs_create(
>  		tres = &M_RES(mp)->tr_create;
>  	}
>  
> +	if (xfs_has_larp(mp)) {
> +		error = xfs_attr_grab_log_assist(mp);
> +		if (error)
> +			goto out_release_dquots;
> +	}
> +
>  	/*
>  	 * Initially assume that the file does not exist and
>  	 * reserve the resources for that case.  If that is not
> @@ -1024,7 +1045,7 @@ xfs_create(
>  				resblks, &tp);
>  	}
>  	if (error)
> -		goto out_release_dquots;
> +		goto drop_incompat;
>  
>  	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
>  	unlock_dp_on_error = true;
> @@ -1048,11 +1069,12 @@ xfs_create(
>  	 * the transaction cancel unlocking dp so don't do it explicitly in the
>  	 * error path.
>  	 */
> -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
> +	xfs_trans_ijoin(tp, dp, 0);
>  	unlock_dp_on_error = false;
>  
>  	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
> -				   resblks - XFS_IALLOC_SPACE_RES(mp), NULL);
> +				   resblks - XFS_IALLOC_SPACE_RES(mp),
> +				   &diroffset);
>  	if (error) {
>  		ASSERT(error != -ENOSPC);
>  		goto out_trans_cancel;
> @@ -1068,6 +1090,20 @@ xfs_create(
>  		xfs_bumplink(tp, dp);
>  	}
>  
> +	/*
> +	 * If we have parent pointers, we need to add the attribute containing
> +	 * the parent information now.
> +	 */
> +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> +		xfs_init_parent_name_rec(&rec, dp, diroffset);
> +		args.dp	= ip;
> +		args.trans = tp;
> +		args.hashval = xfs_da_hashname(args.name, args.namelen);
> +		error =  xfs_attr_defer_add(&args);
> +		if (error)
> +			goto out_trans_cancel;
> +	}
> +
>  	/*
>  	 * If this is a synchronous mount, make sure that the
>  	 * create transaction goes to disk before returning to
> @@ -1093,6 +1129,7 @@ xfs_create(
>  
>  	*ipp = ip;
>  	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	xfs_iunlock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
>  	return 0;
>  
>   out_trans_cancel:
> @@ -1107,6 +1144,9 @@ xfs_create(
>  		xfs_finish_inode_setup(ip);
>  		xfs_irele(ip);
>  	}
> + drop_incompat:
> +	if (xfs_has_larp(mp))
> +		xlog_drop_incompat_feat(mp->m_log);
>   out_release_dquots:
>  	xfs_qm_dqrele(udqp);
>  	xfs_qm_dqrele(gdqp);
> diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> index 35e13e125ec6..6012a6ba512c 100644
> --- a/fs/xfs/xfs_xattr.c
> +++ b/fs/xfs/xfs_xattr.c
> @@ -27,7 +27,7 @@
>   * they must release the permission by calling xlog_drop_incompat_feat
>   * when they're done.
>   */
> -static inline int
> +inline int
>  xfs_attr_grab_log_assist(
>  	struct xfs_mount	*mp)
>  {
> diff --git a/fs/xfs/xfs_xattr.h b/fs/xfs/xfs_xattr.h
> index 2b09133b1b9b..3fd6520a4d69 100644
> --- a/fs/xfs/xfs_xattr.h
> +++ b/fs/xfs/xfs_xattr.h
> @@ -7,6 +7,7 @@
>  #define __XFS_XATTR_H__
>  
>  int xfs_attr_change(struct xfs_da_args *args);
> +int xfs_attr_grab_log_assist(struct xfs_mount *mp);
>  
>  extern const struct xattr_handler *xfs_xattr_handlers[];
>  
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init
  2022-06-11  9:41 ` [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init Allison Henderson
@ 2022-06-29 18:42   ` Darrick J. Wong
  2022-06-30  1:30     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:42 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:41:58AM -0700, Allison Henderson wrote:
> This patch adds a helper function xfs_attr_list_context_init used by
> xfs_attr_list. This function initializes the xfs_attr_list_context
> structure passed to xfs_attr_list_int. We will need this later to call
> xfs_attr_list_int_ilocked when the node is already locked.

Since you've mentioned the xattr userspace functions -- does our current
codebase hide the parent pointer xattrs from regular
getxattr/setxattr/listxattr system calls?

> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>

The change itself looks pretty straightfoward,
Reviewed-by: Darrick J. Wong <djwong@kernel.org>

--D

> ---
>  fs/xfs/xfs_file.c  |  1 +
>  fs/xfs/xfs_ioctl.c | 54 ++++++++++++++++++++++++++++++++--------------
>  fs/xfs/xfs_ioctl.h |  2 ++
>  3 files changed, 41 insertions(+), 16 deletions(-)
> 
> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> index e2f2a3a94634..884827f024fd 100644
> --- a/fs/xfs/xfs_file.c
> +++ b/fs/xfs/xfs_file.c
> @@ -17,6 +17,7 @@
>  #include "xfs_bmap_util.h"
>  #include "xfs_dir2.h"
>  #include "xfs_dir2_priv.h"
> +#include "xfs_attr.h"
>  #include "xfs_ioctl.h"
>  #include "xfs_trace.h"
>  #include "xfs_log.h"
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index 5a364a7d58fd..e1612e99e0c5 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -369,6 +369,40 @@ xfs_attr_flags(
>  	return 0;
>  }
>  
> +/*
> + * Initializes an xfs_attr_list_context suitable for
> + * use by xfs_attr_list
> + */
> +int
> +xfs_ioc_attr_list_context_init(
> +	struct xfs_inode		*dp,
> +	char				*buffer,
> +	int				bufsize,
> +	int				flags,
> +	struct xfs_attr_list_context	*context)
> +{
> +	struct xfs_attrlist		*alist;
> +
> +	/*
> +	 * Initialize the output buffer.
> +	 */
> +	context->dp = dp;
> +	context->resynch = 1;
> +	context->attr_filter = xfs_attr_filter(flags);
> +	context->buffer = buffer;
> +	context->bufsize = round_down(bufsize, sizeof(uint32_t));
> +	context->firstu = context->bufsize;
> +	context->put_listent = xfs_ioc_attr_put_listent;
> +
> +	alist = context->buffer;
> +	alist->al_count = 0;
> +	alist->al_more = 0;
> +	alist->al_offset[0] = context->bufsize;
> +
> +	return 0;
> +}
> +
> +
>  int
>  xfs_ioc_attr_list(
>  	struct xfs_inode		*dp,
> @@ -378,7 +412,6 @@ xfs_ioc_attr_list(
>  	struct xfs_attrlist_cursor __user *ucursor)
>  {
>  	struct xfs_attr_list_context	context = { };
> -	struct xfs_attrlist		*alist;
>  	void				*buffer;
>  	int				error;
>  
> @@ -410,21 +443,10 @@ xfs_ioc_attr_list(
>  	if (!buffer)
>  		return -ENOMEM;
>  
> -	/*
> -	 * Initialize the output buffer.
> -	 */
> -	context.dp = dp;
> -	context.resynch = 1;
> -	context.attr_filter = xfs_attr_filter(flags);
> -	context.buffer = buffer;
> -	context.bufsize = round_down(bufsize, sizeof(uint32_t));
> -	context.firstu = context.bufsize;
> -	context.put_listent = xfs_ioc_attr_put_listent;
> -
> -	alist = context.buffer;
> -	alist->al_count = 0;
> -	alist->al_more = 0;
> -	alist->al_offset[0] = context.bufsize;
> +	error = xfs_ioc_attr_list_context_init(dp, buffer, bufsize, flags,
> +			&context);
> +	if (error)
> +		return error;
>  
>  	error = xfs_attr_list(&context);
>  	if (error)
> diff --git a/fs/xfs/xfs_ioctl.h b/fs/xfs/xfs_ioctl.h
> index d4abba2c13c1..ca60e1c427a3 100644
> --- a/fs/xfs/xfs_ioctl.h
> +++ b/fs/xfs/xfs_ioctl.h
> @@ -35,6 +35,8 @@ int xfs_ioc_attrmulti_one(struct file *parfilp, struct inode *inode,
>  int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
>  		      size_t bufsize, int flags,
>  		      struct xfs_attrlist_cursor __user *ucursor);
> +int xfs_ioc_attr_list_context_init(struct xfs_inode *dp, char *buffer,
> +		int bufsize, int flags, struct xfs_attr_list_context *context);
>  
>  extern struct dentry *
>  xfs_handle_to_dentry(
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 16/17] xfs: Increase  XFS_DEFER_OPS_NR_INODES to 4
  2022-06-18  0:32     ` Alli
@ 2022-06-29 18:43       ` Darrick J. Wong
  2022-06-30  1:30         ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:43 UTC (permalink / raw)
  To: Alli; +Cc: Dave Chinner, linux-xfs

On Fri, Jun 17, 2022 at 05:32:45PM -0700, Alli wrote:
> On Fri, 2022-06-17 at 07:54 +1000, Dave Chinner wrote:
> > On Sat, Jun 11, 2022 at 02:41:59AM -0700, Allison Henderson wrote:
> > > Renames that generate parent pointer updates will need to 2 extra
> > > defer
> > > operations. One for the rmap update and another for the parent
> > > pointer
> > > update
> > 
> > Not sure I follow this - defer operation counts are something
> > tracked in the transaction reservations, whilst this is changing the
> > number of inodes that are joined and held across defer operations.
> > 
> > These rmap updates already occur on the directory inodes in a rename
> > (when the dir update changes the dir shape), so I'm guessing that
> > you are now talking about changing parent attrs for the child inodes
> > may require attr fork shape changes (hence rmap updates) due to the
> > deferred parent pointer xattr update?
> > 
> > If so, this should be placed in the series before the modifications
> > to the rename operation is modified to join 4 ops to it, preferably
> > at the start of the series....
> 
> I see, sure, I can move this patch down to the beginning of the set
> > 
> > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > ---
> > >  fs/xfs/libxfs/xfs_defer.h | 2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
> > > index 114a3a4930a3..0c2a6e537016 100644
> > > --- a/fs/xfs/libxfs/xfs_defer.h
> > > +++ b/fs/xfs/libxfs/xfs_defer.h
> > > @@ -70,7 +70,7 @@ extern const struct xfs_defer_op_type
> > > xfs_attr_defer_type;
> > >  /*
> > >   * Deferred operation item relogging limits.
> > >   */
> > > -#define XFS_DEFER_OPS_NR_INODES	2	/* join up to two inodes */
> > > +#define XFS_DEFER_OPS_NR_INODES	4	/* join up to four inodes
> > > */
> > 
> > The comment is not useful  - it should desvribe what operation
> > requires 4 inodes to be joined. e.g.
> > 
> > /*
> >  * Rename w/ parent pointers requires 4 indoes with defered ops to
> >  * be joined to the transaction.
> >  */
> Sure, will update
> 
> > 
> > Then, if we are changing the maximum number of inodes that are
> > joined to a deferred operation, then we need to also update the
> > locking code such as in xfs_defer_ops_continue() that has to order
> > locking of multiple inodes correctly.
> Ok, I see it, I will take a look at updating that
> 
> > 
> > Also, rename can lock and modify 5 inodes, not 4, so the 4 inodes
> > that get joined here need to be clearly documented somewhere. 
> Ok, I think its src dir, target dir, src inode, target inode, and then
> wip.  Do we want the documenting in xfs_defer_ops_continue?  Or just
> the commit description?
> 
> > Also,
> > xfs_sort_for_rename() that orders all the inodes in rename into
> > correct locking order in an array, and xfs_lock_inodes() that does
> > the locking of the inodes in the array.
> Yes, I see it.  You want a comment in xfs_defer_ops_continue referring
> to the order?

I wouldn't mind one somewhere, though it could probably live with the
parent pointer helper functions or buried in xfs_rename somewhere.

--D

> 
> Thanks!
> Allison
> 
> > 
> > Cheers,
> > 
> > Dave.
> 

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

* Re: [PATCH v1 17/17] xfs: Add parent pointer ioctl
  2022-06-11  9:42 ` [PATCH v1 17/17] xfs: Add parent pointer ioctl Allison Henderson
@ 2022-06-29 18:52   ` Darrick J. Wong
  2022-06-30  1:30     ` Alli
  0 siblings, 1 reply; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-29 18:52 UTC (permalink / raw)
  To: Allison Henderson; +Cc: linux-xfs

On Sat, Jun 11, 2022 at 02:42:00AM -0700, Allison Henderson wrote:
> This patch adds a new file ioctl to retrieve the parent pointer of a
> given inode
> 
> Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> ---
>  fs/xfs/Makefile            |   1 +
>  fs/xfs/libxfs/xfs_fs.h     |  46 +++++++++++++
>  fs/xfs/libxfs/xfs_parent.c |  10 +++
>  fs/xfs/libxfs/xfs_parent.h |   2 +
>  fs/xfs/xfs_ioctl.c         |  90 ++++++++++++++++++++++++-
>  fs/xfs/xfs_ondisk.h        |   4 ++
>  fs/xfs/xfs_parent_utils.c  | 133 +++++++++++++++++++++++++++++++++++++
>  fs/xfs/xfs_parent_utils.h  |  22 ++++++
>  8 files changed, 306 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index fc717dc3470c..da86f6231f2e 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -86,6 +86,7 @@ xfs-y				+= xfs_aops.o \
>  				   xfs_mount.o \
>  				   xfs_mru_cache.o \
>  				   xfs_pwork.o \
> +				   xfs_parent_utils.o \
>  				   xfs_reflink.o \
>  				   xfs_stats.o \
>  				   xfs_super.o \
> diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
> index b0b4d7a3aa15..e6c8873cd234 100644
> --- a/fs/xfs/libxfs/xfs_fs.h
> +++ b/fs/xfs/libxfs/xfs_fs.h
> @@ -574,6 +574,7 @@ typedef struct xfs_fsop_handlereq {
>  #define XFS_IOC_ATTR_SECURE	0x0008	/* use attrs in security namespace */
>  #define XFS_IOC_ATTR_CREATE	0x0010	/* fail if attr already exists */
>  #define XFS_IOC_ATTR_REPLACE	0x0020	/* fail if attr does not exist */
> +#define XFS_IOC_ATTR_PARENT	0x0040  /* use attrs in parent namespace */
>  
>  typedef struct xfs_attrlist_cursor {
>  	__u32		opaque[4];
> @@ -752,6 +753,50 @@ struct xfs_scrub_metadata {
>  				 XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
>  #define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
>  
> +#define XFS_PPTR_MAXNAMELEN				256
> +
> +/* return parents of the handle, not the open fd */
> +#define XFS_PPTR_IFLAG_HANDLE  (1U << 0)
> +
> +/* target was the root directory */
> +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
> +
> +/* Cursor is done iterating pptrs */
> +#define XFS_PPTR_OFLAG_DONE    (1U << 2)
> +
> +/* Get an inode parent pointer through ioctl */
> +struct xfs_parent_ptr {
> +	__u64		xpp_ino;			/* Inode */
> +	__u32		xpp_gen;			/* Inode generation */
> +	__u32		xpp_diroffset;			/* Directory offset */
> +	__u32		xpp_namelen;			/* File name length */
> +	__u32		xpp_pad;
> +	__u8		xpp_name[XFS_PPTR_MAXNAMELEN];	/* File name */
> +};
> +
> +/* Iterate through an inodes parent pointers */
> +struct xfs_pptr_info {
> +	struct xfs_handle		pi_handle;
> +	struct xfs_attrlist_cursor	pi_cursor;
> +	__u32				pi_flags;
> +	__u32				pi_reserved;
> +	__u32				pi_ptrs_size;
> +	__u32				pi_ptrs_used;
> +	__u64				pi_reserved2[6];
> +
> +	/*
> +	 * An array of struct xfs_parent_ptr follows the header
> +	 * information. Use XFS_PPINFO_TO_PP() to access the
> +	 * parent pointer array entries.
> +	 */

	struct xfs_parent_ptr		pi_parents[];

Unless you want to conserve space in the userspace buffer by making the
size of xfs_parent_ptr itself variable?  Userspace would have to walk
the entire buffer by hand like it does for listxattr, but it saves a
fair amount of space.

> +};
> +
> +#define XFS_PPTR_INFO_SIZEOF(nr_ptrs) sizeof (struct xfs_pptr_info) + \
> +				      nr_ptrs * sizeof(struct xfs_parent_ptr)
> +
> +#define XFS_PPINFO_TO_PP(info, idx)    \
> +	(&(((struct xfs_parent_ptr *)((char *)(info) + sizeof(*(info))))[(idx)]))

Turn these into static inline functions so that client programs get some
proper typechecking by the C compiler.

> +
>  /*
>   * ioctl limits
>   */
> @@ -797,6 +842,7 @@ struct xfs_scrub_metadata {
>  /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
>  #define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
>  #define XFS_IOC_AG_GEOMETRY	_IOWR('X', 61, struct xfs_ag_geometry)
> +#define XFS_IOC_GETPPOINTER	_IOR ('X', 62, struct xfs_parent_ptr)
>  
>  /*
>   * ioctl commands that replace IRIX syssgi()'s
> diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
> index cb546652bde9..a5b99f30bc63 100644
> --- a/fs/xfs/libxfs/xfs_parent.c
> +++ b/fs/xfs/libxfs/xfs_parent.c
> @@ -33,6 +33,16 @@
>  #include "xfs_attr_sf.h"
>  #include "xfs_bmap.h"
>  
> +/* Initializes a xfs_parent_ptr from an xfs_parent_name_rec */
> +void
> +xfs_init_parent_ptr(struct xfs_parent_ptr		*xpp,
> +		     struct xfs_parent_name_rec	*rec)

Indenting...

> +{
> +	xpp->xpp_ino = be64_to_cpu(rec->p_ino);
> +	xpp->xpp_gen = be32_to_cpu(rec->p_gen);
> +	xpp->xpp_diroffset = be32_to_cpu(rec->p_diroffset);
> +}
> +
>  /*
>   * Parent pointer attribute handling.
>   *
> diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
> index 10dc576ce693..fa50ada0d6a9 100644
> --- a/fs/xfs/libxfs/xfs_parent.h
> +++ b/fs/xfs/libxfs/xfs_parent.h
> @@ -28,4 +28,6 @@ void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
>  			      uint32_t p_diroffset);
>  void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
>  			       struct xfs_parent_name_rec *rec);
> +void xfs_init_parent_ptr(struct xfs_parent_ptr *xpp,
> +			 struct xfs_parent_name_rec *rec);
>  #endif	/* __XFS_PARENT_H__ */
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index e1612e99e0c5..4cd1de3e9d0b 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -37,6 +37,7 @@
>  #include "xfs_health.h"
>  #include "xfs_reflink.h"
>  #include "xfs_ioctl.h"
> +#include "xfs_parent_utils.h"
>  #include "xfs_xattr.h"
>  
>  #include <linux/mount.h>
> @@ -355,6 +356,8 @@ xfs_attr_filter(
>  		return XFS_ATTR_ROOT;
>  	if (ioc_flags & XFS_IOC_ATTR_SECURE)
>  		return XFS_ATTR_SECURE;
> +	if (ioc_flags & XFS_IOC_ATTR_PARENT)
> +		return XFS_ATTR_PARENT;
>  	return 0;
>  }
>  
> @@ -422,7 +425,8 @@ xfs_ioc_attr_list(
>  	/*
>  	 * Reject flags, only allow namespaces.
>  	 */
> -	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
> +	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE |
> +		      XFS_IOC_ATTR_PARENT))
>  		return -EINVAL;
>  	if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
>  		return -EINVAL;
> @@ -1672,6 +1676,87 @@ xfs_ioc_scrub_metadata(
>  	return 0;
>  }
>  
> +/*
> + * IOCTL routine to get the parent pointers of an inode and return it to user
> + * space.  Caller must pass a buffer space containing a struct xfs_pptr_info,
> + * followed by a region large enough to contain an array of struct
> + * xfs_parent_ptr of a size specified in pi_ptrs_size.  If the inode contains
> + * more parent pointers than can fit in the buffer space, caller may re-call
> + * the function using the returned pi_cursor to resume iteration.  The
> + * number of xfs_parent_ptr returned will be stored in pi_ptrs_used.
> + *
> + * Returns 0 on success or non-zero on failure
> + */
> +STATIC int
> +xfs_ioc_get_parent_pointer(
> +	struct file			*filp,
> +	void				__user *arg)
> +{
> +	struct xfs_pptr_info		*ppi = NULL;
> +	int				error = 0;
> +	struct xfs_inode		*ip = XFS_I(file_inode(filp));
> +	struct xfs_mount		*mp = ip->i_mount;
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	/* Allocate an xfs_pptr_info to put the user data */
> +	ppi = kmem_alloc(sizeof(struct xfs_pptr_info), 0);
> +	if (!ppi)
> +		return -ENOMEM;
> +
> +	/* Copy the data from the user */
> +	error = copy_from_user(ppi, arg, sizeof(struct xfs_pptr_info));
> +	if (error)
> +		goto out;
> +
> +	/* Check size of buffer requested by user */
> +	if (XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size) > XFS_XATTR_LIST_MAX) {
> +		error = -ENOMEM;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Now that we know how big the trailing buffer is, expand
> +	 * our kernel xfs_pptr_info to be the same size
> +	 */
> +	ppi = krealloc(ppi, XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size),
> +		       GFP_NOFS | __GFP_NOFAIL);
> +	if (!ppi)
> +		return -ENOMEM;
> +
> +	if (ppi->pi_flags != 0 && ppi->pi_flags != XFS_PPTR_IFLAG_HANDLE) {

Flags validation should come before the big memory allocation.

> +		error = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ppi->pi_flags == XFS_PPTR_IFLAG_HANDLE) {
> +		error = xfs_iget(mp, NULL, ppi->pi_handle.ha_fid.fid_ino,
> +				0, 0, &ip);
> +		if (error)
> +			goto out;

This ought to be checking the generation number in the file handle.

> +	}
> +
> +	if (ip->i_ino == mp->m_sb.sb_rootino)
> +		ppi->pi_flags |= XFS_PPTR_OFLAG_ROOT;
> +
> +	/* Get the parent pointers */
> +	error = xfs_attr_get_parent_pointer(ip, ppi);
> +
> +	if (error)
> +		goto out;
> +
> +	/* Copy the parent pointers back to the user */
> +	error = copy_to_user(arg, ppi,
> +			XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size));
> +	if (error)
> +		goto out;
> +
> +out:
> +	kmem_free(ppi);
> +	return error;
> +}
> +
>  int
>  xfs_ioc_swapext(
>  	xfs_swapext_t	*sxp)
> @@ -1961,7 +2046,8 @@ xfs_file_ioctl(
>  
>  	case XFS_IOC_FSGETXATTRA:
>  		return xfs_ioc_fsgetxattra(ip, arg);
> -
> +	case XFS_IOC_GETPPOINTER:
> +		return xfs_ioc_get_parent_pointer(filp, arg);
>  	case XFS_IOC_GETBMAP:
>  	case XFS_IOC_GETBMAPA:
>  	case XFS_IOC_GETBMAPX:
> diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
> index 758702b9495f..765eb514a917 100644
> --- a/fs/xfs/xfs_ondisk.h
> +++ b/fs/xfs/xfs_ondisk.h
> @@ -135,6 +135,10 @@ xfs_check_ondisk_structs(void)
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
>  
> +	/* parent pointer ioctls */
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_parent_ptr,            280);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_pptr_info,             104);
> +
>  	/*
>  	 * The v5 superblock format extended several v4 header structures with
>  	 * additional data. While new fields are only accessible on v5
> diff --git a/fs/xfs/xfs_parent_utils.c b/fs/xfs/xfs_parent_utils.c
> new file mode 100644
> index 000000000000..9880718395c6
> --- /dev/null
> +++ b/fs/xfs/xfs_parent_utils.c
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (c) 2015 Red Hat, Inc.
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation
> + */
> +#include "xfs.h"
> +#include "xfs_fs.h"
> +#include "xfs_format.h"
> +#include "xfs_log_format.h"
> +#include "xfs_shared.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_bmap_btree.h"
> +#include "xfs_inode.h"
> +#include "xfs_error.h"
> +#include "xfs_trace.h"
> +#include "xfs_trans.h"
> +#include "xfs_da_format.h"
> +#include "xfs_da_btree.h"
> +#include "xfs_attr.h"
> +#include "xfs_ioctl.h"
> +#include "xfs_parent.h"
> +#include "xfs_da_btree.h"
> +
> +/*
> + * Get the parent pointers for a given inode
> + *
> + * Returns 0 on success and non zero on error
> + */
> +int
> +xfs_attr_get_parent_pointer(struct xfs_inode		*ip,
> +			    struct xfs_pptr_info	*ppi)
> +

Indenting.  Also, should this go in xfs_parent.c ?

> +{
> +
> +	struct xfs_attrlist		*alist;
> +	struct xfs_attrlist_ent		*aent;
> +	struct xfs_parent_ptr		*xpp;
> +	struct xfs_parent_name_rec	*xpnr;
> +	char				*namebuf;
> +	unsigned int			namebuf_size;
> +	int				name_len;
> +	int				error = 0;
> +	unsigned int			ioc_flags = XFS_IOC_ATTR_PARENT;
> +	unsigned int			flags = XFS_ATTR_PARENT;
> +	int				i;
> +	struct xfs_attr_list_context	context;
> +	struct xfs_da_args		args;
> +
> +	/* Allocate a buffer to store the attribute names */
> +	namebuf_size = sizeof(struct xfs_attrlist) +
> +		       (ppi->pi_ptrs_size) * sizeof(struct xfs_attrlist_ent);
> +	namebuf = kvzalloc(namebuf_size, GFP_KERNEL);
> +	if (!namebuf)
> +		return -ENOMEM;
> +
> +	memset(&context, 0, sizeof(struct xfs_attr_list_context));
> +	error = xfs_ioc_attr_list_context_init(ip, namebuf, namebuf_size,
> +			ioc_flags, &context);
> +
> +	/* Copy the cursor provided by caller */
> +	memcpy(&context.cursor, &ppi->pi_cursor,
> +	       sizeof(struct xfs_attrlist_cursor));
> +
> +	if (error)
> +		goto out_kfree;
> +
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +
> +	error = xfs_attr_list_ilocked(&context);
> +	if (error)
> +		goto out_kfree;
> +
> +	alist = (struct xfs_attrlist *)namebuf;
> +	for (i = 0; i < alist->al_count; i++) {
> +		xpp = XFS_PPINFO_TO_PP(ppi, i);
> +		memset(xpp, 0, sizeof(struct xfs_parent_ptr));
> +		aent = (struct xfs_attrlist_ent *)
> +			&namebuf[alist->al_offset[i]];
> +		xpnr = (struct xfs_parent_name_rec *)(aent->a_name);
> +
> +		if (aent->a_valuelen > XFS_PPTR_MAXNAMELEN) {
> +			error = -ERANGE;
> +			goto out_kfree;
> +		}
> +		name_len = aent->a_valuelen;
> +
> +		memset(&args, 0, sizeof(args));
> +		args.geo = ip->i_mount->m_attr_geo;
> +		args.whichfork = XFS_ATTR_FORK;
> +		args.dp = ip;
> +		args.name = (char *)xpnr;
> +		args.namelen = sizeof(struct xfs_parent_name_rec);
> +		args.attr_filter = flags;
> +		args.hashval = xfs_da_hashname(args.name, args.namelen);
> +		args.value = (unsigned char *)(xpp->xpp_name);
> +		args.valuelen = name_len;
> +		args.op_flags = XFS_DA_OP_OKNOENT;

You might want to convert this to a C99 named initialization inside the
loop body.

Otherwise looks ok.

--D

> +
> +		error = xfs_attr_get_ilocked(&args);
> +		error = (error == -EEXIST ? 0 : error);
> +		if (error)
> +			goto out_kfree;
> +
> +		xpp->xpp_namelen = name_len;
> +		xfs_init_parent_ptr(xpp, xpnr);
> +	}
> +	ppi->pi_ptrs_used = alist->al_count;
> +	if (!alist->al_more)
> +		ppi->pi_flags |= XFS_PPTR_OFLAG_DONE;
> +
> +	/* Update the caller with the current cursor position */
> +	memcpy(&ppi->pi_cursor, &context.cursor,
> +		sizeof(struct xfs_attrlist_cursor));
> +
> +out_kfree:
> +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> +	kmem_free(namebuf);
> +
> +	return error;
> +}
> +
> diff --git a/fs/xfs/xfs_parent_utils.h b/fs/xfs/xfs_parent_utils.h
> new file mode 100644
> index 000000000000..0e952b2ebd4a
> --- /dev/null
> +++ b/fs/xfs/xfs_parent_utils.h
> @@ -0,0 +1,22 @@
> +/*
> + * Copyright (c) 2017 Oracle, Inc.
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write the Free Software Foundation Inc.
> + */
> +#ifndef	__XFS_PARENT_UTILS_H__
> +#define	__XFS_PARENT_UTILS_H__
> +
> +int xfs_attr_get_parent_pointer(struct xfs_inode *ip,
> +				struct xfs_pptr_info *ppi);
> +#endif	/* __XFS_PARENT_UTILS_H__ */
> -- 
> 2.25.1
> 

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

* Re: [PATCH v1 06/17] xfs: add parent pointer support to attribute code
  2022-06-29 18:33   ` Darrick J. Wong
@ 2022-06-29 18:58     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-29 18:58 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, 2022-06-29 at 11:33 -0700, Darrick J. Wong wrote:
> On Sat, Jun 11, 2022 at 02:41:49AM -0700, Allison Henderson wrote:
> > Add the new parent attribute type. XFS_ATTR_PARENT is used only for
> > parent pointer
> > entries; it uses reserved blocks like XFS_ATTR_ROOT.
> > 
> > [dchinner: forward ported and cleaned up]
> > [achender: rebased]
> > 
> > Signed-off-by: Mark Tinguely <tinguely@sgi.com>
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_attr.c       | 4 +++-
> >  fs/xfs/libxfs/xfs_da_format.h  | 5 ++++-
> >  fs/xfs/libxfs/xfs_log_format.h | 1 +
> >  3 files changed, 8 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > index a94850d9b8b1..ee5dfebcf163 100644
> > --- a/fs/xfs/libxfs/xfs_attr.c
> > +++ b/fs/xfs/libxfs/xfs_attr.c
> > @@ -996,11 +996,13 @@ xfs_attr_set(
> >  	struct xfs_inode	*dp = args->dp;
> >  	struct xfs_mount	*mp = dp->i_mount;
> >  	struct xfs_trans_res	tres;
> > -	bool			rsvd = (args->attr_filter &
> > XFS_ATTR_ROOT);
> > +	bool			rsvd;
> >  	int			error, local;
> >  	int			rmt_blks = 0;
> >  	unsigned int		total;
> >  
> > +	rsvd = (args->attr_filter & (XFS_ATTR_ROOT | XFS_ATTR_PARENT))
> > != 0;
> > +
> >  	if (xfs_is_shutdown(dp->i_mount))
> >  		return -EIO;
> >  
> > diff --git a/fs/xfs/libxfs/xfs_da_format.h
> > b/fs/xfs/libxfs/xfs_da_format.h
> > index 25e2841084e1..2d771e6429f2 100644
> > --- a/fs/xfs/libxfs/xfs_da_format.h
> > +++ b/fs/xfs/libxfs/xfs_da_format.h
> > @@ -688,12 +688,15 @@ struct xfs_attr3_leafblock {
> >  #define	XFS_ATTR_LOCAL_BIT	0	/* attr is stored locally
> > */
> >  #define	XFS_ATTR_ROOT_BIT	1	/* limit access to trusted
> > attrs */
> >  #define	XFS_ATTR_SECURE_BIT	2	/* limit access to secure
> > attrs */
> > +#define 	XFS_ATTR_PARENT_BIT	3	/* parent pointer secure
> > attrs */
> 
>           ^ whitespace
> 
> What is 'secure' about parent pointers? 
Nothing, it's a typo :-)

>  Could the comment simply read:
> 
> 	/* parent pointer attrs */
> 
> ?
> 
Yes, will fix.  Thx for the catch

Allison

> (The rest looks fine...)
> 
> --D
> 
> >  #define	XFS_ATTR_INCOMPLETE_BIT	7	/* attr in middle
> > of create/delete */
> >  #define XFS_ATTR_LOCAL		(1u << XFS_ATTR_LOCAL_BIT)
> >  #define XFS_ATTR_ROOT		(1u << XFS_ATTR_ROOT_BIT)
> >  #define XFS_ATTR_SECURE		(1u << XFS_ATTR_SECURE_BIT)
> > +#define XFS_ATTR_PARENT		(1u << XFS_ATTR_PARENT_BIT)
> >  #define XFS_ATTR_INCOMPLETE	(1u << XFS_ATTR_INCOMPLETE_BIT)
> > -#define XFS_ATTR_NSP_ONDISK_MASK	(XFS_ATTR_ROOT |
> > XFS_ATTR_SECURE)
> > +#define XFS_ATTR_NSP_ONDISK_MASK \
> > +			(XFS_ATTR_ROOT | XFS_ATTR_SECURE |
> > XFS_ATTR_PARENT)
> >  
> >  /*
> >   * Alignment for namelist and valuelist entries (since they are
> > mixed
> > diff --git a/fs/xfs/libxfs/xfs_log_format.h
> > b/fs/xfs/libxfs/xfs_log_format.h
> > index b351b9dc6561..eea53874fde8 100644
> > --- a/fs/xfs/libxfs/xfs_log_format.h
> > +++ b/fs/xfs/libxfs/xfs_log_format.h
> > @@ -917,6 +917,7 @@ struct xfs_icreate_log {
> >   */
> >  #define XFS_ATTRI_FILTER_MASK		(XFS_ATTR_ROOT | \
> >  					 XFS_ATTR_SECURE | \
> > +					 XFS_ATTR_PARENT | \
> >  					 XFS_ATTR_INCOMPLETE)
> >  
> >  /*
> > -- 
> > 2.25.1
> > 


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

* Re: [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes
  2022-06-29 18:37   ` Darrick J. Wong
@ 2022-06-29 19:23     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-29 19:23 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, 2022-06-29 at 11:37 -0700, Darrick J. Wong wrote:
> > Subject: xfs: extent transaction reservations for parent
> > attributeso
> 
> s/extent/extend/?
Yes, will fix

> 
> 
> On Sat, Jun 11, 2022 at 02:41:52AM -0700, Allison Henderson wrote:
> > We need to add, remove or modify parent pointer attributes during
> > create/link/unlink/rename operations atomically with the dirents in
> > the
> > parent directories being modified. This means they need to be
> > modified
> > in the same transaction as the parent directories, and so we need
> > to add
> > the required space for the attribute modifications to the
> > transaction
> > reservations.
> > 
> > [achender: rebased, added xfs_sb_version_hasparent stub]
> > 
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_format.h     |   5 ++
> >  fs/xfs/libxfs/xfs_trans_resv.c | 103 +++++++++++++++++++++++++++
> > ------
> >  fs/xfs/libxfs/xfs_trans_resv.h |   1 +
> >  3 files changed, 90 insertions(+), 19 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_format.h
> > b/fs/xfs/libxfs/xfs_format.h
> > index afdfc8108c5f..96976497306c 100644
> > --- a/fs/xfs/libxfs/xfs_format.h
> > +++ b/fs/xfs/libxfs/xfs_format.h
> > @@ -390,6 +390,11 @@ xfs_sb_has_incompat_feature(
> >  	return (sbp->sb_features_incompat & feature) != 0;
> >  }
> >  
> > +static inline bool xfs_sb_version_hasparent(struct xfs_sb *sbp)
> > +{
> > +	return false; /* We'll enable this at the end of the set */
> > +}
> > +
> >  #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS   (1 << 0)	/* Delayed
> > Attributes */
> >  #define XFS_SB_FEAT_INCOMPAT_LOG_ALL \
> >  	(XFS_SB_FEAT_INCOMPAT_LOG_XATTRS)
> > diff --git a/fs/xfs/libxfs/xfs_trans_resv.c
> > b/fs/xfs/libxfs/xfs_trans_resv.c
> > index e9913c2c5a24..fbe46fd3b722 100644
> > --- a/fs/xfs/libxfs/xfs_trans_resv.c
> > +++ b/fs/xfs/libxfs/xfs_trans_resv.c
> > @@ -909,24 +909,30 @@ xfs_calc_sb_reservation(
> >  	return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
> >  }
> >  
> > -void
> > -xfs_trans_resv_calc(
> > +/*
> > + * Namespace reservations.
> > + *
> > + * These get tricky when parent pointers are enabled as we have
> > attribute
> > + * modifications occurring from within these transactions. Rather
> > than confuse
> > + * each of these reservation calculations with the conditional
> > attribute
> > + * reservations, add them here in a clear and concise manner. This
> > assumes that
> > + * the attribute reservations have already been calculated.
> > + *
> > + * Note that we only include the static attribute reservation
> > here; the runtime
> > + * reservation will have to be modified by the size of the
> > attributes being
> > + * added/removed/modified. See the comments on the attribute
> > reservation
> > + * calculations for more details.
> > + *
> > + * Note for rename: rename will vastly overestimate requirements.
> > This will be
> > + * addressed later when modifications are made to ensure parent
> > attribute
> > + * modifications can be done atomically with the rename operation.
> > + */
> > +STATIC void
> > +xfs_calc_namespace_reservations(
> >  	struct xfs_mount	*mp,
> >  	struct xfs_trans_resv	*resp)
> >  {
> > -	int			logcount_adj = 0;
> > -
> > -	/*
> > -	 * The following transactions are logged in physical format and
> > -	 * require a permanent reservation on space.
> > -	 */
> > -	resp->tr_write.tr_logres = xfs_calc_write_reservation(mp,
> > false);
> > -	resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
> > -	resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > -
> > -	resp->tr_itruncate.tr_logres =
> > xfs_calc_itruncate_reservation(mp, false);
> > -	resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
> > -	resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > +	ASSERT(resp->tr_attrsetm.tr_logres > 0);
> >  
> >  	resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
> >  	resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
> > @@ -948,15 +954,72 @@ xfs_trans_resv_calc(
> >  	resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
> >  	resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> >  
> > +	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
> > +	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
> > +	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > +
> > +	xfs_calc_parent_ptr_reservations(mp);
> > +}
> > +
> > +void xfs_calc_parent_ptr_reservations(struct xfs_mount     *mp)
> 
> Indenting, etc.
> 
> Also vaguely wondering if this should be static inline?  Or does
> something else call this?

Yes, I think it can be made static, nothing else needs to call it.  Thx
for the review!

Allison

> 
> --D
> 
> > +{
> > +	struct xfs_trans_resv   *resp = M_RES(mp);
> > +
> > +	/* Calculate extra space needed for parent pointer attributes
> > */
> > +	if (!xfs_sb_version_hasparent(&mp->m_sb))
> > +		return;
> > +
> > +	/* rename can add/remove/modify 4 parent attributes */
> > +	resp->tr_rename.tr_logres += 4 * max(resp-
> > >tr_attrsetm.tr_logres,
> > +					 resp->tr_attrrm.tr_logres);
> > +	resp->tr_rename.tr_logcount += 4 * max(resp-
> > >tr_attrsetm.tr_logcount,
> > +					   resp-
> > >tr_attrrm.tr_logcount);
> > +
> > +	/* create will add 1 parent attribute */
> > +	resp->tr_create.tr_logres += resp->tr_attrsetm.tr_logres;
> > +	resp->tr_create.tr_logcount += resp->tr_attrsetm.tr_logcount;
> > +
> > +	/* mkdir will add 1 parent attribute */
> > +	resp->tr_mkdir.tr_logres += resp->tr_attrsetm.tr_logres;
> > +	resp->tr_mkdir.tr_logcount += resp->tr_attrsetm.tr_logcount;
> > +
> > +	/* link will add 1 parent attribute */
> > +	resp->tr_link.tr_logres += resp->tr_attrsetm.tr_logres;
> > +	resp->tr_link.tr_logcount += resp->tr_attrsetm.tr_logcount;
> > +
> > +	/* symlink will add 1 parent attribute */
> > +	resp->tr_symlink.tr_logres += resp->tr_attrsetm.tr_logres;
> > +	resp->tr_symlink.tr_logcount += resp->tr_attrsetm.tr_logcount;
> > +
> > +	/* remove will remove 1 parent attribute */
> > +	resp->tr_remove.tr_logres += resp->tr_attrrm.tr_logres;
> > +	resp->tr_remove.tr_logcount += resp->tr_attrrm.tr_logcount;
> > +}
> > +
> > +void
> > +xfs_trans_resv_calc(
> > +	struct xfs_mount	*mp,
> > +	struct xfs_trans_resv	*resp)
> > +{
> > +	int			logcount_adj = 0;
> > +
> > +	/*
> > +	 * The following transactions are logged in physical format and
> > +	 * require a permanent reservation on space.
> > +	 */
> > +	resp->tr_write.tr_logres = xfs_calc_write_reservation(mp,
> > false);
> > +	resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
> > +	resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > +
> > +	resp->tr_itruncate.tr_logres =
> > xfs_calc_itruncate_reservation(mp, false);
> > +	resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
> > +	resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > +
> >  	resp->tr_create_tmpfile.tr_logres =
> >  			xfs_calc_create_tmpfile_reservation(mp);
> >  	resp->tr_create_tmpfile.tr_logcount =
> > XFS_CREATE_TMPFILE_LOG_COUNT;
> >  	resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> >  
> > -	resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
> > -	resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
> > -	resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > -
> >  	resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
> >  	resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
> >  	resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> > @@ -986,6 +1049,8 @@ xfs_trans_resv_calc(
> >  	resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
> >  	resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
> >  
> > +	xfs_calc_namespace_reservations(mp, resp);
> > +
> >  	/*
> >  	 * The following transactions are logged in logical format with
> >  	 * a default log count.
> > diff --git a/fs/xfs/libxfs/xfs_trans_resv.h
> > b/fs/xfs/libxfs/xfs_trans_resv.h
> > index 0554b9d775d2..cab8084a84d6 100644
> > --- a/fs/xfs/libxfs/xfs_trans_resv.h
> > +++ b/fs/xfs/libxfs/xfs_trans_resv.h
> > @@ -101,5 +101,6 @@ uint xfs_allocfree_block_count(struct xfs_mount
> > *mp, uint num_ops);
> >  unsigned int xfs_calc_itruncate_reservation_minlogsize(struct
> > xfs_mount *mp);
> >  unsigned int xfs_calc_write_reservation_minlogsize(struct
> > xfs_mount *mp);
> >  unsigned int xfs_calc_qm_dqalloc_reservation_minlogsize(struct
> > xfs_mount *mp);
> > +void xfs_calc_parent_ptr_reservations(struct xfs_mount *mp);
> >  
> >  #endif	/* __XFS_TRANS_RESV_H__ */
> > -- 
> > 2.25.1
> > 


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

* Re: [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK
  2022-06-29  6:33           ` Alli
@ 2022-06-30  0:40             ` Darrick J. Wong
  0 siblings, 0 replies; 58+ messages in thread
From: Darrick J. Wong @ 2022-06-30  0:40 UTC (permalink / raw)
  To: Alli; +Cc: Dave Chinner, linux-xfs

On Tue, Jun 28, 2022 at 11:33:49PM -0700, Alli wrote:
> On Thu, 2022-06-16 at 15:32 +1000, Dave Chinner wrote:
> > On Thu, Jun 16, 2022 at 12:08:43PM +1000, Dave Chinner wrote:
> > > On Wed, Jun 15, 2022 at 04:40:07PM -0700, Alli wrote:
> > > > On Wed, 2022-06-15 at 11:09 +1000, Dave Chinner wrote:
> > > > > On Sat, Jun 11, 2022 at 02:41:44AM -0700, Allison Henderson
> > > > > wrote:
> > > > > > Recent parent pointer testing has exposed a bug in the
> > > > > > underlying
> > > > > > larp state machine.  A replace operation may remove an old
> > > > > > attr
> > > > > > before adding the new one, but if it is the only attr in the
> > > > > > fork,
> > > > > > then the fork is removed.  This later causes a null pointer
> > > > > > in
> > > > > > xfs_attr_try_sf_addname which expects the fork present.  This
> > > > > > patch adds an extra state to create the fork.
> > > > > 
> > > > > Hmmmm.
> > > > > 
> > > > > I thought I fixed those problems - in xfs_attr_sf_removename()
> > > > > there
> > > > > is this code:
> > > > > 
> > > > >         if (totsize == sizeof(xfs_attr_sf_hdr_t) &&
> > > > > xfs_has_attr2(mp)
> > > > > &&
> > > > >             (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
> > > > >             !(args->op_flags & (XFS_DA_OP_ADDNAME |
> > > > > XFS_DA_OP_REPLACE))) {
> > > > >                 xfs_attr_fork_remove(dp, args->trans);
> > > > Hmm, ok, let me shuffle in some traces around there to see where
> > > > things
> > > > fall off the rails
> > > > 
> > > > > A replace operation will have XFS_DA_OP_REPLACE set, and so the
> > > > > final remove from a sf directory will not remove the attr fork
> > > > > in
> > > > > this case. There is equivalent checks in the leaf/node remove
> > > > > name
> > > > > paths to avoid removing the attr fork if the last attr is
> > > > > removed
> > > > > while the attr fork is in those formats.
> > > > > 
> > > > > How do you reproduce this issue?
> > > > > 
> > > > 
> > > > Sure, you can apply this kernel set or download it here:
> > > > https://urldefense.com/v3/__https://github.com/allisonhenderson/xfs/tree/xfs_new_pptrs__;!!ACWV5N9M2RV99hQ!O7bhq9bR_z2xjlhlwWb78ZXwzigOh6P8V3_EkeL9AHFOpdYdr_irAwUocygT_G8LI-lKDL5_01Df49lTy27a$ 
> > > > 
> > > > Next you'll need this xfsprogs that has the neccassary updates to
> > > > run
> > > > parent pointers
> > > > https://urldefense.com/v3/__https://github.com/allisonhenderson/xfsprogs/tree/xfsprogs_new_pptrs__;!!ACWV5N9M2RV99hQ!O7bhq9bR_z2xjlhlwWb78ZXwzigOh6P8V3_EkeL9AHFOpdYdr_irAwUocygT_G8LI-lKDL5_01Df4w3Mct8F$ 
> > > > 
> > > > 
> > > > To reproduce the bug, you'll need to apply a quick patch on the
> > > > kernel
> > > > side:
> > > > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > > > index b86188b63897..f279afd43462 100644
> > > > --- a/fs/xfs/libxfs/xfs_attr.c
> > > > +++ b/fs/xfs/libxfs/xfs_attr.c
> > > > @@ -741,8 +741,8 @@ xfs_attr_set_iter(
> > > >  		fallthrough;
> > > >  	case XFS_DAS_SF_ADD:
> > > >  		if (!args->dp->i_afp) {
> > > > -			attr->xattri_dela_state = XFS_DAS_CREATE_FORK;
> > > > -			goto next_state;
> > > > +//			attr->xattri_dela_state =
> > > > XFS_DAS_CREATE_FORK;
> > > > +//			goto next_state;
> > > >  		}
> > > 
> > > Ah, so it's recovery that trips this....
> > > 
> > > > [  365.290048]  xfs_attr_try_sf_addname+0x2a/0xd0 [xfs]
> > > > [  365.290423]  xfs_attr_set_iter+0x2f9/0x1510 [xfs]
> > > > [  365.291592]  xfs_xattri_finish_update+0x66/0xd0 [xfs]
> > > > [  365.292008]  xfs_attr_finish_item+0x43/0x120 [xfs]
> > > > [  365.292410]  xfs_defer_finish_noroll+0x3c2/0xcc0 [xfs]
> > > > [  365.293196]  __xfs_trans_commit+0x333/0x610 [xfs]
> > > > [  365.294401]  xfs_trans_commit+0x10/0x20 [xfs]
> > > > [  365.294797]  xlog_finish_defer_ops+0x133/0x270 [xfs]
> > > > [  365.296054]  xlog_recover_process_intents+0x1f7/0x3e0 [xfs]
> > > 
> > > ayup.
> > > 
> > > > > > Additionally the new state will be used by parent pointers
> > > > > > which
> > > > > > need to add attributes to newly created inodes that do not
> > > > > > yet
> > > > > > have a fork.
> > > > > 
> > > > > We already have the capability of doing that in
> > > > > xfs_init_new_inode()
> > > > > by passing in init_xattrs == true. So when we are creating a
> > > > > new
> > > > > inode with parent pointers enabled, we know that we are going
> > > > > to be
> > > > > creating an xattr on the inode and so we should always set
> > > > > init_xattrs in that case.
> > > > Hmm, ok.  I'll add some tracing around in there too, if I back
> > > > out the
> > > > entire first patch, we crash out earlier in recovery path because
> > > > no
> > > > state is set.  If we enter xfs_attri_item_recover with no fork,
> > > > we end
> > > > up in the following switch:
> > > > 
> > > > 
> > > >         case XFS_ATTRI_OP_FLAGS_REPLACE:
> > > >                 args->value = nv- >value.i_addr;
> > > >                 args->valuelen = nv- >value.i_len;
> > > >                 args->total = xfs_attr_calc_size(args, &local);
> > > >                 if (xfs_inode_hasattr(args- >dp))
> > > >                         attr->xattri_dela_state =
> > > > xfs_attr_init_replace_state(args);
> > > >                 else
> > > >                         attr->xattri_dela_state =
> > > > xfs_attr_init_add_state(args);
> > > >                 break;
> > > > 
> > > > Which will leave the state unset if the fork is absent.
> > > 
> > > Yeah, OK, I think this is because we are combining attribute
> > > creation with inode creation. When log recovery replays inode core
> > > modifications, it replays the inode state into the cluster buffer
> > > and writes it. Then when we go to replay the attr intent at the end
> > > of recovery, the inode is read from disk via xlog_recover_iget(),
> > > but we don't initialise the attr fork because ip->i_forkoff is
> > > zero.
> > > i.e. it has no attrs at this point.
> > > 
> > > I suspect that we could catch that in xlog_recover_iget() when it
> > > is
> > > called from attr recovery. i.e. we detect newly created inodes and
> > > initialise the attr fork similar to what we do in
> > > xfs_init_new_inode(). I was thinking something like this:
> > > 
> > > 	if (init_xattrs && xfs_has_attr(mp)) {
> > > 		if (!ip->i_forkoff && !ip->i_nextents) {
> > > 			ip->i_forkoff = xfs_default_attroffset(ip) >>
> > > 3;
> > > 			ip->i_afp =
> > > xfs_ifork_alloc(XFS_DINODE_FMT_EXTENTS, 0);
> > > 		} else {
> > > 			ASSERT(ip->i_afp);
> > > 		}
> > > 	}
> > > 
> > > Would do the trick, but then I realised that the timing/ordering is
> > > very different to runtime: we don't replay the attr intent until
> > > the
> > > end of log recovery and all the inode changes have been replayed
> > > into the inode cluster buffer. That means we could have already
> > > replayed a bunch of data fork extent modifications into the inode,
> > > and so the default attr offset is almost certainly not a safe thing
> > > to be using here. Indeed, there might not be space in the inode for
> > > the attr we want to insert and so we might need to convert the data
> > > fork to a different format before we run the attr intent replay.
> > 
> > Ok, so after further thought, I don't think this can happen. If we
> > are replaying an attr intent, it means we crashed before the intent
> > done was recorded in the log. At this point in time the inode was
> > locked and so there could be no racing changes to the inode data
> > fork in the log. i.e. because of log item ordering, if the intent
> > done is not in the log, none of the future changes that occurred
> > after the intent done will be in the log, either. The inode on disk
> > will not contain them either because the intent done must be in the
> > log before the inode gets unpinned and is able to be written to
> > disk.
> > 
> > Hence if we've got an attr intent to replay, it must be the last
> > active modification to that inode that must be replayed, and the
> > state of the inode on disk at the time of recovering the attr intent
> > should match the state of the inode in memory at the time the attr
> > intent was started.
> > 
> > Hence there isn't a consistency model coherency problem here, and
> > that means if there's no attr fork at the time the attr recovery is
> > started, it *must* be a newly created inode. If the inode already
> > existed and a transaction had to be run to create the attr fork
> > (i.e. xfs_bmap_add_attrfork() had to be run) then that transaction
> > would have been recovered from the log before attr replay started,
> > and so xlog_recovery_iget() should see a non-zero ip->i_forkoff and
> > initialise the attr fork correctly.
> > 
> > But this makes me wonder further. If the attr intent is logged in
> > the same transaction as the inode is allocated, then the setting of
> > ip->i_forkoff in xfs_init_new_inode() should also be logged in that
> > transaction (because XFS_ILOG_CORE is used) and hence be replayed
> > into the on-disk inode by recovery before the attr intent recovery
> > starts. Hence xlog_recover_iget() should be initialising the attr
> > fork through this path:
> > 
> > xlog_recover_iget
> >   xfs_iget
> >     xfs_iget_cache_miss
> >       xfs_inode_from_disk
> > 	if (ip->i_forkoff)
> > 	  xfs_iformat_attr_fork()
> > 
> > This means the newly allocated inode would have an attr fork
> > allocated to it, in extent format with zero extents. If we then look
> > at xfs_inode_hasattr():
> > 
> > int
> > xfs_inode_hasattr(
> >         struct xfs_inode        *ip)
> > {
> >         if (!XFS_IFORK_Q(ip))
> >                 return 0;
> >         if (!ip->i_afp)
> >                 return 0;
> > > > > > >   if (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS &&
> > > > > > >       ip->i_afp->if_nextents == 0)
> > > > > > >           return 0;
> >         return 1;
> > }
> > 
> > It would still say that it has no attrs even though the fork has
> > been initialised and we go down the xfs_attr_init_add_state()
> > branch.  This does:
> > 
> > static inline enum xfs_delattr_state
> > xfs_attr_init_add_state(struct xfs_da_args *args)
> > {
> >         /*
> >          * When called from the completion of a attr remove to
> > determine the
> >          * next state, the attribute fork may be null. This can occur
> > only occur
> >          * on a pure remove, but we grab the next state before we
> > check if a
> >          * replace operation is being performed. If we are called
> > from any other
> >          * context, i_afp is guaranteed to exist. Hence if the attr
> > fork is
> >          * null, we were called from a pure remove operation and so
> > we are done.
> >          */
> > > > > >    if (!args->dp->i_afp)
> > > > > >            return XFS_DAS_DONE;
> > 
> >         args->op_flags |= XFS_DA_OP_ADDNAME;
> >         if (xfs_attr_is_shortform(args->dp))
> >                 return XFS_DAS_SF_ADD;
> >         if (xfs_attr_is_leaf(args->dp))
> >                 return XFS_DAS_LEAF_ADD;
> >         return XFS_DAS_NODE_ADD;
> > }
> > 
> > Which would return XFS_DAS_DONE if there was no attr fork
> > initialised and recovery would be skipped completely. Hence for this
> > change to be required to trigger failures:
> > 
> > > > @@ -741,8 +741,8 @@ xfs_attr_set_iter(
> > > >             fallthrough;
> > > >     case XFS_DAS_SF_ADD:
> > > >             if (!args->dp->i_afp) {
> > > > -                   attr->xattri_dela_state =
> > > > XFS_DAS_CREATE_FORK;
> > > > -                   goto next_state;
> > > > +//                 attr->xattri_dela_state =
> > > > XFS_DAS_CREATE_FORK;
> > > > +//                 goto next_state;
> > > >             }
> > 
> > then it implies the only way we can get here is via a replace
> > operation that has removed the attr fork during the remove and we
> > hit this on the attr add. Yet, AFAICT, the attr fork does not get
> > removed when a replace operation is in progress.
> > 
> > Maybe there's a new bug introduced in the PP patchset that triggers
> > this - I'll do some more looking...
> > 
> > > > > This should avoid the need for parent pointers to ever need to
> > > > > run
> > > > > an extra transaction to create the attr fork. Hence, AFAICT,
> > > > > this
> > > > > new state to handle attr fork creation shouldn't ever be needed
> > > > > for
> > > > > parent pointers....
> > > > > 
> > > > > What am I missing?
> > > > > 
> > > > I hope the description helped?  I'll do some more poking around
> > > > too and
> > > > post back if I find anything else.
> > > 
> > > Yup, it most definitely helped. :)
> > > 
> > > You've pointed out something I had completely missed w.r.t. attr
> > > intent replay ordering against replay of data fork modifications.
> > > There's definitely an issue here, I think it might be a fundamental
> > > issue with the recovery mechanism (and not parent pointers), and I
> > > think we'll end up needing  something like this patch to fix it.
> > > Let me bounce this around my head for a bit...
> > 
> > In summary, after further thought this turns out not to be an issue
> > at all, so I'm back to "replace doesn't remove the attr fork, so
> > how does this happen?"....
> Just a follow up here...
> 
> So, I think what is happening is we are getting interleaved replays.
> When a parent changes, the parent pointer attribute needs to be
> updated.  But since this changes the attribute name, it cant be an attr
> replace.  So we queue up an attr set and then a remove (in xfs_rename).
> The test case sets the inject which starts a replay of a set and
> remove.  The set expands the leaf, and bounces out the EAGAIN
> into xfs_attri_item_recover.  We then fall into this error handling:
> 
>         ret = xfs_xattri_finish_update(attr,
> done_item);                        
>         if (ret == -EAGAIN)
> {                                                   
>                 /* There's more work to do, so add it to this
> transaction */    
>                 xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr-
> >xattri_list);
>         }
> 
> Which I think is queuing up the rest of the set operation, but on the
> other side of the remove operation.  The remove operation removes the
> fork, and we get the null pointer when the set operation resumes.
> 
> I havnt quite worked out a fix yet, but I'm pretty sure that's the
> problem description....  I think what we're going to need is some sort
> of defer_push operation or something similar.

Hm.  To summarize (poorly) a conversation we had on IRC, I guess the
problem here is that xfs_rename logs both an attr-set and an attr-remove
log item to a rename transaction.  Then log recovery finds both, starts
the first step of the attr-set, and we capture-and-commit the rest of
the chain.  Next it recovers the the attr-remove, starts the first step
of /that/ (which clobbers the attr fork) and capture-and-commits that.
Finally, we continue the attr-set, which promptly barfs because the attr
fork disappeared on it!

So I guess this is a gaping hole in how log recovery works because we
don't write enough information into the log intent items to be able to
reconstruct the full defer ops chains that were attached to a
transaction.  If we could do that, then we'd know that there exists a
data dependency between the set and the remove such that we must finish
the entire set chain before moving on to the remove chain.

However, we don't know this.  Thus far in the lifespan of defer ops, I
think we've been careful/lucky enough that we've never had this problem,
and reworking the log format to encode transaction chain details is
quite a lot of work.

---

A second thought -- the xattri log recovery routine recreates the defer
ops state, logs an xattrd for the recovered item, takes one step in the
state machine, and uses capture-and-commit to defer any more steps to a
later time.  What if instead the recovery routine didn't take that extra
step?  If all it did was recreate the incore defer ops state and
deferred everything else to xlog_finish_defer_ops, the attr set and attr
remove recoveries would not overlap and trip over each other.

Maybe a POC would be to have the _recover function log the xattrd for
the recovered log item and call xfs_defer_ops_capture_and_commit without
actually bothering with xfs_attri_finish_update?  Something like this?

	xfs_init_attr_trans(args, &tres, &total);
	error = xfs_trans_alloc(mp, &tres, total, 0, XFS_TRANS_RESERVE,
			&tp);
	if (error)
		goto out;

	args->trans = tp;
	done_item = xfs_trans_get_attrd(tp, attrip);

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, 0);

	xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr->xattri_list);
	error = xfs_defer_ops_capture_and_commit(tp, capture_list);
	if (error)
		goto out_unlock;

	xfs_iunlock(ip, XFS_ILOCK_EXCL);
	xfs_irele(ip);
	return 0;

---

A third thought I had for solving this problem would be to extend the
xattr log format to be able to record a "rename and rewrite" operation.
I only suggest this because directory online repair currently works by
creating a temporary file, using the regular xfs_dir_createname function
to build the directory structures (without touching the children) and
then uses swapext to exchange the directory structure blocks.  When
parent pointers come along, I guess we'll have to figure out how to
insert dirents at specific offsets, or we'll have to figure out how to
update all the parent pointers atomically.  Yuck.

While a "rename and reset" xattr op would work here, I suppose the
online repair case is a lot simpler -- it never needs to update the
dirent name, so all we really have to do is rewrite the xattr key,
remove the dabtree hash mapping, and insert a new one.

Hmm ok I'm going to go eat dinner and think about my second suggestion
tomorrow.

--D

> Allison
> 
> > 
> > Cheers,
> > 
> > Dave.
> > 
> 

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

* Re: [PATCH v1 10/17] xfs: parent pointer attribute creation
  2022-06-29 18:41   ` Darrick J. Wong
@ 2022-06-30  1:29     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-30  1:29 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, 2022-06-29 at 11:41 -0700, Darrick J. Wong wrote:
> On Sat, Jun 11, 2022 at 02:41:53AM -0700, Allison Henderson wrote:
> > Add parent pointer attribute during xfs_create, and subroutines to
> > initialize attributes
> > 
> > [bfoster: rebase, use VFS inode generation]
> > [achender: rebased, changed __unint32_t to xfs_dir2_dataptr_t,
> >            fixed some null pointer bugs,
> >            merged error handling patch,
> >            added subroutines to handle attribute initialization,
> >            remove unnecessary ENOSPC handling in
> > xfs_attr_set_first_parent]
> > 
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/Makefile            |  1 +
> >  fs/xfs/libxfs/xfs_attr.c   |  2 +-
> >  fs/xfs/libxfs/xfs_attr.h   |  1 +
> >  fs/xfs/libxfs/xfs_parent.c | 77 +++++++++++++++++++++++++++++++++
> >  fs/xfs/libxfs/xfs_parent.h | 31 ++++++++++++++
> >  fs/xfs/xfs_inode.c         | 88 +++++++++++++++++++++++++++-------
> > ----
> >  fs/xfs/xfs_xattr.c         |  2 +-
> >  fs/xfs/xfs_xattr.h         |  1 +
> >  8 files changed, 177 insertions(+), 26 deletions(-)
> > 
> > diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> > index b056cfc6398e..fc717dc3470c 100644
> > --- a/fs/xfs/Makefile
> > +++ b/fs/xfs/Makefile
> > @@ -40,6 +40,7 @@ xfs-y				+= $(addprefix
> > libxfs/, \
> >  				   xfs_inode_fork.o \
> >  				   xfs_inode_buf.o \
> >  				   xfs_log_rlimit.o \
> > +				   xfs_parent.o \
> >  				   xfs_ag_resv.o \
> >  				   xfs_rmap.o \
> >  				   xfs_rmap_btree.o \
> > diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
> > index 30c8d9e9c2f1..f814a9177237 100644
> > --- a/fs/xfs/libxfs/xfs_attr.c
> > +++ b/fs/xfs/libxfs/xfs_attr.c
> > @@ -926,7 +926,7 @@ xfs_attr_intent_init(
> >  }
> >  
> >  /* Sets an attribute for an inode as a deferred operation */
> > -static int
> > +int
> >  xfs_attr_defer_add(
> >  	struct xfs_da_args	*args)
> >  {
> > diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
> > index a87bc503976b..576062e37d11 100644
> > --- a/fs/xfs/libxfs/xfs_attr.h
> > +++ b/fs/xfs/libxfs/xfs_attr.h
> > @@ -559,6 +559,7 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
> >  bool xfs_attr_is_leaf(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_defer_add(struct xfs_da_args *args);
> >  int xfs_attr_set(struct xfs_da_args *args);
> >  int xfs_attr_set_iter(struct xfs_attr_intent *attr);
> >  int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
> > diff --git a/fs/xfs/libxfs/xfs_parent.c
> > b/fs/xfs/libxfs/xfs_parent.c
> > new file mode 100644
> > index 000000000000..cb546652bde9
> > --- /dev/null
> > +++ b/fs/xfs/libxfs/xfs_parent.c
> > @@ -0,0 +1,77 @@
> > +/*
> 
> New files need an SPDX header.
ok, will fix
> 
> > + * Copyright (c) 2015 Red Hat, Inc.
> > + * All rights reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it would be
> > useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program; if not, write the Free Software
> > Foundation
> 
> No need for all this boilerplate once you've switched to SPDX tags.
> 
yep, will remove
> > + */
> > +#include "xfs.h"
> > +#include "xfs_fs.h"
> > +#include "xfs_format.h"
> > +#include "xfs_da_format.h"
> > +#include "xfs_log_format.h"
> > +#include "xfs_shared.h"
> > +#include "xfs_trans_resv.h"
> > +#include "xfs_mount.h"
> > +#include "xfs_bmap_btree.h"
> > +#include "xfs_inode.h"
> > +#include "xfs_error.h"
> > +#include "xfs_trace.h"
> > +#include "xfs_trans.h"
> > +#include "xfs_da_btree.h"
> > +#include "xfs_attr.h"
> > +#include "xfs_da_btree.h"
> > +#include "xfs_attr_sf.h"
> > +#include "xfs_bmap.h"
> > +
> > +/*
> > + * Parent pointer attribute handling.
> > + *
> > + * Because the attribute value is a filename component, it will
> > never be longer
> > + * than 255 bytes. This means the attribute will always be a local
> > format
> > + * attribute as it is xfs_attr_leaf_entsize_local_max() for v5
> > filesystems will
> > + * always be larger than this (max is 75% of block size).
> > + *
> > + * Creating a new parent attribute will always create a new
> > attribute - there
> > + * should never, ever be an existing attribute in the tree for a
> > new inode.
> > + * ENOSPC behavior is problematic - creating the inode without the
> > parent
> > + * pointer is effectively a corruption, so we allow parent
> > attribute creation
> > + * to dip into the reserve block pool to avoid unexpected ENOSPC
> > errors from
> > + * occurring.
> > + */
> > +
> > +
> > +/* Initializes a xfs_parent_name_rec to be stored as an attribute
> > name */
> > +void
> > +xfs_init_parent_name_rec(
> > +	struct xfs_parent_name_rec	*rec,
> > +	struct xfs_inode		*ip,
> > +	uint32_t			p_diroffset)
> > +{
> > +	xfs_ino_t			p_ino = ip->i_ino;
> > +	uint32_t			p_gen = VFS_I(ip)->i_generation;
> > +
> > +	rec->p_ino = cpu_to_be64(p_ino);
> > +	rec->p_gen = cpu_to_be32(p_gen);
> > +	rec->p_diroffset = cpu_to_be32(p_diroffset);
> > +}
> > +
> > +/* Initializes a xfs_parent_name_irec from an xfs_parent_name_rec
> > */
> > +void
> > +xfs_init_parent_name_irec(
> > +	struct xfs_parent_name_irec	*irec,
> > +	struct xfs_parent_name_rec	*rec)
> 
> Should this second arg be const struct xfs_parent_name_rec* ?
> 
I guess it could be, but now that I look harder at it, it's not
actually used.  I suspect it was likely just a suggestion from an older
review to add it as a sort of utility function, but with out an actuall
call, maybe we should just take it out

> > +{
> > +	irec->p_ino = be64_to_cpu(rec->p_ino);
> > +	irec->p_gen = be32_to_cpu(rec->p_gen);
> > +	irec->p_diroffset = be32_to_cpu(rec->p_diroffset);
> > +}
> > diff --git a/fs/xfs/libxfs/xfs_parent.h
> > b/fs/xfs/libxfs/xfs_parent.h
> > new file mode 100644
> > index 000000000000..10dc576ce693
> > --- /dev/null
> > +++ b/fs/xfs/libxfs/xfs_parent.h
> > @@ -0,0 +1,31 @@
> > +/*
> > + * Copyright (c) 2018 Oracle, Inc.
> 
> New file needs an SPDX header, and you should probably update the
> copyright to be 2018-2022.
will fix

> 
> > + * All Rights Reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it would be
> > useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program; if not, write the Free Software
> > Foundation Inc.
> 
> No need for all this boilerplate once you've switched to SPDX tags.
> 
> > + */
> > +#ifndef	__XFS_PARENT_H__
> > +#define	__XFS_PARENT_H__
> > +
> > +#include "xfs_da_format.h"
> > +#include "xfs_format.h"
> 
> Don't include headers in headers.  If it's really a mess to add these
> two to every single .c file, then just add:
> 
> struct xfs_inode;
> struct xfs_parent_name_rec;
> struct xfs_parent_name_irec;
> 
> and that'll do for pointers.
> 
Alrighty, will do

> > +
> > +/*
> > + * Parent pointer attribute prototypes
> > + */
> > +void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec,
> > +			      struct xfs_inode *ip,
> > +			      uint32_t p_diroffset);
> > +void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
> > +			       struct xfs_parent_name_rec *rec);
> > +#endif	/* __XFS_PARENT_H__ */
> > diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> > index b2dfd84e1f62..6b1e4cb11b5c 100644
> > --- a/fs/xfs/xfs_inode.c
> > +++ b/fs/xfs/xfs_inode.c
> > @@ -36,6 +36,8 @@
> >  #include "xfs_reflink.h"
> >  #include "xfs_ag.h"
> >  #include "xfs_log_priv.h"
> > +#include "xfs_parent.h"
> > +#include "xfs_xattr.h"
> >  
> >  struct kmem_cache *xfs_inode_cache;
> >  
> > @@ -962,27 +964,40 @@ xfs_bumplink(
> >  
> >  int
> >  xfs_create(
> > -	struct user_namespace	*mnt_userns,
> > -	xfs_inode_t		*dp,
> > -	struct xfs_name		*name,
> > -	umode_t			mode,
> > -	dev_t			rdev,
> > -	bool			init_xattrs,
> > -	xfs_inode_t		**ipp)
> > -{
> > -	int			is_dir = S_ISDIR(mode);
> > -	struct xfs_mount	*mp = dp->i_mount;
> > -	struct xfs_inode	*ip = NULL;
> > -	struct xfs_trans	*tp = NULL;
> > -	int			error;
> > -	bool                    unlock_dp_on_error = false;
> > -	prid_t			prid;
> > -	struct xfs_dquot	*udqp = NULL;
> > -	struct xfs_dquot	*gdqp = NULL;
> > -	struct xfs_dquot	*pdqp = NULL;
> > -	struct xfs_trans_res	*tres;
> > -	uint			resblks;
> > -	xfs_ino_t		ino;
> > +	struct user_namespace		*mnt_userns,
> > +	xfs_inode_t			*dp,
> 
> Convert the struct typedefs...
> 
Ok, thx for the reviews!
Allison

> --D
> 
> > +	struct xfs_name			*name,
> > +	umode_t				mode,
> > +	dev_t				rdev,
> > +	bool				init_xattrs,
> > +	xfs_inode_t			**ipp)
> > +{
> > +	int				is_dir = S_ISDIR(mode);
> > +	struct xfs_mount		*mp = dp->i_mount;
> > +	struct xfs_inode		*ip = NULL;
> > +	struct xfs_trans		*tp = NULL;
> > +	int				error;
> > +	bool				unlock_dp_on_error = false;
> > +	prid_t				prid;
> > +	struct xfs_dquot		*udqp = NULL;
> > +	struct xfs_dquot		*gdqp = NULL;
> > +	struct xfs_dquot		*pdqp = NULL;
> > +	struct xfs_trans_res		*tres;
> > +	uint				resblks;
> > +	xfs_ino_t			ino;
> > +	xfs_dir2_dataptr_t		diroffset;
> > +	struct xfs_parent_name_rec	rec;
> > +	struct xfs_da_args		args = {
> > +		.dp		= dp,
> > +		.geo		= mp->m_attr_geo,
> > +		.whichfork	= XFS_ATTR_FORK,
> > +		.attr_filter	= XFS_ATTR_PARENT,
> > +		.op_flags	= XFS_DA_OP_OKNOENT,
> > +		.name		= (const uint8_t *)&rec,
> > +		.namelen	= sizeof(rec),
> > +		.value		= (void *)name->name,
> > +		.valuelen	= name->len,
> > +	};
> >  
> >  	trace_xfs_create(dp, name);
> >  
> > @@ -1009,6 +1024,12 @@ xfs_create(
> >  		tres = &M_RES(mp)->tr_create;
> >  	}
> >  
> > +	if (xfs_has_larp(mp)) {
> > +		error = xfs_attr_grab_log_assist(mp);
> > +		if (error)
> > +			goto out_release_dquots;
> > +	}
> > +
> >  	/*
> >  	 * Initially assume that the file does not exist and
> >  	 * reserve the resources for that case.  If that is not
> > @@ -1024,7 +1045,7 @@ xfs_create(
> >  				resblks, &tp);
> >  	}
> >  	if (error)
> > -		goto out_release_dquots;
> > +		goto drop_incompat;
> >  
> >  	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
> >  	unlock_dp_on_error = true;
> > @@ -1048,11 +1069,12 @@ xfs_create(
> >  	 * the transaction cancel unlocking dp so don't do it
> > explicitly in the
> >  	 * error path.
> >  	 */
> > -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
> > +	xfs_trans_ijoin(tp, dp, 0);
> >  	unlock_dp_on_error = false;
> >  
> >  	error = xfs_dir_createname(tp, dp, name, ip->i_ino,
> > -				   resblks - XFS_IALLOC_SPACE_RES(mp),
> > NULL);
> > +				   resblks - XFS_IALLOC_SPACE_RES(mp),
> > +				   &diroffset);
> >  	if (error) {
> >  		ASSERT(error != -ENOSPC);
> >  		goto out_trans_cancel;
> > @@ -1068,6 +1090,20 @@ xfs_create(
> >  		xfs_bumplink(tp, dp);
> >  	}
> >  
> > +	/*
> > +	 * If we have parent pointers, we need to add the attribute
> > containing
> > +	 * the parent information now.
> > +	 */
> > +	if (xfs_sb_version_hasparent(&mp->m_sb)) {
> > +		xfs_init_parent_name_rec(&rec, dp, diroffset);
> > +		args.dp	= ip;
> > +		args.trans = tp;
> > +		args.hashval = xfs_da_hashname(args.name,
> > args.namelen);
> > +		error =  xfs_attr_defer_add(&args);
> > +		if (error)
> > +			goto out_trans_cancel;
> > +	}
> > +
> >  	/*
> >  	 * If this is a synchronous mount, make sure that the
> >  	 * create transaction goes to disk before returning to
> > @@ -1093,6 +1129,7 @@ xfs_create(
> >  
> >  	*ipp = ip;
> >  	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> > +	xfs_iunlock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
> >  	return 0;
> >  
> >   out_trans_cancel:
> > @@ -1107,6 +1144,9 @@ xfs_create(
> >  		xfs_finish_inode_setup(ip);
> >  		xfs_irele(ip);
> >  	}
> > + drop_incompat:
> > +	if (xfs_has_larp(mp))
> > +		xlog_drop_incompat_feat(mp->m_log);
> >   out_release_dquots:
> >  	xfs_qm_dqrele(udqp);
> >  	xfs_qm_dqrele(gdqp);
> > diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
> > index 35e13e125ec6..6012a6ba512c 100644
> > --- a/fs/xfs/xfs_xattr.c
> > +++ b/fs/xfs/xfs_xattr.c
> > @@ -27,7 +27,7 @@
> >   * they must release the permission by calling
> > xlog_drop_incompat_feat
> >   * when they're done.
> >   */
> > -static inline int
> > +inline int
> >  xfs_attr_grab_log_assist(
> >  	struct xfs_mount	*mp)
> >  {
> > diff --git a/fs/xfs/xfs_xattr.h b/fs/xfs/xfs_xattr.h
> > index 2b09133b1b9b..3fd6520a4d69 100644
> > --- a/fs/xfs/xfs_xattr.h
> > +++ b/fs/xfs/xfs_xattr.h
> > @@ -7,6 +7,7 @@
> >  #define __XFS_XATTR_H__
> >  
> >  int xfs_attr_change(struct xfs_da_args *args);
> > +int xfs_attr_grab_log_assist(struct xfs_mount *mp);
> >  
> >  extern const struct xattr_handler *xfs_xattr_handlers[];
> >  
> > -- 
> > 2.25.1
> > 


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

* Re: [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init
  2022-06-29 18:42   ` Darrick J. Wong
@ 2022-06-30  1:30     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-30  1:30 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, 2022-06-29 at 11:42 -0700, Darrick J. Wong wrote:
> On Sat, Jun 11, 2022 at 02:41:58AM -0700, Allison Henderson wrote:
> > This patch adds a helper function xfs_attr_list_context_init used
> > by
> > xfs_attr_list. This function initializes the xfs_attr_list_context
> > structure passed to xfs_attr_list_int. We will need this later to
> > call
> > xfs_attr_list_int_ilocked when the node is already locked.
> 
> Since you've mentioned the xattr userspace functions -- does our
> current
> codebase hide the parent pointer xattrs from regular
> getxattr/setxattr/listxattr system calls?
I don't think it's hidden, but it has the XFS_ATTR_PARENT flag set so
that the caller can know to handle it differently than a normal string
attr. 

> 
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> 
> The change itself looks pretty straightfoward,
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> 
Great, thanks!
Allison

> --D
> 
> > ---
> >  fs/xfs/xfs_file.c  |  1 +
> >  fs/xfs/xfs_ioctl.c | 54 ++++++++++++++++++++++++++++++++--------
> > ------
> >  fs/xfs/xfs_ioctl.h |  2 ++
> >  3 files changed, 41 insertions(+), 16 deletions(-)
> > 
> > diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> > index e2f2a3a94634..884827f024fd 100644
> > --- a/fs/xfs/xfs_file.c
> > +++ b/fs/xfs/xfs_file.c
> > @@ -17,6 +17,7 @@
> >  #include "xfs_bmap_util.h"
> >  #include "xfs_dir2.h"
> >  #include "xfs_dir2_priv.h"
> > +#include "xfs_attr.h"
> >  #include "xfs_ioctl.h"
> >  #include "xfs_trace.h"
> >  #include "xfs_log.h"
> > diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> > index 5a364a7d58fd..e1612e99e0c5 100644
> > --- a/fs/xfs/xfs_ioctl.c
> > +++ b/fs/xfs/xfs_ioctl.c
> > @@ -369,6 +369,40 @@ xfs_attr_flags(
> >  	return 0;
> >  }
> >  
> > +/*
> > + * Initializes an xfs_attr_list_context suitable for
> > + * use by xfs_attr_list
> > + */
> > +int
> > +xfs_ioc_attr_list_context_init(
> > +	struct xfs_inode		*dp,
> > +	char				*buffer,
> > +	int				bufsize,
> > +	int				flags,
> > +	struct xfs_attr_list_context	*context)
> > +{
> > +	struct xfs_attrlist		*alist;
> > +
> > +	/*
> > +	 * Initialize the output buffer.
> > +	 */
> > +	context->dp = dp;
> > +	context->resynch = 1;
> > +	context->attr_filter = xfs_attr_filter(flags);
> > +	context->buffer = buffer;
> > +	context->bufsize = round_down(bufsize, sizeof(uint32_t));
> > +	context->firstu = context->bufsize;
> > +	context->put_listent = xfs_ioc_attr_put_listent;
> > +
> > +	alist = context->buffer;
> > +	alist->al_count = 0;
> > +	alist->al_more = 0;
> > +	alist->al_offset[0] = context->bufsize;
> > +
> > +	return 0;
> > +}
> > +
> > +
> >  int
> >  xfs_ioc_attr_list(
> >  	struct xfs_inode		*dp,
> > @@ -378,7 +412,6 @@ xfs_ioc_attr_list(
> >  	struct xfs_attrlist_cursor __user *ucursor)
> >  {
> >  	struct xfs_attr_list_context	context = { };
> > -	struct xfs_attrlist		*alist;
> >  	void				*buffer;
> >  	int				error;
> >  
> > @@ -410,21 +443,10 @@ xfs_ioc_attr_list(
> >  	if (!buffer)
> >  		return -ENOMEM;
> >  
> > -	/*
> > -	 * Initialize the output buffer.
> > -	 */
> > -	context.dp = dp;
> > -	context.resynch = 1;
> > -	context.attr_filter = xfs_attr_filter(flags);
> > -	context.buffer = buffer;
> > -	context.bufsize = round_down(bufsize, sizeof(uint32_t));
> > -	context.firstu = context.bufsize;
> > -	context.put_listent = xfs_ioc_attr_put_listent;
> > -
> > -	alist = context.buffer;
> > -	alist->al_count = 0;
> > -	alist->al_more = 0;
> > -	alist->al_offset[0] = context.bufsize;
> > +	error = xfs_ioc_attr_list_context_init(dp, buffer, bufsize,
> > flags,
> > +			&context);
> > +	if (error)
> > +		return error;
> >  
> >  	error = xfs_attr_list(&context);
> >  	if (error)
> > diff --git a/fs/xfs/xfs_ioctl.h b/fs/xfs/xfs_ioctl.h
> > index d4abba2c13c1..ca60e1c427a3 100644
> > --- a/fs/xfs/xfs_ioctl.h
> > +++ b/fs/xfs/xfs_ioctl.h
> > @@ -35,6 +35,8 @@ int xfs_ioc_attrmulti_one(struct file *parfilp,
> > struct inode *inode,
> >  int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
> >  		      size_t bufsize, int flags,
> >  		      struct xfs_attrlist_cursor __user *ucursor);
> > +int xfs_ioc_attr_list_context_init(struct xfs_inode *dp, char
> > *buffer,
> > +		int bufsize, int flags, struct xfs_attr_list_context
> > *context);
> >  
> >  extern struct dentry *
> >  xfs_handle_to_dentry(
> > -- 
> > 2.25.1
> > 


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

* Re: [PATCH v1 16/17] xfs: Increase  XFS_DEFER_OPS_NR_INODES to 4
  2022-06-29 18:43       ` Darrick J. Wong
@ 2022-06-30  1:30         ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-30  1:30 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Dave Chinner, linux-xfs

On Wed, 2022-06-29 at 11:43 -0700, Darrick J. Wong wrote:
> On Fri, Jun 17, 2022 at 05:32:45PM -0700, Alli wrote:
> > On Fri, 2022-06-17 at 07:54 +1000, Dave Chinner wrote:
> > > On Sat, Jun 11, 2022 at 02:41:59AM -0700, Allison Henderson
> > > wrote:
> > > > Renames that generate parent pointer updates will need to 2
> > > > extra
> > > > defer
> > > > operations. One for the rmap update and another for the parent
> > > > pointer
> > > > update
> > > 
> > > Not sure I follow this - defer operation counts are something
> > > tracked in the transaction reservations, whilst this is changing
> > > the
> > > number of inodes that are joined and held across defer
> > > operations.
> > > 
> > > These rmap updates already occur on the directory inodes in a
> > > rename
> > > (when the dir update changes the dir shape), so I'm guessing that
> > > you are now talking about changing parent attrs for the child
> > > inodes
> > > may require attr fork shape changes (hence rmap updates) due to
> > > the
> > > deferred parent pointer xattr update?
> > > 
> > > If so, this should be placed in the series before the
> > > modifications
> > > to the rename operation is modified to join 4 ops to it,
> > > preferably
> > > at the start of the series....
> > 
> > I see, sure, I can move this patch down to the beginning of the set
> > > > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > > > ---
> > > >  fs/xfs/libxfs/xfs_defer.h | 2 +-
> > > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > > 
> > > > diff --git a/fs/xfs/libxfs/xfs_defer.h
> > > > b/fs/xfs/libxfs/xfs_defer.h
> > > > index 114a3a4930a3..0c2a6e537016 100644
> > > > --- a/fs/xfs/libxfs/xfs_defer.h
> > > > +++ b/fs/xfs/libxfs/xfs_defer.h
> > > > @@ -70,7 +70,7 @@ extern const struct xfs_defer_op_type
> > > > xfs_attr_defer_type;
> > > >  /*
> > > >   * Deferred operation item relogging limits.
> > > >   */
> > > > -#define XFS_DEFER_OPS_NR_INODES	2	/* join up to
> > > > two inodes */
> > > > +#define XFS_DEFER_OPS_NR_INODES	4	/* join up to
> > > > four inodes
> > > > */
> > > 
> > > The comment is not useful  - it should desvribe what operation
> > > requires 4 inodes to be joined. e.g.
> > > 
> > > /*
> > >  * Rename w/ parent pointers requires 4 indoes with defered ops
> > > to
> > >  * be joined to the transaction.
> > >  */
> > Sure, will update
> > 
> > > Then, if we are changing the maximum number of inodes that are
> > > joined to a deferred operation, then we need to also update the
> > > locking code such as in xfs_defer_ops_continue() that has to
> > > order
> > > locking of multiple inodes correctly.
> > Ok, I see it, I will take a look at updating that
> > 
> > > Also, rename can lock and modify 5 inodes, not 4, so the 4 inodes
> > > that get joined here need to be clearly documented somewhere. 
> > Ok, I think its src dir, target dir, src inode, target inode, and
> > then
> > wip.  Do we want the documenting in xfs_defer_ops_continue?  Or
> > just
> > the commit description?
> > 
> > > Also,
> > > xfs_sort_for_rename() that orders all the inodes in rename into
> > > correct locking order in an array, and xfs_lock_inodes() that
> > > does
> > > the locking of the inodes in the array.
> > Yes, I see it.  You want a comment in xfs_defer_ops_continue
> > referring
> > to the order?
> 
> I wouldn't mind one somewhere, though it could probably live with the
> parent pointer helper functions or buried in xfs_rename somewhere.
Alrighty, sounds good then

Allison
> 
> --D
> 
> > Thanks!
> > Allison
> > 
> > > Cheers,
> > > 
> > > Dave.


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

* Re: [PATCH v1 17/17] xfs: Add parent pointer ioctl
  2022-06-29 18:52   ` Darrick J. Wong
@ 2022-06-30  1:30     ` Alli
  0 siblings, 0 replies; 58+ messages in thread
From: Alli @ 2022-06-30  1:30 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, 2022-06-29 at 11:52 -0700, Darrick J. Wong wrote:
> On Sat, Jun 11, 2022 at 02:42:00AM -0700, Allison Henderson wrote:
> > This patch adds a new file ioctl to retrieve the parent pointer of
> > a
> > given inode
> > 
> > Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
> > ---
> >  fs/xfs/Makefile            |   1 +
> >  fs/xfs/libxfs/xfs_fs.h     |  46 +++++++++++++
> >  fs/xfs/libxfs/xfs_parent.c |  10 +++
> >  fs/xfs/libxfs/xfs_parent.h |   2 +
> >  fs/xfs/xfs_ioctl.c         |  90 ++++++++++++++++++++++++-
> >  fs/xfs/xfs_ondisk.h        |   4 ++
> >  fs/xfs/xfs_parent_utils.c  | 133
> > +++++++++++++++++++++++++++++++++++++
> >  fs/xfs/xfs_parent_utils.h  |  22 ++++++
> >  8 files changed, 306 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> > index fc717dc3470c..da86f6231f2e 100644
> > --- a/fs/xfs/Makefile
> > +++ b/fs/xfs/Makefile
> > @@ -86,6 +86,7 @@ xfs-y				+= xfs_aops.o \
> >  				   xfs_mount.o \
> >  				   xfs_mru_cache.o \
> >  				   xfs_pwork.o \
> > +				   xfs_parent_utils.o \
> >  				   xfs_reflink.o \
> >  				   xfs_stats.o \
> >  				   xfs_super.o \
> > diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
> > index b0b4d7a3aa15..e6c8873cd234 100644
> > --- a/fs/xfs/libxfs/xfs_fs.h
> > +++ b/fs/xfs/libxfs/xfs_fs.h
> > @@ -574,6 +574,7 @@ typedef struct xfs_fsop_handlereq {
> >  #define XFS_IOC_ATTR_SECURE	0x0008	/* use attrs in
> > security namespace */
> >  #define XFS_IOC_ATTR_CREATE	0x0010	/* fail if attr
> > already exists */
> >  #define XFS_IOC_ATTR_REPLACE	0x0020	/* fail if attr
> > does not exist */
> > +#define XFS_IOC_ATTR_PARENT	0x0040  /* use attrs in parent
> > namespace */
> >  
> >  typedef struct xfs_attrlist_cursor {
> >  	__u32		opaque[4];
> > @@ -752,6 +753,50 @@ struct xfs_scrub_metadata {
> >  				 XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
> >  #define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN |
> > XFS_SCRUB_FLAGS_OUT)
> >  
> > +#define XFS_PPTR_MAXNAMELEN				256
> > +
> > +/* return parents of the handle, not the open fd */
> > +#define XFS_PPTR_IFLAG_HANDLE  (1U << 0)
> > +
> > +/* target was the root directory */
> > +#define XFS_PPTR_OFLAG_ROOT    (1U << 1)
> > +
> > +/* Cursor is done iterating pptrs */
> > +#define XFS_PPTR_OFLAG_DONE    (1U << 2)
> > +
> > +/* Get an inode parent pointer through ioctl */
> > +struct xfs_parent_ptr {
> > +	__u64		xpp_ino;			/* Inode */
> > +	__u32		xpp_gen;			/* Inode
> > generation */
> > +	__u32		xpp_diroffset;			/*
> > Directory offset */
> > +	__u32		xpp_namelen;			/* File
> > name length */
> > +	__u32		xpp_pad;
> > +	__u8		xpp_name[XFS_PPTR_MAXNAMELEN];	/* File
> > name */
> > +};
> > +
> > +/* Iterate through an inodes parent pointers */
> > +struct xfs_pptr_info {
> > +	struct xfs_handle		pi_handle;
> > +	struct xfs_attrlist_cursor	pi_cursor;
> > +	__u32				pi_flags;
> > +	__u32				pi_reserved;
> > +	__u32				pi_ptrs_size;
> > +	__u32				pi_ptrs_used;
> > +	__u64				pi_reserved2[6];
> > +
> > +	/*
> > +	 * An array of struct xfs_parent_ptr follows the header
> > +	 * information. Use XFS_PPINFO_TO_PP() to access the
> > +	 * parent pointer array entries.
> > +	 */
> 
> 	struct xfs_parent_ptr		pi_parents[];
> 
> Unless you want to conserve space in the userspace buffer by making
> the
> size of xfs_parent_ptr itself variable?  Userspace would have to walk
> the entire buffer by hand like it does for listxattr, but it saves a
> fair amount of space.
Oh i see, I can take a look at it.  Honestly I think the only thing in
user space that's interested in parent pointers might be the test case,
so I'm not too worried about it atm.  But let me see if I can cut down
the buffer usage a bit.

> 
> > +};
> > +
> > +#define XFS_PPTR_INFO_SIZEOF(nr_ptrs) sizeof (struct
> > xfs_pptr_info) + \
> > +				      nr_ptrs * sizeof(struct
> > xfs_parent_ptr)
> > +
> > +#define XFS_PPINFO_TO_PP(info, idx)    \
> > +	(&(((struct xfs_parent_ptr *)((char *)(info) +
> > sizeof(*(info))))[(idx)]))
> 
> Turn these into static inline functions so that client programs get
> some
> proper typechecking by the C compiler.
> 
Sure, will do

> > +
> >  /*
> >   * ioctl limits
> >   */
> > @@ -797,6 +842,7 @@ struct xfs_scrub_metadata {
> >  /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
> >  #define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct
> > xfs_scrub_metadata)
> >  #define XFS_IOC_AG_GEOMETRY	_IOWR('X', 61, struct
> > xfs_ag_geometry)
> > +#define XFS_IOC_GETPPOINTER	_IOR ('X', 62, struct
> > xfs_parent_ptr)
> >  
> >  /*
> >   * ioctl commands that replace IRIX syssgi()'s
> > diff --git a/fs/xfs/libxfs/xfs_parent.c
> > b/fs/xfs/libxfs/xfs_parent.c
> > index cb546652bde9..a5b99f30bc63 100644
> > --- a/fs/xfs/libxfs/xfs_parent.c
> > +++ b/fs/xfs/libxfs/xfs_parent.c
> > @@ -33,6 +33,16 @@
> >  #include "xfs_attr_sf.h"
> >  #include "xfs_bmap.h"
> >  
> > +/* Initializes a xfs_parent_ptr from an xfs_parent_name_rec */
> > +void
> > +xfs_init_parent_ptr(struct xfs_parent_ptr		*xpp,
> > +		     struct xfs_parent_name_rec	*rec)
> 
> Indenting...
> 
Will fix

> > +{
> > +	xpp->xpp_ino = be64_to_cpu(rec->p_ino);
> > +	xpp->xpp_gen = be32_to_cpu(rec->p_gen);
> > +	xpp->xpp_diroffset = be32_to_cpu(rec->p_diroffset);
> > +}
> > +
> >  /*
> >   * Parent pointer attribute handling.
> >   *
> > diff --git a/fs/xfs/libxfs/xfs_parent.h
> > b/fs/xfs/libxfs/xfs_parent.h
> > index 10dc576ce693..fa50ada0d6a9 100644
> > --- a/fs/xfs/libxfs/xfs_parent.h
> > +++ b/fs/xfs/libxfs/xfs_parent.h
> > @@ -28,4 +28,6 @@ void xfs_init_parent_name_rec(struct
> > xfs_parent_name_rec *rec,
> >  			      uint32_t p_diroffset);
> >  void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec,
> >  			       struct xfs_parent_name_rec *rec);
> > +void xfs_init_parent_ptr(struct xfs_parent_ptr *xpp,
> > +			 struct xfs_parent_name_rec *rec);
> >  #endif	/* __XFS_PARENT_H__ */
> > diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> > index e1612e99e0c5..4cd1de3e9d0b 100644
> > --- a/fs/xfs/xfs_ioctl.c
> > +++ b/fs/xfs/xfs_ioctl.c
> > @@ -37,6 +37,7 @@
> >  #include "xfs_health.h"
> >  #include "xfs_reflink.h"
> >  #include "xfs_ioctl.h"
> > +#include "xfs_parent_utils.h"
> >  #include "xfs_xattr.h"
> >  
> >  #include <linux/mount.h>
> > @@ -355,6 +356,8 @@ xfs_attr_filter(
> >  		return XFS_ATTR_ROOT;
> >  	if (ioc_flags & XFS_IOC_ATTR_SECURE)
> >  		return XFS_ATTR_SECURE;
> > +	if (ioc_flags & XFS_IOC_ATTR_PARENT)
> > +		return XFS_ATTR_PARENT;
> >  	return 0;
> >  }
> >  
> > @@ -422,7 +425,8 @@ xfs_ioc_attr_list(
> >  	/*
> >  	 * Reject flags, only allow namespaces.
> >  	 */
> > -	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
> > +	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE |
> > +		      XFS_IOC_ATTR_PARENT))
> >  		return -EINVAL;
> >  	if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
> >  		return -EINVAL;
> > @@ -1672,6 +1676,87 @@ xfs_ioc_scrub_metadata(
> >  	return 0;
> >  }
> >  
> > +/*
> > + * IOCTL routine to get the parent pointers of an inode and return
> > it to user
> > + * space.  Caller must pass a buffer space containing a struct
> > xfs_pptr_info,
> > + * followed by a region large enough to contain an array of struct
> > + * xfs_parent_ptr of a size specified in pi_ptrs_size.  If the
> > inode contains
> > + * more parent pointers than can fit in the buffer space, caller
> > may re-call
> > + * the function using the returned pi_cursor to resume
> > iteration.  The
> > + * number of xfs_parent_ptr returned will be stored in
> > pi_ptrs_used.
> > + *
> > + * Returns 0 on success or non-zero on failure
> > + */
> > +STATIC int
> > +xfs_ioc_get_parent_pointer(
> > +	struct file			*filp,
> > +	void				__user *arg)
> > +{
> > +	struct xfs_pptr_info		*ppi = NULL;
> > +	int				error = 0;
> > +	struct xfs_inode		*ip = XFS_I(file_inode(filp));
> > +	struct xfs_mount		*mp = ip->i_mount;
> > +
> > +	if (!capable(CAP_SYS_ADMIN))
> > +		return -EPERM;
> > +
> > +	/* Allocate an xfs_pptr_info to put the user data */
> > +	ppi = kmem_alloc(sizeof(struct xfs_pptr_info), 0);
> > +	if (!ppi)
> > +		return -ENOMEM;
> > +
> > +	/* Copy the data from the user */
> > +	error = copy_from_user(ppi, arg, sizeof(struct xfs_pptr_info));
> > +	if (error)
> > +		goto out;
> > +
> > +	/* Check size of buffer requested by user */
> > +	if (XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size) >
> > XFS_XATTR_LIST_MAX) {
> > +		error = -ENOMEM;
> > +		goto out;
> > +	}
> > +
> > +	/*
> > +	 * Now that we know how big the trailing buffer is, expand
> > +	 * our kernel xfs_pptr_info to be the same size
> > +	 */
> > +	ppi = krealloc(ppi, XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size),
> > +		       GFP_NOFS | __GFP_NOFAIL);
> > +	if (!ppi)
> > +		return -ENOMEM;
> > +
> > +	if (ppi->pi_flags != 0 && ppi->pi_flags !=
> > XFS_PPTR_IFLAG_HANDLE) {
> 
> Flags validation should come before the big memory allocation.
> 
Alrighty, will scoot up

> > +		error = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	if (ppi->pi_flags == XFS_PPTR_IFLAG_HANDLE) {
> > +		error = xfs_iget(mp, NULL, ppi-
> > >pi_handle.ha_fid.fid_ino,
> > +				0, 0, &ip);
> > +		if (error)
> > +			goto out;
> 
> This ought to be checking the generation number in the file handle.
Oh I see, sure I will add that in

> 
> > +	}
> > +
> > +	if (ip->i_ino == mp->m_sb.sb_rootino)
> > +		ppi->pi_flags |= XFS_PPTR_OFLAG_ROOT;
> > +
> > +	/* Get the parent pointers */
> > +	error = xfs_attr_get_parent_pointer(ip, ppi);
> > +
> > +	if (error)
> > +		goto out;
> > +
> > +	/* Copy the parent pointers back to the user */
> > +	error = copy_to_user(arg, ppi,
> > +			XFS_PPTR_INFO_SIZEOF(ppi->pi_ptrs_size));
> > +	if (error)
> > +		goto out;
> > +
> > +out:
> > +	kmem_free(ppi);
> > +	return error;
> > +}
> > +
> >  int
> >  xfs_ioc_swapext(
> >  	xfs_swapext_t	*sxp)
> > @@ -1961,7 +2046,8 @@ xfs_file_ioctl(
> >  
> >  	case XFS_IOC_FSGETXATTRA:
> >  		return xfs_ioc_fsgetxattra(ip, arg);
> > -
> > +	case XFS_IOC_GETPPOINTER:
> > +		return xfs_ioc_get_parent_pointer(filp, arg);
> >  	case XFS_IOC_GETBMAP:
> >  	case XFS_IOC_GETBMAPA:
> >  	case XFS_IOC_GETBMAPX:
> > diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
> > index 758702b9495f..765eb514a917 100644
> > --- a/fs/xfs/xfs_ondisk.h
> > +++ b/fs/xfs/xfs_ondisk.h
> > @@ -135,6 +135,10 @@ xfs_check_ondisk_structs(void)
> >  	XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format,	40);
> >  	XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format,	16);
> >  
> > +	/* parent pointer ioctls */
> > +	XFS_CHECK_STRUCT_SIZE(struct xfs_parent_ptr,            280);
> > +	XFS_CHECK_STRUCT_SIZE(struct xfs_pptr_info,             104);
> > +
> >  	/*
> >  	 * The v5 superblock format extended several v4 header
> > structures with
> >  	 * additional data. While new fields are only accessible on v5
> > diff --git a/fs/xfs/xfs_parent_utils.c b/fs/xfs/xfs_parent_utils.c
> > new file mode 100644
> > index 000000000000..9880718395c6
> > --- /dev/null
> > +++ b/fs/xfs/xfs_parent_utils.c
> > @@ -0,0 +1,133 @@
> > +/*
> > + * Copyright (c) 2015 Red Hat, Inc.
> > + * All rights reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it would be
> > useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program; if not, write the Free Software
> > Foundation
> > + */
> > +#include "xfs.h"
> > +#include "xfs_fs.h"
> > +#include "xfs_format.h"
> > +#include "xfs_log_format.h"
> > +#include "xfs_shared.h"
> > +#include "xfs_trans_resv.h"
> > +#include "xfs_mount.h"
> > +#include "xfs_bmap_btree.h"
> > +#include "xfs_inode.h"
> > +#include "xfs_error.h"
> > +#include "xfs_trace.h"
> > +#include "xfs_trans.h"
> > +#include "xfs_da_format.h"
> > +#include "xfs_da_btree.h"
> > +#include "xfs_attr.h"
> > +#include "xfs_ioctl.h"
> > +#include "xfs_parent.h"
> > +#include "xfs_da_btree.h"
> > +
> > +/*
> > + * Get the parent pointers for a given inode
> > + *
> > + * Returns 0 on success and non zero on error
> > + */
> > +int
> > +xfs_attr_get_parent_pointer(struct xfs_inode		*ip,
> > +			    struct xfs_pptr_info	*ppi)
> > +
> 
> Indenting.  Also, should this go in xfs_parent.c ?
> 
Will fix indenting.  I think the reason I put it here was to avoid
unnecessary porting to libxfs.  Since parent.c is in libxfs and utils
is not.

> > +{
> > +
> > +	struct xfs_attrlist		*alist;
> > +	struct xfs_attrlist_ent		*aent;
> > +	struct xfs_parent_ptr		*xpp;
> > +	struct xfs_parent_name_rec	*xpnr;
> > +	char				*namebuf;
> > +	unsigned int			namebuf_size;
> > +	int				name_len;
> > +	int				error = 0;
> > +	unsigned int			ioc_flags =
> > XFS_IOC_ATTR_PARENT;
> > +	unsigned int			flags = XFS_ATTR_PARENT;
> > +	int				i;
> > +	struct xfs_attr_list_context	context;
> > +	struct xfs_da_args		args;
> > +
> > +	/* Allocate a buffer to store the attribute names */
> > +	namebuf_size = sizeof(struct xfs_attrlist) +
> > +		       (ppi->pi_ptrs_size) * sizeof(struct
> > xfs_attrlist_ent);
> > +	namebuf = kvzalloc(namebuf_size, GFP_KERNEL);
> > +	if (!namebuf)
> > +		return -ENOMEM;
> > +
> > +	memset(&context, 0, sizeof(struct xfs_attr_list_context));
> > +	error = xfs_ioc_attr_list_context_init(ip, namebuf,
> > namebuf_size,
> > +			ioc_flags, &context);
> > +
> > +	/* Copy the cursor provided by caller */
> > +	memcpy(&context.cursor, &ppi->pi_cursor,
> > +	       sizeof(struct xfs_attrlist_cursor));
> > +
> > +	if (error)
> > +		goto out_kfree;
> > +
> > +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> > +
> > +	error = xfs_attr_list_ilocked(&context);
> > +	if (error)
> > +		goto out_kfree;
> > +
> > +	alist = (struct xfs_attrlist *)namebuf;
> > +	for (i = 0; i < alist->al_count; i++) {
> > +		xpp = XFS_PPINFO_TO_PP(ppi, i);
> > +		memset(xpp, 0, sizeof(struct xfs_parent_ptr));
> > +		aent = (struct xfs_attrlist_ent *)
> > +			&namebuf[alist->al_offset[i]];
> > +		xpnr = (struct xfs_parent_name_rec *)(aent->a_name);
> > +
> > +		if (aent->a_valuelen > XFS_PPTR_MAXNAMELEN) {
> > +			error = -ERANGE;
> > +			goto out_kfree;
> > +		}
> > +		name_len = aent->a_valuelen;
> > +
> > +		memset(&args, 0, sizeof(args));
> > +		args.geo = ip->i_mount->m_attr_geo;
> > +		args.whichfork = XFS_ATTR_FORK;
> > +		args.dp = ip;
> > +		args.name = (char *)xpnr;
> > +		args.namelen = sizeof(struct xfs_parent_name_rec);
> > +		args.attr_filter = flags;
> > +		args.hashval = xfs_da_hashname(args.name,
> > args.namelen);
> > +		args.value = (unsigned char *)(xpp->xpp_name);
> > +		args.valuelen = name_len;
> > +		args.op_flags = XFS_DA_OP_OKNOENT;
> 
> You might want to convert this to a C99 named initialization inside
> the
> loop body.
Ok, will do.
> 
> Otherwise looks ok.
> 
Great!  Thanks!
Allison

> --D
> 
> > +
> > +		error = xfs_attr_get_ilocked(&args);
> > +		error = (error == -EEXIST ? 0 : error);
> > +		if (error)
> > +			goto out_kfree;
> > +
> > +		xpp->xpp_namelen = name_len;
> > +		xfs_init_parent_ptr(xpp, xpnr);
> > +	}
> > +	ppi->pi_ptrs_used = alist->al_count;
> > +	if (!alist->al_more)
> > +		ppi->pi_flags |= XFS_PPTR_OFLAG_DONE;
> > +
> > +	/* Update the caller with the current cursor position */
> > +	memcpy(&ppi->pi_cursor, &context.cursor,
> > +		sizeof(struct xfs_attrlist_cursor));
> > +
> > +out_kfree:
> > +	xfs_iunlock(ip, XFS_ILOCK_EXCL);
> > +	kmem_free(namebuf);
> > +
> > +	return error;
> > +}
> > +
> > diff --git a/fs/xfs/xfs_parent_utils.h b/fs/xfs/xfs_parent_utils.h
> > new file mode 100644
> > index 000000000000..0e952b2ebd4a
> > --- /dev/null
> > +++ b/fs/xfs/xfs_parent_utils.h
> > @@ -0,0 +1,22 @@
> > +/*
> > + * Copyright (c) 2017 Oracle, Inc.
> > + * All Rights Reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it would be
> > useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program; if not, write the Free Software
> > Foundation Inc.
> > + */
> > +#ifndef	__XFS_PARENT_UTILS_H__
> > +#define	__XFS_PARENT_UTILS_H__
> > +
> > +int xfs_attr_get_parent_pointer(struct xfs_inode *ip,
> > +				struct xfs_pptr_info *ppi);
> > +#endif	/* __XFS_PARENT_UTILS_H__ */
> > -- 
> > 2.25.1
> > 


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

end of thread, other threads:[~2022-06-30  1:30 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-11  9:41 [PATCH v1 00/17] Return of the Parent Pointers Allison Henderson
2022-06-11  9:41 ` [PATCH v1 01/17] xfs: Add larp state XFS_DAS_CREATE_FORK Allison Henderson
2022-06-15  1:09   ` Dave Chinner
2022-06-15 23:40     ` Alli
2022-06-16  2:08       ` Dave Chinner
2022-06-16  5:32         ` Dave Chinner
2022-06-29  6:33           ` Alli
2022-06-30  0:40             ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 02/17] xfs: Hold inode locks in xfs_ialloc Allison Henderson
2022-06-29 18:28   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 03/17] xfs: get directory offset when adding directory name Allison Henderson
2022-06-29 18:29   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 04/17] xfs: get directory offset when removing " Allison Henderson
2022-06-29 18:30   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 05/17] xfs: get directory offset when replacing a " Allison Henderson
2022-06-29 18:30   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 06/17] xfs: add parent pointer support to attribute code Allison Henderson
2022-06-29 18:33   ` Darrick J. Wong
2022-06-29 18:58     ` Alli
2022-06-11  9:41 ` [PATCH v1 07/17] xfs: define parent pointer xattr format Allison Henderson
2022-06-29 18:34   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 08/17] xfs: Add xfs_verify_pptr Allison Henderson
2022-06-29 18:35   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 09/17] xfs: extent transaction reservations for parent attributes Allison Henderson
2022-06-16  5:38   ` Dave Chinner
2022-06-18  0:31     ` Alli
2022-06-29 18:37   ` Darrick J. Wong
2022-06-29 19:23     ` Alli
2022-06-11  9:41 ` [PATCH v1 10/17] xfs: parent pointer attribute creation Allison Henderson
2022-06-11 15:10   ` kernel test robot
2022-06-16  5:49   ` Dave Chinner
2022-06-18  0:32     ` Alli
2022-06-29 18:41   ` Darrick J. Wong
2022-06-30  1:29     ` Alli
2022-06-11  9:41 ` [PATCH v1 11/17] xfs: add parent attributes to link Allison Henderson
2022-06-16 22:39   ` Dave Chinner
2022-06-18  0:32     ` Alli
2022-06-29 18:09       ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 12/17] xfs: remove parent pointers in unlink Allison Henderson
2022-06-29 17:35   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 13/17] xfs: Add parent pointers to rename Allison Henderson
2022-06-29 18:02   ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 14/17] xfs: Add the parent pointer support to the superblock version 5 Allison Henderson
2022-06-16  6:03   ` Dave Chinner
2022-06-18  0:32     ` Alli
2022-06-20  0:21       ` Dave Chinner
2022-06-29 18:16         ` Darrick J. Wong
2022-06-11  9:41 ` [PATCH v1 15/17] xfs: Add helper function xfs_attr_list_context_init Allison Henderson
2022-06-29 18:42   ` Darrick J. Wong
2022-06-30  1:30     ` Alli
2022-06-11  9:41 ` [PATCH v1 16/17] xfs: Increase XFS_DEFER_OPS_NR_INODES to 4 Allison Henderson
2022-06-16 21:54   ` Dave Chinner
2022-06-18  0:32     ` Alli
2022-06-29 18:43       ` Darrick J. Wong
2022-06-30  1:30         ` Alli
2022-06-11  9:42 ` [PATCH v1 17/17] xfs: Add parent pointer ioctl Allison Henderson
2022-06-29 18:52   ` Darrick J. Wong
2022-06-30  1:30     ` Alli

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.