]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: read and write metadata inode directory tree
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:10:58 +0000 (21:10 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Thu, 1 Aug 2024 00:10:01 +0000 (17:10 -0700)
Plumb in the bits we need to load metadata inodes from a named entry in
a metadir directory, create (or hardlink) inodes into a metadir
directory, create metadir directories, and flag inodes as being metadata
files.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/Makefile
fs/xfs/libxfs/xfs_metadir.c [new file with mode: 0644]
fs/xfs/libxfs/xfs_metadir.h [new file with mode: 0644]
fs/xfs/libxfs/xfs_metafile.c [new file with mode: 0644]
fs/xfs/libxfs/xfs_metafile.h
fs/xfs/xfs_icache.c
fs/xfs/xfs_trace.c
fs/xfs/xfs_trace.h

index dd692619bed58022e3b857529068c885884e8f92..4482cc8c390395272e342175555dfbebbc2cb174 100644 (file)
@@ -15,6 +15,7 @@ xfs-y                         += xfs_trace.o
 # build the libxfs code first
 xfs-y                          += $(addprefix libxfs/, \
                                   xfs_ag.o \
+                                  xfs_ag_resv.o \
                                   xfs_alloc.o \
                                   xfs_alloc_btree.o \
                                   xfs_attr.o \
@@ -42,7 +43,8 @@ xfs-y                         += $(addprefix libxfs/, \
                                   xfs_inode_buf.o \
                                   xfs_inode_util.o \
                                   xfs_log_rlimit.o \
-                                  xfs_ag_resv.o \
+                                  xfs_metadir.o \
+                                  xfs_metafile.o \
                                   xfs_parent.o \
                                   xfs_rmap.o \
                                   xfs_rmap_btree.o \
diff --git a/fs/xfs/libxfs/xfs_metadir.c b/fs/xfs/libxfs/xfs_metadir.c
new file mode 100644 (file)
index 0000000..079d426
--- /dev/null
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_metafile.h"
+#include "xfs_metadir.h"
+#include "xfs_trace.h"
+#include "xfs_inode.h"
+#include "xfs_quota.h"
+#include "xfs_ialloc.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_parent.h"
+
+/*
+ * Metadata Directory Tree
+ * =======================
+ *
+ * These functions provide an abstraction layer for looking up, creating, and
+ * deleting metadata inodes that live within a special metadata directory tree.
+ *
+ * This code does not manage the five existing metadata inodes: real time
+ * bitmap & summary; and the user, group, and quotas.  All other metadata
+ * inodes must use only the xfs_meta{dir,file}_* functions.
+ *
+ * Callers wishing to create or hardlink a metadata inode must create an
+ * xfs_metadir_update structure, call the appropriate xfs_metadir* function,
+ * and then call xfs_metadir_commit or xfs_metadir_cancel to commit or cancel
+ * the update.  Files in the metadata directory tree currently cannot be
+ * unlinked.
+ *
+ * When the metadir feature is enabled, all metadata inodes must have the
+ * "metadata" inode flag set to prevent them from being exposed to the outside
+ * world.
+ *
+ * Callers must take the ILOCK of any inode in the metadata directory tree to
+ * synchronize access to that inode.  It is never necessary to take the IOLOCK
+ * or the MMAPLOCK since metadata inodes must not be exposed to user space.
+ */
+
+static inline void
+xfs_metadir_set_xname(
+       struct xfs_name                 *xname,
+       const char                      *path,
+       unsigned char                   ftype)
+{
+       xname->name = (const unsigned char *)path;
+       xname->len = strlen(path);
+       xname->type = ftype;
+}
+
+/*
+ * Given a parent directory @dp and a metadata inode path component @xname,
+ * Look up the inode number in the directory, returning it in @ino.
+ * @xname.type must match the directory entry's ftype.
+ *
+ * Caller must hold ILOCK_EXCL.
+ */
+static inline int
+xfs_metadir_lookup(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
+       struct xfs_name         *xname,
+       xfs_ino_t               *ino)
+{
+       struct xfs_mount        *mp = dp->i_mount;
+       struct xfs_da_args      args = {
+               .trans          = tp,
+               .dp             = dp,
+               .geo            = mp->m_dir_geo,
+               .name           = xname->name,
+               .namelen        = xname->len,
+               .hashval        = xfs_dir2_hashname(mp, xname),
+               .whichfork      = XFS_DATA_FORK,
+               .op_flags       = XFS_DA_OP_OKNOENT,
+               .owner          = dp->i_ino,
+       };
+       int                     error;
+
+       if (!S_ISDIR(VFS_I(dp)->i_mode))
+               return -EFSCORRUPTED;
+       if (xfs_is_shutdown(mp))
+               return -EIO;
+
+       error = xfs_dir_lookup_args(&args);
+       if (error)
+               return error;
+
+       if (!xfs_verify_ino(mp, args.inumber))
+               return -EFSCORRUPTED;
+       if (xname->type != XFS_DIR3_FT_UNKNOWN && xname->type != args.filetype)
+               return -EFSCORRUPTED;
+
+       trace_xfs_metadir_lookup(dp, xname, args.inumber);
+       *ino = args.inumber;
+       return 0;
+}
+
+/*
+ * Look up and read a metadata inode from the metadata directory.  If the path
+ * component doesn't exist, return -ENOENT.
+ */
+int
+xfs_metadir_load(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
+       const char              *path,
+       umode_t                 mode,
+       struct xfs_inode        **ipp)
+{
+       struct xfs_name         xname;
+       int                     error;
+       xfs_ino_t               ino;
+
+       xfs_metadir_set_xname(&xname, path, XFS_DIR3_FT_UNKNOWN);
+
+       xfs_ilock(dp, XFS_ILOCK_EXCL);
+       error = xfs_metadir_lookup(tp, dp, &xname, &ino);
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+       if (error)
+               return error;
+       return xfs_metafile_iget(tp, ino, mode, ipp);
+}
+
+/*
+ * Unlock and release resources after committing (or cancelling) a metadata
+ * directory tree operation.  The caller retains its reference to @upd->ip
+ * and must release it explicitly.
+ */
+static inline void
+xfs_metadir_teardown(
+       struct xfs_metadir_update               *upd,
+       int                             error)
+{
+       trace_xfs_metadir_teardown(upd, error);
+
+       if (upd->ppargs) {
+               xfs_parent_finish(upd->dp->i_mount, upd->ppargs);
+               upd->ppargs = NULL;
+       }
+
+       if (upd->ip) {
+               if (upd->ip_locked)
+                       xfs_iunlock(upd->ip, XFS_ILOCK_EXCL);
+               upd->ip_locked = false;
+       }
+
+       if (upd->dp_locked)
+               xfs_iunlock(upd->dp, XFS_ILOCK_EXCL);
+       upd->dp_locked = false;
+}
+
+/*
+ * Begin the process of creating a metadata file by allocating transactions
+ * and taking whatever resources we're going to need.
+ */
+int
+xfs_metadir_start_create(
+       struct xfs_metadir_update               *upd)
+{
+       struct xfs_mount                *mp = upd->dp->i_mount;
+       int                             error;
+
+       ASSERT(upd->dp != NULL);
+       ASSERT(upd->ip == NULL);
+       ASSERT(xfs_has_metadir(mp));
+
+       error = xfs_parent_start(mp, &upd->ppargs);
+       if (error)
+               return error;
+
+       /*
+        * If we ever need the ability to create rt metadata files on a
+        * pre-metadir filesystem, we'll need to dqattach the parent here.
+        * Currently we assume that mkfs will create the files and quotacheck
+        * will account for them.
+        */
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_create,
+                       xfs_create_space_res(mp, MAXNAMELEN), 0, 0, &upd->tp);
+       if (error)
+               goto out_teardown;
+
+       /*
+        * Lock the parent directory if there is one.  We can't ijoin it to
+        * the transaction until after the child file has been created.
+        */
+       xfs_ilock(upd->dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
+       upd->dp_locked = true;
+
+       trace_xfs_metadir_start_create(upd);
+       return 0;
+out_teardown:
+       xfs_metadir_teardown(upd, error);
+       return error;
+}
+
+/*
+ * Create a metadata inode with the given @mode, and insert it into the
+ * metadata directory tree at the given @upd->path.  The path up to the final
+ * component must already exist.  The final path component must not exist.
+ *
+ * The new metadata inode will be attached to the update structure @upd->ip,
+ * with the ILOCK held until the caller releases it.
+ *
+ * NOTE: This function may return a new inode to the caller even if it returns
+ * a negative error code.  If an inode is passed back, the caller must finish
+ * setting up the inode before releasing it.
+ */
+int
+xfs_metadir_create(
+       struct xfs_metadir_update               *upd,
+       umode_t                         mode)
+{
+       struct xfs_icreate_args         args = {
+               .pip                    = upd->dp,
+               .mode                   = mode,
+       };
+       struct xfs_name                 xname;
+       struct xfs_dir_update           du = {
+               .dp                     = upd->dp,
+               .name                   = &xname,
+               .ppargs                 = upd->ppargs,
+       };
+       struct xfs_mount                *mp = upd->dp->i_mount;
+       xfs_ino_t                       ino;
+       unsigned int                    resblks;
+       int                             error;
+
+       xfs_assert_ilocked(upd->dp, XFS_ILOCK_EXCL);
+
+       /* Check that the name does not already exist in the directory. */
+       xfs_metadir_set_xname(&xname, upd->path, XFS_DIR3_FT_UNKNOWN);
+       error = xfs_metadir_lookup(upd->tp, upd->dp, &xname, &ino);
+       switch (error) {
+       case -ENOENT:
+               break;
+       case 0:
+               error = -EEXIST;
+               fallthrough;
+       default:
+               return error;
+       }
+
+       /*
+        * A newly created regular or special file just has one directory
+        * entry pointing to them, but a directory also the "." entry
+        * pointing to itself.
+        */
+       error = xfs_dialloc(&upd->tp, &args, &ino);
+       if (error)
+               return error;
+       error = xfs_icreate(upd->tp, ino, &args, &upd->ip);
+       if (error)
+               return error;
+       du.ip = upd->ip;
+       xfs_metafile_set_iflag(upd->tp, upd->ip);
+       upd->ip_locked = true;
+
+       /*
+        * Join the directory inode to the transaction.  We do not do it
+        * earlier because xfs_dialloc rolls the transaction.
+        */
+       xfs_trans_ijoin(upd->tp, upd->dp, 0);
+
+       /* Create the entry. */
+       if (S_ISDIR(args.mode))
+               resblks = xfs_mkdir_space_res(mp, xname.len);
+       else
+               resblks = xfs_create_space_res(mp, xname.len);
+       xname.type = xfs_mode_to_ftype(args.mode);
+
+       trace_xfs_metadir_try_create(upd);
+
+       error = xfs_dir_create_child(upd->tp, resblks, &du);
+       if (error)
+               return error;
+
+       /* Metadir files are not accounted to quota. */
+
+       trace_xfs_metadir_create(upd);
+
+       return 0;
+}
+
+#ifndef __KERNEL__
+/*
+ * Begin the process of linking a metadata file by allocating transactions
+ * and locking whatever resources we're going to need.
+ */
+int
+xfs_metadir_start_link(
+       struct xfs_metadir_update               *upd)
+{
+       struct xfs_mount                *mp = upd->dp->i_mount;
+       unsigned int                    resblks;
+       int                             nospace_error = 0;
+       int                             error;
+
+       ASSERT(upd->dp != NULL);
+       ASSERT(upd->ip != NULL);
+       ASSERT(xfs_has_metadir(mp));
+
+       error = xfs_parent_start(mp, &upd->ppargs);
+       if (error)
+               return error;
+
+       resblks = xfs_link_space_res(mp, MAXNAMELEN);
+       error = xfs_trans_alloc_dir(upd->dp, &M_RES(mp)->tr_link, upd->ip,
+                       &resblks, &upd->tp, &nospace_error);
+       if (error)
+               goto out_teardown;
+       if (!resblks) {
+               /* We don't allow reservationless updates. */
+               xfs_trans_cancel(upd->tp);
+               upd->tp = NULL;
+               xfs_iunlock(upd->dp, XFS_ILOCK_EXCL);
+               xfs_iunlock(upd->ip, XFS_ILOCK_EXCL);
+               error = nospace_error;
+               goto out_teardown;
+       }
+
+       upd->dp_locked = true;
+       upd->ip_locked = true;
+
+       trace_xfs_metadir_start_link(upd);
+       return 0;
+out_teardown:
+       xfs_metadir_teardown(upd, error);
+       return error;
+}
+
+/*
+ * Link the metadata directory given by @path to the inode @upd->ip.
+ * The path (up to the final component) must already exist, but the final
+ * component must not already exist.
+ */
+int
+xfs_metadir_link(
+       struct xfs_metadir_update               *upd)
+{
+       struct xfs_name                 xname;
+       struct xfs_dir_update           du = {
+               .dp                     = upd->dp,
+               .name                   = &xname,
+               .ip                     = upd->ip,
+               .ppargs                 = upd->ppargs,
+       };
+       struct xfs_mount                *mp = upd->dp->i_mount;
+       xfs_ino_t                       ino;
+       unsigned int                    resblks;
+       int                             error;
+
+       xfs_assert_ilocked(upd->dp, XFS_ILOCK_EXCL);
+       xfs_assert_ilocked(upd->ip, XFS_ILOCK_EXCL);
+
+       /* Look up the name in the current directory. */
+       xfs_metadir_set_xname(&xname, upd->path,
+                       xfs_mode_to_ftype(VFS_I(upd->ip)->i_mode));
+       error = xfs_metadir_lookup(upd->tp, upd->dp, &xname, &ino);
+       switch (error) {
+       case -ENOENT:
+               break;
+       case 0:
+               error = -EEXIST;
+               fallthrough;
+       default:
+               return error;
+       }
+
+       resblks = xfs_link_space_res(mp, xname.len);
+       error = xfs_dir_add_child(upd->tp, resblks, &du);
+       if (error)
+               return error;
+
+       trace_xfs_metadir_link(upd);
+
+       return 0;
+}
+#endif /* ! __KERNEL__ */
+
+/* Commit a metadir update and unlock/drop all resources. */
+int
+xfs_metadir_commit(
+       struct xfs_metadir_update               *upd)
+{
+       int                             error;
+
+       trace_xfs_metadir_commit(upd);
+
+       error = xfs_trans_commit(upd->tp);
+       upd->tp = NULL;
+
+       xfs_metadir_teardown(upd, error);
+       return error;
+}
+
+/* Cancel a metadir update and unlock/drop all resources. */
+void
+xfs_metadir_cancel(
+       struct xfs_metadir_update               *upd,
+       int                             error)
+{
+       trace_xfs_metadir_cancel(upd);
+
+       xfs_trans_cancel(upd->tp);
+       upd->tp = NULL;
+
+       xfs_metadir_teardown(upd, error);
+}
+
+/* Create a metadata for the last component of the path. */
+int
+xfs_metadir_mkdir(
+       struct xfs_inode                *dp,
+       const char                      *path,
+       struct xfs_inode                **ipp)
+{
+       struct xfs_metadir_update               upd = {
+               .dp             = dp,
+               .path           = path,
+       };
+       int                             error;
+
+       if (xfs_is_shutdown(dp->i_mount))
+               return -EIO;
+
+       /* Allocate a transaction to create the last directory. */
+       error = xfs_metadir_start_create(&upd);
+       if (error)
+               return error;
+
+       /* Create the subdirectory and take our reference. */
+       error = xfs_metadir_create(&upd, S_IFDIR);
+       if (error)
+               goto out_cancel;
+
+       error = xfs_metadir_commit(&upd);
+       if (error)
+               goto out_irele;
+
+       xfs_finish_inode_setup(upd.ip);
+       *ipp = upd.ip;
+       return 0;
+
+out_cancel:
+       xfs_metadir_cancel(&upd, error);
+out_irele:
+       /* Have to finish setting up the inode to ensure it's deleted. */
+       if (upd.ip) {
+               xfs_finish_inode_setup(upd.ip);
+               xfs_irele(upd.ip);
+       }
+       return error;
+}
diff --git a/fs/xfs/libxfs/xfs_metadir.h b/fs/xfs/libxfs/xfs_metadir.h
new file mode 100644 (file)
index 0000000..873df4f
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2018-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_METADIR_H__
+#define __XFS_METADIR_H__
+
+/* Cleanup widget for metadata inode creation and deletion. */
+struct xfs_metadir_update {
+       /* Parent directory */
+       struct xfs_inode        *dp;
+
+       /* Path to metadata file */
+       const char              *path;
+
+       /* Parent pointer update context */
+       struct xfs_parent_args  *ppargs;
+
+       /* Child metadata file */
+       struct xfs_inode        *ip;
+
+       struct xfs_trans        *tp;
+
+       unsigned int            dp_locked:1;
+       unsigned int            ip_locked:1;
+};
+
+int xfs_metadir_load(struct xfs_trans *tp, struct xfs_inode *dp,
+               const char *path, umode_t mode, struct xfs_inode **ipp);
+
+int xfs_metadir_start_create(struct xfs_metadir_update *upd);
+int xfs_metadir_create(struct xfs_metadir_update *upd, umode_t mode);
+
+int xfs_metadir_start_link(struct xfs_metadir_update *upd);
+int xfs_metadir_link(struct xfs_metadir_update *upd);
+
+int xfs_metadir_commit(struct xfs_metadir_update *upd);
+void xfs_metadir_cancel(struct xfs_metadir_update *upd, int error);
+
+int xfs_metadir_mkdir(struct xfs_inode *dp, const char *path,
+               struct xfs_inode **ipp);
+
+#endif /* __XFS_METADIR_H__ */
diff --git a/fs/xfs/libxfs/xfs_metafile.c b/fs/xfs/libxfs/xfs_metafile.c
new file mode 100644 (file)
index 0000000..495aa4e
--- /dev/null
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_metafile.h"
+#include "xfs_trace.h"
+#include "xfs_inode.h"
+
+/* Set up an inode to be recognized as a metadata directory inode. */
+void
+xfs_metafile_set_iflag(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip)
+{
+       VFS_I(ip)->i_mode &= ~0777;
+       VFS_I(ip)->i_uid = GLOBAL_ROOT_UID;
+       VFS_I(ip)->i_gid = GLOBAL_ROOT_GID;
+       ip->i_diflags |= (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_SYNC |
+                         XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP |
+                         XFS_DIFLAG_NODEFRAG);
+       if (S_ISDIR(VFS_I(ip)->i_mode))
+               ip->i_diflags |= XFS_DIFLAG_NOSYMLINKS;
+       ip->i_diflags2 &= ~XFS_DIFLAG2_DAX;
+       ip->i_diflags2 |= XFS_DIFLAG2_METADATA;
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
+
+/* Clear the metadata directory inode flag. */
+void
+xfs_metafile_clear_iflag(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip)
+{
+       ASSERT(xfs_is_metadir_inode(ip));
+       ASSERT(VFS_I(ip)->i_nlink == 0);
+
+       ip->i_diflags2 &= ~XFS_DIFLAG2_METADATA;
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+}
index ea0ba76be6e81eda8a9aa45f4a0a8ebd099144c2..740621b23db8187175237ec07d665e9c4ec0334c 100644 (file)
@@ -6,6 +6,9 @@
 #ifndef __XFS_METAFILE_H__
 #define __XFS_METAFILE_H__
 
+void xfs_metafile_set_iflag(struct xfs_trans *tp, struct xfs_inode *ip);
+void xfs_metafile_clear_iflag(struct xfs_trans *tp, struct xfs_inode *ip);
+
 /* Code specific to kernel/userspace; must be provided externally. */
 
 int xfs_metafile_iget(struct xfs_trans *tp, xfs_ino_t ino, umode_t mode,
index f5aa6643e6beee78a2970bfeb00dd60a47058859..f1a8aa78e0be74a58657358f74ea4133f9951382 100644 (file)
@@ -828,7 +828,7 @@ xfs_metafile_iget(
        int                     error;
 
        error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED, 0, &ip);
-       if (error == -EFSCORRUPTED)
+       if (error == -EFSCORRUPTED || error == -EINVAL)
                goto whine;
        if (error)
                return error;
index 2af9f274e8724e9ab081168e5b445e9c80e0f131..c5f818cf40c294a0c054848c3d4564833749c5a9 100644 (file)
@@ -44,6 +44,8 @@
 #include "xfs_parent.h"
 #include "xfs_rmap.h"
 #include "xfs_refcount.h"
+#include "xfs_metafile.h"
+#include "xfs_metadir.h"
 
 /*
  * We include this last to have the helpers above available for the trace
index 4cf0fa71ba9cee0acb51167c68361cbd69c54147..7f259891ebcaaf60fcb19d1ba983b2622f79ff3f 100644 (file)
@@ -93,6 +93,7 @@ struct xfs_attrlist_cursor_kern;
 struct xfs_extent_free_item;
 struct xfs_rmap_intent;
 struct xfs_refcount_intent;
+struct xfs_metadir_update;
 
 #define XFS_ATTR_FILTER_FLAGS \
        { XFS_ATTR_ROOT,        "ROOT" }, \
@@ -5332,6 +5333,107 @@ DEFINE_EVENT(xfs_getparents_class, name, \
 DEFINE_XFS_GETPARENTS_EVENT(xfs_getparents_begin);
 DEFINE_XFS_GETPARENTS_EVENT(xfs_getparents_end);
 
+DECLARE_EVENT_CLASS(xfs_metadir_update_class,
+       TP_PROTO(const struct xfs_metadir_update *upd),
+       TP_ARGS(upd),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, dp_ino)
+               __field(xfs_ino_t, ino)
+               __string(fname, upd->path)
+       ),
+       TP_fast_assign(
+               __entry->dev = upd->dp->i_mount->m_super->s_dev;
+               __entry->dp_ino = upd->dp->i_ino;
+               __entry->ino = upd->ip ? upd->ip->i_ino : NULLFSINO;
+               __assign_str(fname);
+       ),
+       TP_printk("dev %d:%d dp 0x%llx fname '%s' ino 0x%llx",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->dp_ino,
+                 __get_str(fname),
+                 __entry->ino)
+)
+
+#define DEFINE_METADIR_UPDATE_EVENT(name) \
+DEFINE_EVENT(xfs_metadir_update_class, name, \
+       TP_PROTO(const struct xfs_metadir_update *upd), \
+       TP_ARGS(upd))
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_start_create);
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_start_link);
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_commit);
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_cancel);
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_try_create);
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_create);
+DEFINE_METADIR_UPDATE_EVENT(xfs_metadir_link);
+
+DECLARE_EVENT_CLASS(xfs_metadir_update_error_class,
+       TP_PROTO(const struct xfs_metadir_update *upd, int error),
+       TP_ARGS(upd, error),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, dp_ino)
+               __field(xfs_ino_t, ino)
+               __field(int, error)
+               __string(fname, upd->path)
+       ),
+       TP_fast_assign(
+               __entry->dev = upd->dp->i_mount->m_super->s_dev;
+               __entry->dp_ino = upd->dp->i_ino;
+               __entry->ino = upd->ip ? upd->ip->i_ino : NULLFSINO;
+               __entry->error = error;
+               __assign_str(fname);
+       ),
+       TP_printk("dev %d:%d dp 0x%llx fname '%s' ino 0x%llx error %d",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->dp_ino,
+                 __get_str(fname),
+                 __entry->ino,
+                 __entry->error)
+)
+
+#define DEFINE_METADIR_UPDATE_ERROR_EVENT(name) \
+DEFINE_EVENT(xfs_metadir_update_error_class, name, \
+       TP_PROTO(const struct xfs_metadir_update *upd, int error), \
+       TP_ARGS(upd, error))
+DEFINE_METADIR_UPDATE_ERROR_EVENT(xfs_metadir_teardown);
+
+DECLARE_EVENT_CLASS(xfs_metadir_class,
+       TP_PROTO(struct xfs_inode *dp, struct xfs_name *name,
+                xfs_ino_t ino),
+       TP_ARGS(dp, name, ino),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, dp_ino)
+               __field(xfs_ino_t, ino)
+               __field(int, ftype)
+               __field(int, namelen)
+               __dynamic_array(char, name, name->len)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(dp)->i_sb->s_dev;
+               __entry->dp_ino = dp->i_ino;
+               __entry->ino = ino,
+               __entry->ftype = name->type;
+               __entry->namelen = name->len;
+               memcpy(__get_str(name), name->name, name->len);
+       ),
+       TP_printk("dev %d:%d dir 0x%llx type %s name '%.*s' ino 0x%llx",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->dp_ino,
+                 __print_symbolic(__entry->ftype, XFS_DIR3_FTYPE_STR),
+                 __entry->namelen,
+                 __get_str(name),
+                 __entry->ino)
+)
+
+#define DEFINE_METADIR_EVENT(name) \
+DEFINE_EVENT(xfs_metadir_class, name, \
+       TP_PROTO(struct xfs_inode *dp, struct xfs_name *name, \
+                xfs_ino_t ino), \
+       TP_ARGS(dp, name, ino))
+DEFINE_METADIR_EVENT(xfs_metadir_lookup);
+
 #endif /* _TRACE_XFS_H */
 
 #undef TRACE_INCLUDE_PATH