[PATCH 27/27] mkfs: create the realtime rmap inode

Darrick J. Wong
2025-02-06T22:56:46Z

Create a realtime rmapbt inode if we format the fs with realtime
and rmap.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
libxfs/init.c | 7 ----
libxfs/libxfs_api_defs.h | 3 ++
mkfs/proto.c | 29 +++++++++++++++
mkfs/xfs_mkfs.c | 87 +++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 114 insertions(+), 12 deletions(-)

diff --git a/libxfs/init.c b/libxfs/init.c
index 02a4cfdf38b198..f92805620c33f1 100644
--- a/libxfs/init.c
+++ b/libxfs/init.c
@@ -312,13 +312,6 @@ rtmount_init(
return -1;
}

- if (xfs_has_rmapbt(mp)) {
- fprintf(stderr,
- _("%s: Reverse mapping btree not compatible with realtime device. Please try a newer xfsprogs.\n"),
- progname);
- return -1;
- }
-
if (mp->m_rtdev_targp->bt_bdev == 0 && !xfs_is_debugger(mp)) {
fprintf(stderr, _("%s: filesystem has a realtime subvolume\n"),
progname);
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 193b1eeaa7537e..df24f36f0d2874 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -224,6 +224,8 @@

#define xfs_metafile_iget libxfs_metafile_iget
#define xfs_trans_metafile_iget libxfs_trans_metafile_iget
+#define xfs_metafile_resv_free libxfs_metafile_resv_free
+#define xfs_metafile_resv_init libxfs_metafile_resv_init
#define xfs_metafile_set_iflag libxfs_metafile_set_iflag
#define xfs_metadir_cancel libxfs_metadir_cancel
#define xfs_metadir_commit libxfs_metadir_commit
@@ -324,6 +326,7 @@
#define xfs_rtrmapbt_droot_maxrecs libxfs_rtrmapbt_droot_maxrecs
#define xfs_rtrmapbt_maxlevels_ondisk libxfs_rtrmapbt_maxlevels_ondisk
#define xfs_rtrmapbt_init_cursor libxfs_rtrmapbt_init_cursor
+#define xfs_rtrmapbt_init_rtsb libxfs_rtrmapbt_init_rtsb
#define xfs_rtrmapbt_maxrecs libxfs_rtrmapbt_maxrecs
#define xfs_rtrmapbt_mem_init libxfs_rtrmapbt_mem_init
#define xfs_rtrmapbt_mem_cursor libxfs_rtrmapbt_mem_cursor
diff --git a/mkfs/proto.c b/mkfs/proto.c
index 60e5c7d02713d0..2c453480271666 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -1095,6 +1095,28 @@ rtinit_nogroups(
}
}

+static int
+init_rtrmap_for_rtsb(
+ struct xfs_rtgroup *rtg)
+{
+ struct xfs_mount *mp = rtg_mount(rtg);
+ struct xfs_trans *tp;
+ int error;
+
+ error = -libxfs_trans_alloc_inode(rtg_rmap(rtg),
+ &M_RES(mp)->tr_itruncate, 0, 0, false, &tp);
+ if (error)
+ return error;
+
+ error = -libxfs_rtrmapbt_init_rtsb(mp, rtg, tp);
+ if (error) {
+ libxfs_trans_cancel(tp);
+ return error;
+ }
+
+ return -libxfs_trans_commit(tp);
+}
+
static void
rtinit_groups(
struct xfs_mount *mp)
@@ -1115,6 +1137,13 @@ rtinit_groups(
error);
}

+ if (xfs_has_rtsb(mp) && xfs_has_rtrmapbt(mp) &&
+ rtg_rgno(rtg) == 0) {
+ error = init_rtrmap_for_rtsb(rtg);
+ if (error)
+ fail(_("rtrmap rtsb init failed"), error);
+ }
+
rtfreesp_init(rtg);
}
}
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index f5556fcc4040ed..c8042261328171 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -2678,12 +2678,18 @@ _("reflink not supported with realtime devices\n"));
}
cli->sb_feat.reflink = false;

- if (cli->sb_feat.rmapbt && cli_opt_set(&mopts, M_RMAPBT)) {
- fprintf(stderr,
-_("rmapbt not supported with realtime devices\n"));
- usage();
+ if (!cli->sb_feat.metadir && cli->sb_feat.rmapbt) {
+ if (cli_opt_set(&mopts, M_RMAPBT) &&
+ cli_opt_set(&mopts, M_METADIR)) {
+ fprintf(stderr,
+_("rmapbt not supported on realtime devices without metadir feature\n"));
+ usage();
+ } else if (cli_opt_set(&mopts, M_RMAPBT)) {
+ cli->sb_feat.metadir = true;
+ } else {
+ cli->sb_feat.rmapbt = false;
+ }
}
- cli->sb_feat.rmapbt = false;
}

if ((cli->fsx.fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
@@ -5006,6 +5012,74 @@ write_rtsb(
libxfs_buf_relse(sb_bp);
}

+static inline void
+prealloc_fail(
+ struct xfs_mount *mp,
+ int error,
+ xfs_filblks_t ask,
+ const char *tag)
+{
+ if (error == ENOSPC)
+ fprintf(stderr,
+ _("%s: cannot handle expansion of %s; need %llu free blocks, have %llu\n"),
+ progname, tag, (unsigned long long)ask,
+ (unsigned long long)mp->m_sb.sb_fdblocks);
+ else
+ fprintf(stderr,
+ _("%s: error %d while checking free space for %s\n"),
+ progname, error, tag);
+ exit(1);
+}
+
+/*
+ * Make sure there's enough space on the data device to handle realtime
+ * metadata btree expansions.
+ */
+static void
+check_rt_meta_prealloc(
+ struct xfs_mount *mp)
+{
+ struct xfs_perag *pag = NULL;
+ struct xfs_rtgroup *rtg = NULL;
+ xfs_filblks_t ask;
+ int error;
+
+ /*
+ * First create all the per-AG reservations, since they take from the
+ * free block count. Each AG should start with enough free space for
+ * the per-AG reservation.
+ */
+ mp->m_finobt_nores = false;
+
+ while ((pag = xfs_perag_next(mp, pag))) {
+ error = -libxfs_ag_resv_init(pag, NULL);
+ if (error && error != ENOSPC) {
+ fprintf(stderr,
+ _("%s: error %d while checking AG free space for realtime metadata\n"),
+ progname, error);
+ exit(1);
+ }
+ }
+
+ /* Realtime metadata btree inode */
+ while ((rtg = xfs_rtgroup_next(mp, rtg))) {
+ ask = libxfs_rtrmapbt_calc_reserves(mp);
+ error = -libxfs_metafile_resv_init(rtg_rmap(rtg), ask);
+ if (error)
+ prealloc_fail(mp, error, ask, _("realtime rmap btree"));
+ }
+
+ /* Unreserve the realtime metadata reservations. */
+ while ((rtg = xfs_rtgroup_next(mp, rtg)))
+ libxfs_metafile_resv_free(rtg_rmap(rtg));
+
+ /* Unreserve the per-AG reservations. */
+ while ((pag = xfs_perag_next(mp, pag)))
+ libxfs_ag_resv_free(pag);
+
+ mp->m_finobt_nores = false;
+}
+
int
main(
int argc,
@@ -5343,6 +5417,9 @@ main(
*/
check_root_ino(mp);

+ /* Make sure we can handle space preallocations of rt metadata btrees */
+ check_rt_meta_prealloc(mp);
+
/*
* Re-write multiple secondary superblocks with rootinode field set
*/