From bf112bef9730c4b21bac704ee5673084dfd36d81 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 1 Sep 2021 11:07:54 -0700 Subject: xfs: create imeta abstractions to get and set metadata inodes Create some helper routines to get and set metadata inode numbers instead of open-coding them throughout xfs. Signed-off-by: Darrick J. Wong --- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_imeta.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_imeta.h | 45 +++++ fs/xfs/libxfs/xfs_types.c | 5 +- fs/xfs/xfs_mount.c | 22 +++ fs/xfs/xfs_trace.h | 36 +++- 6 files changed, 511 insertions(+), 4 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_imeta.c create mode 100644 fs/xfs/libxfs/xfs_imeta.h (limited to 'fs') diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index b19e36d762b0..f14fa453586b 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -37,6 +37,7 @@ xfs-y += $(addprefix libxfs/, \ xfs_ialloc.o \ xfs_ialloc_btree.o \ xfs_iext_tree.o \ + xfs_imeta.o \ xfs_inode_fork.o \ xfs_inode_buf.o \ xfs_inode_util.o \ diff --git a/fs/xfs/libxfs/xfs_imeta.c b/fs/xfs/libxfs/xfs_imeta.c new file mode 100644 index 000000000000..57dce8387028 --- /dev/null +++ b/fs/xfs/libxfs/xfs_imeta.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#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_imeta.h" +#include "xfs_trace.h" +#include "xfs_inode.h" +#include "xfs_quota.h" +#include "xfs_ialloc.h" + +/* + * Metadata Inode Number Management + * ================================ + * + * These functions provide an abstraction layer for looking up, creating, and + * deleting metadata inodes. These pointers live in the in-core superblock, + * so the functions moderate access to those fields and take care of logging. + * + * For the five existing metadata inodes (real time bitmap & summary; and the + * user, group, and quotas) we'll continue to maintain the in-core superblock + * inodes for reads and only require xfs_imeta_create and xfs_imeta_unlink to + * persist changes. New metadata inode types must only use the xfs_imeta_* + * functions. + * + * Callers wishing to create or unlink a metadata inode must pass in a + * xfs_imeta_end structure. After committing or cancelling the transaction, + * 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. + */ + +/* Static metadata inode paths */ + +const struct xfs_imeta_path XFS_IMETA_RTBITMAP = { + .bogus = 0, +}; + +const struct xfs_imeta_path XFS_IMETA_RTSUMMARY = { + .bogus = 1, +}; + +const struct xfs_imeta_path XFS_IMETA_USRQUOTA = { + .bogus = 2, +}; + +const struct xfs_imeta_path XFS_IMETA_GRPQUOTA = { + .bogus = 3, +}; + +const struct xfs_imeta_path XFS_IMETA_PRJQUOTA = { + .bogus = 4, +}; + +/* Are these two paths equal? */ +STATIC bool +xfs_imeta_path_compare( + const struct xfs_imeta_path *a, + const struct xfs_imeta_path *b) +{ + return a == b; +} + +/* Is this path ok? */ +static inline bool +xfs_imeta_path_check( + const struct xfs_imeta_path *path) +{ + return true; +} + +/* Functions for storing and retrieving superblock inode values. */ + +/* Mapping of metadata inode paths to in-core superblock values. */ +static const struct xfs_imeta_sbmap { + const struct xfs_imeta_path *path; + unsigned int offset; +} xfs_imeta_sbmaps[] = { + { + .path = &XFS_IMETA_RTBITMAP, + .offset = offsetof(struct xfs_sb, sb_rbmino), + }, + { + .path = &XFS_IMETA_RTSUMMARY, + .offset = offsetof(struct xfs_sb, sb_rsumino), + }, + { + .path = &XFS_IMETA_USRQUOTA, + .offset = offsetof(struct xfs_sb, sb_uquotino), + }, + { + .path = &XFS_IMETA_GRPQUOTA, + .offset = offsetof(struct xfs_sb, sb_gquotino), + }, + { + .path = &XFS_IMETA_PRJQUOTA, + .offset = offsetof(struct xfs_sb, sb_pquotino), + }, + { NULL, 0 }, +}; + +/* Return a pointer to the in-core superblock inode value. */ +static inline xfs_ino_t * +xfs_imeta_sbmap_to_inop( + struct xfs_mount *mp, + const struct xfs_imeta_sbmap *map) +{ + return (xfs_ino_t *)(((char *)&mp->m_sb) + map->offset); +} + +/* Compute location of metadata inode pointer in the in-core superblock */ +static inline xfs_ino_t * +xfs_imeta_path_to_sb_inop( + struct xfs_mount *mp, + const struct xfs_imeta_path *path) +{ + const struct xfs_imeta_sbmap *p; + + for (p = xfs_imeta_sbmaps; p->path; p++) + if (xfs_imeta_path_compare(p->path, path)) + return xfs_imeta_sbmap_to_inop(mp, p); + + return NULL; +} + +/* Look up a superblock metadata inode by its path. */ +STATIC int +xfs_imeta_sb_lookup( + struct xfs_mount *mp, + const struct xfs_imeta_path *path, + xfs_ino_t *inop) +{ + xfs_ino_t *sb_inop; + + sb_inop = xfs_imeta_path_to_sb_inop(mp, path); + if (!sb_inop) + return -EINVAL; + + trace_xfs_imeta_sb_lookup(mp, sb_inop); + *inop = *sb_inop; + return 0; +} + +/* + * Create a new metadata inode and set a superblock pointer to this new inode. + * The superblock field must not already be pointing to an inode. + */ +STATIC int +xfs_imeta_sb_create( + struct xfs_trans **tpp, + const struct xfs_imeta_path *path, + umode_t mode, + unsigned int flags, + struct xfs_inode **ipp) +{ + struct xfs_icreate_args args = { + .nlink = S_ISDIR(mode) ? 2 : 1, + }; + struct xfs_mount *mp = (*tpp)->t_mountp; + xfs_ino_t *sb_inop; + xfs_ino_t ino; + int error; + + xfs_icreate_args_rootfile(&args, mode); + + /* Reject if the sb already points to some inode. */ + sb_inop = xfs_imeta_path_to_sb_inop(mp, path); + if (!sb_inop) + return -EINVAL; + + if (*sb_inop != NULLFSINO) + return -EEXIST; + + /* Create a new inode and set the sb pointer. */ + error = xfs_dialloc(tpp, 0, mode, &ino); + if (error) + return error; + error = xfs_icreate(*tpp, ino, &args, ipp); + if (error) + return error; + + /* Attach dquots to this file. Caller should have allocated them! */ + if (!(flags & XFS_IMETA_CREATE_NOQUOTA)) { + error = xfs_qm_dqattach_locked(*ipp, false); + if (error) + return error; + xfs_trans_mod_dquot_byino(*tpp, *ipp, XFS_TRANS_DQ_ICOUNT, 1); + } + + /* Update superblock pointer. */ + *sb_inop = ino; + trace_xfs_imeta_sb_create(mp, sb_inop); + xfs_log_sb(*tpp); + return 0; +} + +/* + * Clear the given inode pointer from the superblock and drop the link count + * of the metadata inode. + */ +STATIC int +xfs_imeta_sb_unlink( + struct xfs_trans **tpp, + const struct xfs_imeta_path *path, + struct xfs_inode *ip) +{ + struct xfs_mount *mp = (*tpp)->t_mountp; + xfs_ino_t *sb_inop; + + sb_inop = xfs_imeta_path_to_sb_inop(mp, path); + if (!sb_inop) + return -EINVAL; + + /* Reject if the sb doesn't point to the inode that was passed in. */ + if (*sb_inop != ip->i_ino) + return -ENOENT; + + *sb_inop = NULLFSINO; + trace_xfs_imeta_sb_unlink(mp, sb_inop); + xfs_log_sb(*tpp); + return xfs_droplink(*tpp, ip); +} + +/* Set the given inode pointer in the superblock. */ +STATIC int +xfs_imeta_sb_link( + struct xfs_trans *tp, + const struct xfs_imeta_path *path, + struct xfs_inode *ip) +{ + struct xfs_mount *mp = tp->t_mountp; + xfs_ino_t *sb_inop; + + sb_inop = xfs_imeta_path_to_sb_inop(mp, path); + if (!sb_inop) + return -EINVAL; + if (*sb_inop == NULLFSINO) + return -EEXIST; + + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); + + inc_nlink(VFS_I(ip)); + *sb_inop = ip->i_ino; + trace_xfs_imeta_sb_link(mp, sb_inop); + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + xfs_log_sb(tp); + return 0; +} + +/* General functions for managing metadata inode pointers */ + +/* + * Is this metadata inode pointer ok? We allow the fields to be set to + * NULLFSINO if the metadata structure isn't present, and we don't allow + * obviously incorrect inode pointers. + */ +static inline bool +xfs_imeta_verify( + struct xfs_mount *mp, + xfs_ino_t ino) +{ + if (ino == NULLFSINO) + return true; + return xfs_verify_ino(mp, ino); +} + +/* Look up a metadata inode by its path. */ +int +xfs_imeta_lookup( + struct xfs_mount *mp, + const struct xfs_imeta_path *path, + xfs_ino_t *inop) +{ + xfs_ino_t ino; + int error; + + ASSERT(xfs_imeta_path_check(path)); + + error = xfs_imeta_sb_lookup(mp, path, &ino); + if (error) + return error; + + if (!xfs_imeta_verify(mp, ino)) + return -EFSCORRUPTED; + + *inop = ino; + return 0; +} + +/* + * Create a metadata inode with the given @mode, and insert it into the + * metadata directory tree at the given @path. The path (up to the final + * component) must already exist. The new metadata inode @ipp will be ijoined + * and logged to @tpp, with the ILOCK held until the next transaction commit. + * The caller must provide a @cleanup structure. + * + * Callers must ensure that the root dquots are allocated, if applicable. + * + * NOTE: This function may pass a child inode @ipp back to the caller even if + * it returns a negative error code. If an inode is passed back, the caller + * must finish setting up the incore inode before releasing it. + */ +int +xfs_imeta_create( + struct xfs_trans **tpp, + const struct xfs_imeta_path *path, + umode_t mode, + unsigned int flags, + struct xfs_inode **ipp, + struct xfs_imeta_end *cleanup) +{ + ASSERT(xfs_imeta_path_check(path)); + *ipp = NULL; + + return xfs_imeta_sb_create(tpp, path, mode, flags, ipp); +} + +/* + * Unlink a metadata inode @ip from the metadata directory given by @path. The + * metadata inode must not be ILOCKed. Upon return, the inode will be ijoined + * and logged to @tpp, and returned with reduced link count, ready to be + * released. The caller must provide a @cleanup structure. + */ +int +xfs_imeta_unlink( + struct xfs_trans **tpp, + const struct xfs_imeta_path *path, + struct xfs_inode *ip, + struct xfs_imeta_end *cleanup) +{ + ASSERT(xfs_imeta_path_check(path)); + ASSERT(xfs_imeta_verify((*tpp)->t_mountp, ip->i_ino)); + + return xfs_imeta_sb_unlink(tpp, path, ip); +} + +/* + * Link the metadata directory given by @path point to the given inode number. + * The path must not already exist. The caller must not hold the ILOCK, and + * the function will return with the inode joined to the transaction. + */ +int +xfs_imeta_link( + struct xfs_trans *tp, + const struct xfs_imeta_path *path, + struct xfs_inode *ip, + struct xfs_imeta_end *cleanup) +{ + ASSERT(xfs_imeta_path_check(path)); + + return xfs_imeta_sb_link(tp, path, ip); +} + +/* + * Clean up after committing (or cancelling) a metadata inode creation or + * removal. + */ +void +xfs_imeta_end_update( + struct xfs_mount *mp, + struct xfs_imeta_end *cleanup, + int error) +{ + trace_xfs_imeta_end_update(mp, error, __return_address); +} + +/* Does this inode number refer to a static metadata inode? */ +bool +xfs_is_static_meta_ino( + struct xfs_mount *mp, + xfs_ino_t ino) +{ + const struct xfs_imeta_sbmap *p; + + if (ino == NULLFSINO) + return false; + + for (p = xfs_imeta_sbmaps; p->path; p++) + if (ino == *xfs_imeta_sbmap_to_inop(mp, p)) + return true; + + return false; +} + +/* Ensure that the in-core superblock has all the values that it should. */ +int +xfs_imeta_mount( + struct xfs_mount *mp) +{ + return 0; +} diff --git a/fs/xfs/libxfs/xfs_imeta.h b/fs/xfs/libxfs/xfs_imeta.h new file mode 100644 index 000000000000..9d0dfafa14fd --- /dev/null +++ b/fs/xfs/libxfs/xfs_imeta.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#ifndef __XFS_IMETA_H__ +#define __XFS_IMETA_H__ + +/* Key for looking up metadata inodes. */ +struct xfs_imeta_path { + /* Temporary: integer to keep the static imeta definitions unique */ + int bogus; +}; + +/* Cleanup widget for metadata inode creation and deletion. */ +struct xfs_imeta_end { + /* empty for now */ +}; + +/* Lookup keys for static metadata inodes. */ +extern const struct xfs_imeta_path XFS_IMETA_RTBITMAP; +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; + +int xfs_imeta_lookup(struct xfs_mount *mp, const struct xfs_imeta_path *path, + xfs_ino_t *ino); + +/* Don't allocate quota for this file. */ +#define XFS_IMETA_CREATE_NOQUOTA (1 << 0) +int xfs_imeta_create(struct xfs_trans **tpp, const struct xfs_imeta_path *path, + umode_t mode, unsigned int flags, struct xfs_inode **ipp, + struct xfs_imeta_end *cleanup); +int xfs_imeta_unlink(struct xfs_trans **tpp, const struct xfs_imeta_path *path, + struct xfs_inode *ip, struct xfs_imeta_end *cleanup); +int xfs_imeta_link(struct xfs_trans *tp, const struct xfs_imeta_path *path, + struct xfs_inode *ip, struct xfs_imeta_end *cleanup); +void xfs_imeta_end_update(struct xfs_mount *mp, struct xfs_imeta_end *cleanup, + int error); + +bool xfs_is_static_meta_ino(struct xfs_mount *mp, xfs_ino_t ino); +int xfs_imeta_mount(struct xfs_mount *mp); + +#endif /* __XFS_IMETA_H__ */ diff --git a/fs/xfs/libxfs/xfs_types.c b/fs/xfs/libxfs/xfs_types.c index 96647008f9e0..d12dc9a4bdb9 100644 --- a/fs/xfs/libxfs/xfs_types.c +++ b/fs/xfs/libxfs/xfs_types.c @@ -12,6 +12,7 @@ #include "xfs_bit.h" #include "xfs_mount.h" #include "xfs_ag.h" +#include "xfs_imeta.h" /* Find the size of the AG, in blocks. */ inline xfs_agblock_t @@ -188,9 +189,7 @@ xfs_internal_inum( struct xfs_mount *mp, xfs_ino_t ino) { - return ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || - (xfs_has_quota(mp) && - xfs_is_quota_inode(&mp->m_sb, ino)); + return xfs_is_static_meta_ino(mp, ino); } /* diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 490bf1266661..a53e6aa54675 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -33,6 +33,7 @@ #include "xfs_health.h" #include "xfs_trace.h" #include "xfs_ag.h" +#include "xfs_imeta.h" static DEFINE_MUTEX(xfs_uuid_table_mutex); static int xfs_uuid_table_size; @@ -514,6 +515,22 @@ xfs_check_summary_counts( return xfs_initialize_perag_data(mp, mp->m_sb.sb_agcount); } +STATIC int +xfs_mountfs_imeta( + struct xfs_mount *mp) +{ + int error; + + error = xfs_imeta_mount(mp); + if (error) { + xfs_warn(mp, "Failed to load metadata inode info, error %d", + error); + return error; + } + + return 0; +} + /* * Flush and reclaim dirty inodes in preparation for unmount. Inodes and * internal inode structures can be sitting in the CIL and AIL at this point, @@ -807,6 +824,11 @@ xfs_mountfs( mp->m_features |= XFS_FEAT_ATTR2; } + /* Load the metadata directory tree. */ + error = xfs_mountfs_imeta(mp); + if (error) + goto out_log_dealloc; + /* * Get and sanity-check the root inode. * Save the pointer to it in the mount structure. diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index e235df640b60..56446233f3f0 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -149,7 +149,7 @@ DEFINE_ATTR_LIST_EVENT(xfs_attr_list_notfound); DEFINE_ATTR_LIST_EVENT(xfs_attr_leaf_list); DEFINE_ATTR_LIST_EVENT(xfs_attr_node_list); -TRACE_EVENT(xlog_intent_recovery_failed, +DECLARE_EVENT_CLASS(xfs_fs_error_class, TP_PROTO(struct xfs_mount *mp, int error, void *function), TP_ARGS(mp, error, function), TP_STRUCT__entry( @@ -166,6 +166,11 @@ TRACE_EVENT(xlog_intent_recovery_failed, MAJOR(__entry->dev), MINOR(__entry->dev), __entry->error, __entry->function) ); +#define DEFINE_FS_ERROR_EVENT(name) \ +DEFINE_EVENT(xfs_fs_error_class, name, \ + TP_PROTO(struct xfs_mount *mp, int error, void *function), \ + TP_ARGS(mp, error, function)) +DEFINE_FS_ERROR_EVENT(xlog_intent_recovery_failed); DECLARE_EVENT_CLASS(xfs_perag_class, TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, int refcount, @@ -4581,6 +4586,35 @@ TRACE_EVENT(xfs_swapext_delta_nextents, __entry->d_nexts1, __entry->d_nexts2) ); +DECLARE_EVENT_CLASS(xfs_imeta_sb_class, + TP_PROTO(struct xfs_mount *mp, xfs_ino_t *sb_inop), + TP_ARGS(mp, sb_inop), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, sb_offset) + __field(xfs_ino_t, ino) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->sb_offset = (char *)sb_inop - (char *)&mp->m_sb; + __entry->ino = *sb_inop; + ), + TP_printk("dev %d:%d sb_offset 0x%x ino 0x%llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->sb_offset, + __entry->ino) +) + +#define DEFINE_IMETA_SB_EVENT(name) \ +DEFINE_EVENT(xfs_imeta_sb_class, name, \ + TP_PROTO(struct xfs_mount *mp, xfs_ino_t *sb_inop), \ + TP_ARGS(mp, sb_inop)) +DEFINE_IMETA_SB_EVENT(xfs_imeta_sb_lookup); +DEFINE_IMETA_SB_EVENT(xfs_imeta_sb_create); +DEFINE_IMETA_SB_EVENT(xfs_imeta_sb_unlink); +DEFINE_IMETA_SB_EVENT(xfs_imeta_sb_link); +DEFINE_FS_ERROR_EVENT(xfs_imeta_end_update); + #endif /* _TRACE_XFS_H */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3