summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2020-10-25 17:16:04 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2020-10-26 18:32:30 -0700
commitfcce10df855ca8fd2050b1d39be1316be2756e31 (patch)
tree79d9df9c44e20331a6d0777abccbf15ebc2b65cf
parent424996fdf4a8f3e4b0ce3c6348ceee312f77eede (diff)
xfs: read and write metadata inode directory
Plumb in the bits we need to look up metadata inode numbers from the metadata inode directory and save them back. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/libxfs/xfs_imeta.c525
-rw-r--r--fs/xfs/libxfs/xfs_imeta.h4
-rw-r--r--fs/xfs/libxfs/xfs_inode_util.c2
-rw-r--r--fs/xfs/libxfs/xfs_trans_resv.c8
-rw-r--r--fs/xfs/xfs_trace.h37
5 files changed, 570 insertions, 6 deletions
diff --git a/fs/xfs/libxfs/xfs_imeta.c b/fs/xfs/libxfs/xfs_imeta.c
index eb475b91c91f..3ba62c24855c 100644
--- a/fs/xfs/libxfs/xfs_imeta.c
+++ b/fs/xfs/libxfs/xfs_imeta.c
@@ -21,6 +21,7 @@
#include "xfs_da_format.h"
#include "xfs_da_btree.h"
#include "xfs_trans_space.h"
+#include "xfs_dir2.h"
/*
* Metadata Inode Number Management
@@ -41,9 +42,15 @@
* this structure must be passed to xfs_imeta_end_update to free resources that
* cannot be freed during the transaction.
*
- * Right now we only support callers passing in the predefined metadata inode
- * paths; the goal is that callers will some day locate metadata inodes based
- * on path lookups into a metadata directory structure.
+ * When the metadata directory tree (metadir) feature is enabled, we can create
+ * a complex directory tree in which to store metadata inodes. Inodes within
+ * the metadata directory tree should have the "metadata" inode flag set to
+ * prevent them from being exposed to the outside world.
+ *
+ * Callers are expected to take the ILOCK of metadata inodes to synchronize
+ * access to the file. This includes directory operations. It is not
+ * necessary to take the IOLOCK or MMAPLOCK since metadata inodes should never
+ * be exposed to user space.
*/
/* Static metadata inode paths */
@@ -59,6 +66,10 @@ XFS_IMETA_DEFINE_PATH(XFS_IMETA_USRQUOTA, usrquota_path);
XFS_IMETA_DEFINE_PATH(XFS_IMETA_GRPQUOTA, grpquota_path);
XFS_IMETA_DEFINE_PATH(XFS_IMETA_PRJQUOTA, prjquota_path);
+const struct xfs_imeta_path XFS_IMETA_METADIR = {
+ .im_depth = 0,
+};
+
/* Are these two paths equal? */
STATIC bool
xfs_imeta_path_compare(
@@ -116,6 +127,10 @@ static const struct xfs_imeta_sbmap {
.path = &XFS_IMETA_PRJQUOTA,
.offset = offsetof(struct xfs_sb, sb_pquotino),
},
+ {
+ .path = &XFS_IMETA_METADIR,
+ .offset = offsetof(struct xfs_sb, sb_metadirino),
+ },
{ NULL, 0 },
};
@@ -254,6 +269,469 @@ xfs_imeta_sb_link(
return 0;
}
+/* Functions for storing and retrieving metadata directory inode values. */
+
+static inline void
+set_xname(
+ struct xfs_name *xname,
+ const struct xfs_imeta_path *path,
+ unsigned int path_idx,
+ unsigned char ftype)
+{
+ xname->name = (const unsigned char *)path->im_path[path_idx];
+ xname->len = strlen(path->im_path[path_idx]);
+ xname->type = ftype;
+}
+
+/*
+ * Given a parent directory @dp, a metadata inode @path and component
+ * @path_idx, and the expected file type @ftype of the path component, fill out
+ * the @xname and look up the inode number in the directory, returning it in
+ * @ino.
+ */
+static inline int
+xfs_imeta_dir_lookup_component(
+ struct xfs_inode *dp,
+ struct xfs_name *xname,
+ xfs_ino_t *ino)
+{
+ int error;
+
+ trace_xfs_imeta_dir_lookup_component(dp, xname, NULLFSINO);
+
+ error = xfs_dir_lookup(NULL, dp, xname, ino, NULL);
+ if (error)
+ return error;
+ if (!xfs_verify_ino(dp->i_mount, *ino))
+ return -EFSCORRUPTED;
+
+ trace_xfs_imeta_dir_lookup_found(dp, xname, *ino);
+ return 0;
+}
+
+/*
+ * Traverse a metadata directory tree path, returning the inode corresponding
+ * to the parent of the last path component. If any of the path components do
+ * not exist, return -ENOENT.
+ */
+STATIC int
+xfs_imeta_dir_parent(
+ struct xfs_mount *mp,
+ const struct xfs_imeta_path *path,
+ struct xfs_inode **dpp)
+{
+ struct xfs_name xname;
+ struct xfs_inode *dp;
+ xfs_ino_t ino;
+ unsigned int i;
+ int error;
+
+ if (mp->m_metadirip == NULL)
+ return -ENOENT;
+
+ /* Grab the metadir root. */
+ error = xfs_imeta_iget(mp, mp->m_metadirip->i_ino, XFS_DIR3_FT_DIR,
+ &dp);
+ if (error)
+ return error;
+
+ /* Caller wanted the root, we're done! */
+ if (path->im_depth == 0) {
+ *dpp = dp;
+ return 0;
+ }
+
+ for (i = 0; i < path->im_depth - 1; i++) {
+ /* Look up the name in the current directory. */
+ set_xname(&xname, path, i, XFS_DIR3_FT_DIR);
+ error = xfs_imeta_dir_lookup_component(dp, &xname, &ino);
+ if (error)
+ goto out_rele;
+
+ /* Drop the existing dp and pick up the new one. */
+ xfs_imeta_irele(dp);
+ error = xfs_imeta_iget(mp, ino, XFS_DIR3_FT_DIR, &dp);
+ if (error)
+ return error;
+ }
+
+ *dpp = dp;
+ return 0;
+
+out_rele:
+ xfs_imeta_irele(dp);
+ return error;
+}
+
+/*
+ * Look up a metadata inode from the metadata directory. If the last path
+ * component doesn't exist, return NULLFSINO. If any other part of the path
+ * does not exist, return -ENOENT so we can distinguish the two.
+ */
+STATIC int
+xfs_imeta_dir_lookup_int(
+ struct xfs_mount *mp,
+ const struct xfs_imeta_path *path,
+ xfs_ino_t *inop)
+{
+ struct xfs_name xname;
+ struct xfs_inode *dp = NULL;
+ xfs_ino_t ino;
+ int error;
+
+ /* metadir ino is recorded in superblock */
+ if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR))
+ return xfs_imeta_sb_lookup(mp, path, inop);
+
+ ASSERT(path->im_depth > 0);
+
+ /* Find the parent of the last path component. */
+ error = xfs_imeta_dir_parent(mp, path, &dp);
+ if (error)
+ return error;
+
+ /* Look up the name in the current directory. */
+ set_xname(&xname, path, path->im_depth - 1, XFS_DIR3_FT_UNKNOWN);
+ error = xfs_imeta_dir_lookup_component(dp, &xname, &ino);
+ switch (error) {
+ case 0:
+ *inop = ino;
+ break;
+ case -ENOENT:
+ *inop = NULLFSINO;
+ error = 0;
+ break;
+ }
+
+ xfs_imeta_irele(dp);
+ return error;
+}
+
+/*
+ * Look up a metadata inode from the metadata directory tree. If any of the
+ * middle path components do not exist, we consider this corruption because
+ * only the last component is allowed to not exist.
+ */
+STATIC int
+xfs_imeta_dir_lookup(
+ struct xfs_mount *mp,
+ const struct xfs_imeta_path *path,
+ xfs_ino_t *inop)
+{
+ int error;
+
+ error = xfs_imeta_dir_lookup_int(mp, path, inop);
+ if (error == -ENOENT)
+ return -EFSCORRUPTED;
+ return error;
+}
+
+/*
+ * Load all the metadata inode pointers that are cached in the in-core
+ * superblock but live somewhere in the metadata directory tree.
+ */
+STATIC int
+xfs_imeta_dir_mount(
+ struct xfs_mount *mp)
+{
+ const struct xfs_imeta_sbmap *p;
+ xfs_ino_t *sb_inop;
+ int err2;
+ int error = 0;
+
+ for (p = xfs_imeta_sbmaps; p->path && p->path->im_depth > 0; p++) {
+ if (p->path == &XFS_IMETA_METADIR)
+ continue;
+ sb_inop = xfs_imeta_sbmap_to_inop(mp, p);
+ err2 = xfs_imeta_dir_lookup_int(mp, p->path, sb_inop);
+ if (err2 == -ENOENT) {
+ *sb_inop = NULLFSINO;
+ continue;
+ }
+ if (!error && err2)
+ error = err2;
+ }
+
+ return error;
+}
+
+/*
+ * Create a new metadata inode accessible via the given metadata directory path.
+ * Callers must ensure that the directory entry does not already exist; a new
+ * one will be created.
+ */
+STATIC int
+xfs_imeta_dir_create(
+ struct xfs_trans **tpp,
+ const struct xfs_imeta_path *path,
+ umode_t mode,
+ struct xfs_inode **ipp,
+ struct xfs_imeta_end *cleanup)
+{
+ struct xfs_ialloc_args args = {
+ .nlink = S_ISDIR(mode) ? 2 : 1,
+ .mode = mode,
+ };
+ struct xfs_name xname;
+ struct xfs_mount *mp = (*tpp)->t_mountp;
+ struct xfs_inode *dp = NULL;
+ xfs_ino_t *sb_inop;
+ xfs_ino_t ino;
+ unsigned int resblks;
+ int error;
+
+ /* metadir ino is recorded in superblock */
+ if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR)) {
+ error = xfs_imeta_sb_create(tpp, path, mode, ipp);
+ if (error)
+ return error;
+
+ /* Set the metadata iflag, initialize directory. */
+ (*ipp)->i_d.di_flags2 |= XFS_DIFLAG2_METADATA;
+ return xfs_dir_init(*tpp, *ipp, *ipp);
+ }
+
+ ASSERT(path->im_depth > 0);
+
+ /*
+ * Find the parent of the last path component. If the parent path does
+ * not exist, we consider this corruption because paths are supposed
+ * to exist.
+ */
+ error = xfs_imeta_dir_parent(mp, path, &dp);
+ if (error == -ENOENT)
+ return -EFSCORRUPTED;
+ if (error)
+ return error;
+
+ /* Check that the name does not already exist in the directory. */
+ set_xname(&xname, path, path->im_depth - 1, XFS_DIR3_FT_UNKNOWN);
+ error = xfs_imeta_dir_lookup_component(dp, &xname, &ino);
+ switch (error) {
+ case 0:
+ error = -EEXIST;
+ break;
+ case -ENOENT:
+ error = 0;
+ break;
+ }
+ if (error)
+ goto out_rele;
+
+ xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
+
+ /*
+ * A newly created regular or special file just has one directory
+ * entry pointing to them, but a directory also the "." entry
+ * pointing to itself.
+ */
+ args.pip = dp;
+ error = xfs_dir_ialloc(tpp, &args, ipp);
+ if (error)
+ goto out_ilock;
+
+ /* Set the metadata iflag */
+ (*ipp)->i_d.di_flags2 |= XFS_DIFLAG2_METADATA;
+ xfs_trans_log_inode(*tpp, *ipp, XFS_ILOG_CORE);
+
+ /*
+ * Once we join the parent directory to the transaction we can't
+ * release it until after the transaction commits or cancels, so we
+ * must defer releasing it to end_update. This is different from
+ * regular file creation, where the vfs holds the parent dir reference
+ * and will free it. The caller is always responsible for releasing
+ * ipp, even if we failed.
+ */
+ xfs_trans_ijoin(*tpp, dp, XFS_ILOCK_EXCL);
+ cleanup->dp = dp;
+
+ /* 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_imeta_dir_try_create(dp, &xname, NULLFSINO);
+ error = xfs_dir_create_new_child(*tpp, resblks, dp, &xname, *ipp);
+ if (error)
+ return error;
+ trace_xfs_imeta_dir_created(*ipp, &xname, (*ipp)->i_ino);
+
+ /* Update the in-core superblock value if there is one. */
+ sb_inop = xfs_imeta_path_to_sb_inop(mp, path);
+ if (sb_inop)
+ *sb_inop = (*ipp)->i_ino;
+ return 0;
+
+out_ilock:
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+out_rele:
+ xfs_imeta_irele(dp);
+ return error;
+}
+
+/*
+ * Remove the given entry from the metadata directory and drop the link count
+ * of the metadata inode.
+ */
+STATIC int
+xfs_imeta_dir_unlink(
+ struct xfs_trans **tpp,
+ const struct xfs_imeta_path *path,
+ struct xfs_inode *ip,
+ struct xfs_imeta_end *cleanup)
+{
+ struct xfs_name xname;
+ struct xfs_mount *mp = (*tpp)->t_mountp;
+ struct xfs_inode *dp = NULL;
+ xfs_ino_t *sb_inop;
+ xfs_ino_t ino;
+ unsigned int resblks;
+ int error;
+
+ /* metadir ino is recorded in superblock */
+ if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR))
+ return xfs_imeta_sb_unlink(tpp, path, ip);
+
+ ASSERT(path->im_depth > 0);
+
+ /*
+ * Find the parent of the last path component. If the parent path does
+ * not exist, we consider this corruption because paths are supposed
+ * to exist.
+ */
+ error = xfs_imeta_dir_parent(mp, path, &dp);
+ if (error == -ENOENT)
+ return -EFSCORRUPTED;
+ if (error)
+ return error;
+
+ /* Look up the name in the current directory. */
+ set_xname(&xname, path, path->im_depth - 1,
+ xfs_mode_to_ftype(VFS_I(ip)->i_mode));
+ error = xfs_imeta_dir_lookup_component(dp, &xname, &ino);
+ switch (error) {
+ case 0:
+ if (ino != ip->i_ino)
+ error = -ENOENT;
+ break;
+ case -ENOENT:
+ error = -EFSCORRUPTED;
+ break;
+ }
+ if (error)
+ goto out_rele;
+
+ xfs_lock_two_inodes(dp, XFS_ILOCK_EXCL, ip, XFS_ILOCK_EXCL);
+
+ /*
+ * Once we join the parent directory to the transaction we can't
+ * release it until after the transaction commits or cancels, so we
+ * must defer releasing it to end_update. This is different from
+ * regular file removal, where the vfs holds the parent dir reference
+ * and will free it. The unlink caller is always responsible for
+ * releasing ip, so we don't need to take care of that.
+ */
+ xfs_trans_ijoin(*tpp, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(*tpp, ip, XFS_ILOCK_EXCL);
+ cleanup->dp = dp;
+
+ resblks = XFS_REMOVE_SPACE_RES(mp);
+ error = xfs_dir_remove_child(*tpp, resblks, dp, &xname, ip);
+ if (error)
+ return error;
+ trace_xfs_imeta_dir_unlinked(dp, &xname, ip->i_ino);
+
+ /* Update the in-core superblock value if there is one. */
+ sb_inop = xfs_imeta_path_to_sb_inop(mp, path);
+ if (sb_inop)
+ *sb_inop = NULLFSINO;
+ return 0;
+
+out_rele:
+ xfs_imeta_irele(dp);
+ return error;
+}
+
+/* Set the given path in the metadata directory to point to an inode. */
+STATIC int
+xfs_imeta_dir_link(
+ struct xfs_trans *tp,
+ const struct xfs_imeta_path *path,
+ struct xfs_inode *ip,
+ struct xfs_imeta_end *cleanup)
+{
+ struct xfs_name xname;
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_inode *dp = NULL;
+ xfs_ino_t *sb_inop;
+ xfs_ino_t ino;
+ unsigned int resblks;
+ int error;
+
+ /* metadir ino is recorded in superblock */
+ if (xfs_imeta_path_compare(path, &XFS_IMETA_METADIR))
+ return xfs_imeta_sb_link(tp, path, ip);
+
+ ASSERT(path->im_depth > 0);
+
+ /*
+ * Find the parent of the last path component. If the parent path does
+ * not exist, we consider this corruption because paths are supposed
+ * to exist.
+ */
+ error = xfs_imeta_dir_parent(mp, path, &dp);
+ if (error == -ENOENT)
+ return -EFSCORRUPTED;
+ if (error)
+ return error;
+
+ /* Look up the name in the current directory. */
+ set_xname(&xname, path, path->im_depth - 1,
+ xfs_mode_to_ftype(VFS_I(ip)->i_mode));
+ error = xfs_imeta_dir_lookup_component(dp, &xname, &ino);
+ switch (error) {
+ case -ENOENT:
+ break;
+ case 0:
+ error = -EEXIST;
+ /* fall through */
+ default:
+ goto out_rele;
+ }
+
+ xfs_lock_two_inodes(ip, XFS_ILOCK_EXCL, dp, XFS_ILOCK_EXCL);
+
+ /*
+ * Once we join the parent directory to the transaction we can't
+ * release it until after the transaction commits or cancels, so we
+ * must defer releasing it to end_update. This is different from
+ * regular file removal, where the vfs holds the parent dir reference
+ * and will free it. The link caller is always responsible for
+ * releasing ip, so we don't need to take care of that.
+ */
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+ cleanup->dp = dp;
+
+ resblks = XFS_LINK_SPACE_RES(mp, target_name->len);
+ error = xfs_dir_link_existing_child(tp, resblks, dp, &xname, ip);
+ if (error)
+ return error;
+
+ trace_xfs_imeta_dir_link(dp, &xname, ip->i_ino);
+
+ /* Update the in-core superblock value if there is one. */
+ sb_inop = xfs_imeta_path_to_sb_inop(mp, path);
+ if (sb_inop)
+ *sb_inop = ip->i_ino;
+ return 0;
+
+out_rele:
+ xfs_imeta_irele(dp);
+ return error;
+}
+
/* General functions for managing metadata inode pointers */
/*
@@ -283,7 +761,10 @@ xfs_imeta_lookup(
ASSERT(xfs_imeta_path_check(path));
- error = xfs_imeta_sb_lookup(mp, path, &ino);
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ error = xfs_imeta_dir_lookup(mp, path, &ino);
+ else
+ error = xfs_imeta_sb_lookup(mp, path, &ino);
if (error)
return error;
@@ -313,9 +794,14 @@ xfs_imeta_create(
struct xfs_inode **ipp,
struct xfs_imeta_end *cleanup)
{
+ struct xfs_mount *mp = (*tpp)->t_mountp;
+
ASSERT(xfs_imeta_path_check(path));
*ipp = NULL;
+ cleanup->dp = NULL;
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ return xfs_imeta_dir_create(tpp, path, mode, ipp, cleanup);
return xfs_imeta_sb_create(tpp, path, mode, ipp);
}
@@ -332,9 +818,14 @@ xfs_imeta_unlink(
struct xfs_inode *ip,
struct xfs_imeta_end *cleanup)
{
+ struct xfs_mount *mp = (*tpp)->t_mountp;
+ cleanup->dp = NULL;
+
ASSERT(xfs_imeta_path_check(path));
ASSERT(xfs_imeta_verify((*tpp)->t_mountp, ip->i_ino));
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ return xfs_imeta_dir_unlink(tpp, path, ip, cleanup);
return xfs_imeta_sb_unlink(tpp, path, ip);
}
@@ -350,8 +841,13 @@ xfs_imeta_link(
struct xfs_inode *ip,
struct xfs_imeta_end *cleanup)
{
+ struct xfs_mount *mp = tp->t_mountp;
+
ASSERT(xfs_imeta_path_check(path));
+ cleanup->dp = NULL;
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ return xfs_imeta_dir_link(tp, path, ip, cleanup);
return xfs_imeta_sb_link(tp, path, ip);
}
@@ -366,6 +862,10 @@ xfs_imeta_end_update(
int error)
{
trace_xfs_imeta_end_update(mp, 0, error, _RET_IP_);
+
+ if (cleanup->dp)
+ xfs_imeta_irele(cleanup->dp);
+ cleanup->dp = NULL;
}
/* Does this inode number refer to a static metadata inode? */
@@ -391,6 +891,9 @@ int
xfs_imeta_mount(
struct xfs_mount *mp)
{
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ return xfs_imeta_dir_mount(mp);
+
return 0;
}
@@ -399,6 +902,9 @@ unsigned int
xfs_imeta_create_space_res(
struct xfs_mount *mp)
{
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ return max(XFS_MKDIR_SPACE_RES(mp, NAME_MAX),
+ XFS_CREATE_SPACE_RES(mp, NAME_MAX));
return XFS_IALLOC_SPACE_RES(mp);
}
@@ -409,3 +915,14 @@ xfs_imeta_unlink_space_res(
{
return XFS_REMOVE_SPACE_RES(mp);
}
+
+/* Clear the metadata iflag if we're unlinking this inode. */
+void
+xfs_imeta_droplink(
+ struct xfs_inode *ip)
+{
+ if (VFS_I(ip)->i_nlink == 0 &&
+ xfs_sb_version_hasmetadir(&ip->i_mount->m_sb) &&
+ xfs_is_metadata_inode(ip))
+ ip->i_d.di_flags2 &= ~XFS_DIFLAG2_METADATA;
+}
diff --git a/fs/xfs/libxfs/xfs_imeta.h b/fs/xfs/libxfs/xfs_imeta.h
index e9fe012a844d..ef80182c4f9a 100644
--- a/fs/xfs/libxfs/xfs_imeta.h
+++ b/fs/xfs/libxfs/xfs_imeta.h
@@ -27,7 +27,7 @@ struct xfs_imeta_path {
/* Cleanup widget for metadata inode creation and deletion. */
struct xfs_imeta_end {
- /* empty for now */
+ struct xfs_inode *dp;
};
/* Lookup keys for static metadata inodes. */
@@ -36,6 +36,7 @@ extern const struct xfs_imeta_path XFS_IMETA_RTSUMMARY;
extern const struct xfs_imeta_path XFS_IMETA_USRQUOTA;
extern const struct xfs_imeta_path XFS_IMETA_GRPQUOTA;
extern const struct xfs_imeta_path XFS_IMETA_PRJQUOTA;
+extern const struct xfs_imeta_path XFS_IMETA_METADIR;
int xfs_imeta_lookup(struct xfs_mount *mp, const struct xfs_imeta_path *path,
xfs_ino_t *ino);
@@ -52,6 +53,7 @@ void xfs_imeta_end_update(struct xfs_mount *mp, struct xfs_imeta_end *cleanup,
bool xfs_is_static_meta_ino(struct xfs_mount *mp, xfs_ino_t ino);
int xfs_imeta_mount(struct xfs_mount *mp);
+void xfs_imeta_droplink(struct xfs_inode *ip);
unsigned int xfs_imeta_create_space_res(struct xfs_mount *mp);
unsigned int xfs_imeta_unlink_space_res(struct xfs_mount *mp);
diff --git a/fs/xfs/libxfs/xfs_inode_util.c b/fs/xfs/libxfs/xfs_inode_util.c
index 8e7c6b9d9ec6..ab9409ae89e9 100644
--- a/fs/xfs/libxfs/xfs_inode_util.c
+++ b/fs/xfs/libxfs/xfs_inode_util.c
@@ -20,6 +20,7 @@
#include "xfs_error.h"
#include "xfs_trace.h"
#include "xfs_inode_item.h"
+#include "xfs_imeta.h"
uint16_t
xfs_flags2diflags(
@@ -913,6 +914,7 @@ xfs_droplink(
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
drop_nlink(VFS_I(ip));
+ xfs_imeta_droplink(ip);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
if (VFS_I(ip)->i_nlink)
diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c
index 4eeafa7b9647..9f96d455f651 100644
--- a/fs/xfs/libxfs/xfs_trans_resv.c
+++ b/fs/xfs/libxfs/xfs_trans_resv.c
@@ -844,7 +844,10 @@ xfs_calc_imeta_create_resv(
unsigned int ret;
ret = xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
- ret += resp->tr_create.tr_logres;
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ ret += max(resp->tr_create.tr_logres, resp->tr_mkdir.tr_logres);
+ else
+ ret += resp->tr_create.tr_logres;
return ret;
}
@@ -854,6 +857,9 @@ xfs_calc_imeta_create_count(
struct xfs_mount *mp,
struct xfs_trans_resv *resp)
{
+ if (xfs_sb_version_hasmetadir(&mp->m_sb))
+ return max(resp->tr_create.tr_logcount,
+ resp->tr_mkdir.tr_logcount);
return resp->tr_create.tr_logcount;
}
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index f142b7c6c96f..4cc9d1ef69a0 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -4069,6 +4069,43 @@ DEFINE_IMETA_SB_EVENT(xfs_imeta_sb_unlink);
DEFINE_IMETA_SB_EVENT(xfs_imeta_sb_link);
DEFINE_AG_ERROR_EVENT(xfs_imeta_end_update);
+DECLARE_EVENT_CLASS(xfs_imeta_dir_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, 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->namelen = name->len;
+ memcpy(__get_str(name), name->name, name->len);
+ ),
+ TP_printk("dev %d:%d dir 0x%llx ino 0x%llx inoname %.*s",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->dp_ino, __entry->ino,
+ __entry->namelen,
+ __get_str(name))
+)
+
+#define DEFINE_IMETA_DIR_EVENT(name) \
+DEFINE_EVENT(xfs_imeta_dir_class, name, \
+ TP_PROTO(struct xfs_inode *dp, struct xfs_name *name, \
+ xfs_ino_t ino), \
+ TP_ARGS(dp, name, ino))
+DEFINE_IMETA_DIR_EVENT(xfs_imeta_dir_lookup_component);
+DEFINE_IMETA_DIR_EVENT(xfs_imeta_dir_lookup_found);
+DEFINE_IMETA_DIR_EVENT(xfs_imeta_dir_try_create);
+DEFINE_IMETA_DIR_EVENT(xfs_imeta_dir_created);
+DEFINE_IMETA_DIR_EVENT(xfs_imeta_dir_unlinked);
+DEFINE_IMETA_DIR_EVENT(xfs_imeta_dir_link);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH