]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
mkfs: format realtime groups xfs-per-rtg-bitmap-rebase
authorDarrick J. Wong <djwong@kernel.org>
Mon, 5 Aug 2024 18:20:30 +0000 (11:20 -0700)
committerChristoph Hellwig <hch@lst.de>
Tue, 6 Aug 2024 12:53:50 +0000 (05:53 -0700)
Create filesystems with the realtime group feature enabled.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
17 files changed:
libfrog/div64.h
libfrog/util.c
libfrog/util.h
libxfs/libxfs_api_defs.h
libxfs/libxfs_priv.h
libxfs/topology.c
libxfs/topology.h
libxfs/xfs_format.h
man/man8/mkfs.xfs.8.in
mkfs/lts_4.19.conf
mkfs/lts_5.10.conf
mkfs/lts_5.15.conf
mkfs/lts_5.4.conf
mkfs/lts_6.1.conf
mkfs/lts_6.6.conf
mkfs/proto.c
mkfs/xfs_mkfs.c

index 673b01cbab34deca85de929674ebb226c947cb4b..4b0d4c3b3c704d14f7c36c111b6c3b6d5900c976 100644 (file)
@@ -93,4 +93,10 @@ howmany_64(uint64_t x, uint32_t y)
        return x;
 }
 
+static inline __attribute__((const))
+int is_power_of_2(unsigned long n)
+{
+       return (n != 0 && ((n & (n - 1)) == 0));
+}
+
 #endif /* LIBFROG_DIV64_H_ */
index 46047571a5531f16f8832c64c6734dbb5e9ef45c..4e130c884c17a2124bb5da1085f04400cea3f65e 100644 (file)
@@ -36,3 +36,15 @@ memchr_inv(const void *start, int c, size_t bytes)
 
        return NULL;
 }
+
+unsigned int
+log2_rounddown(unsigned long long i)
+{
+       int     rval;
+
+       for (rval = NBBY * sizeof(i) - 1; rval >= 0; rval--) {
+               if ((1ULL << rval) < i)
+                       break;
+       }
+       return rval;
+}
index 8b4ee7c1333b6ba23dba0f6e868ff4ff91c96886..d1c4dd40fc926c0c64964604078d5b8455a9f2a0 100644 (file)
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 
 unsigned int   log2_roundup(unsigned int i);
+unsigned int   log2_rounddown(unsigned long long i);
 
 #define min_t(type,x,y) \
        ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
index 0ad18a389f9383e510ff6c42b895e5b50f78351f..e42ecf8c49921287a438c6a6525b3beb8302984e 100644 (file)
@@ -94,6 +94,7 @@
 #define xfs_btree_stage_afakeroot      libxfs_btree_stage_afakeroot
 #define xfs_btree_stage_ifakeroot      libxfs_btree_stage_ifakeroot
 #define xfs_btree_visit_blocks         libxfs_btree_visit_blocks
+#define xfs_buf_delwri_queue           libxfs_buf_delwri_queue
 #define xfs_buf_delwri_submit          libxfs_buf_delwri_submit
 #define xfs_buf_get                    libxfs_buf_get
 #define xfs_buf_get_uncached           libxfs_buf_get_uncached
index 0f42f8a9060ed91d3c41f87a74a15de4b7a4796d..e141d6ac734a0fa50fe6a35034db51bbc9cbc9d9 100644 (file)
@@ -338,12 +338,6 @@ find_next_zero_bit(const unsigned long *addr, unsigned long size,
 }
 #define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0)
 
-static inline __attribute__((const))
-int is_power_of_2(unsigned long n)
-{
-       return (n != 0 && ((n & (n - 1)) == 0));
-}
-
 /*
  * xfs_iroundup: round up argument to next power of two
  */
index 94adb5be7bdcae7d9487a9d1950eb2401200e614..8c6affb4c4e43611e66132cf5c6879515530e453 100644 (file)
@@ -87,6 +87,48 @@ done:
        *agcount = dblocks / blocks + (dblocks % blocks != 0);
 }
 
+void
+calc_default_rtgroup_geometry(
+       int             blocklog,
+       uint64_t        rblocks,
+       uint64_t        *rgsize,
+       uint64_t        *rgcount)
+{
+       uint64_t        blocks = 0;
+       int             shift = 0;
+
+       /*
+        * For a single underlying storage device over 4TB in size use the
+        * maximum rtgroup size.  Between 128MB and 4TB, just use 4 rtgroups
+        * and scale up smoothly between min/max rtgroup sizes.
+        */
+       if (rblocks >= TERABYTES(4, blocklog)) {
+               blocks = XFS_MAX_RGBLOCKS;
+               goto done;
+       }
+       if (rblocks >= MEGABYTES(128, blocklog)) {
+               shift = XFS_NOMULTIDISK_AGLOG;
+               goto calc_blocks;
+       }
+
+       /*
+        * If rblocks is not evenly divisible by the number of desired rt
+        * groups, round "blocks" up so we don't lose the last bit of the
+        * filesystem. The same principle applies to the rt group count, so we
+        * don't lose the last rt group!
+        */
+calc_blocks:
+       ASSERT(shift >= 0 && shift <= XFS_MULTIDISK_AGLOG);
+       blocks = rblocks >> shift;
+       if (rblocks & xfs_mask32lo(shift)) {
+               if (blocks < XFS_MAX_RGBLOCKS)
+                   blocks++;
+       }
+done:
+       *rgsize = blocks;
+       *rgcount = rblocks / blocks + (rblocks % blocks != 0);
+}
+
 /*
  * Check for existing filesystem or partition table on device.
  * Returns:
index fa0a23b7738624c1cf898a738345b7173d5bf630..207a8a7f150556174779c8d37aa2aec49a9e6ee0 100644 (file)
@@ -37,6 +37,9 @@ calc_default_ag_geometry(
        uint64_t        *agsize,
        uint64_t        *agcount);
 
+void calc_default_rtgroup_geometry(int blocklog, uint64_t rblocks,
+               uint64_t *rgsize, uint64_t *rgcount);
+
 extern int
 check_overwrite(
        const char      *device);
index 46621b29d78c2e1b6da47d24ce64f65a2d20bdbe..73a0de2da948c92048ff627020f419841365cb59 100644 (file)
@@ -370,8 +370,9 @@ xfs_sb_has_compat_feature(
 #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_REFLINK | \
+                XFS_SB_FEAT_RO_COMPAT_INOBTCNT | \
+                XFS_SB_FEAT_RO_COMPAT_RTSB)
 #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN  ~XFS_SB_FEAT_RO_COMPAT_ALL
 static inline bool
 xfs_sb_has_ro_compat_feature(
@@ -400,6 +401,7 @@ xfs_sb_has_ro_compat_feature(
                 XFS_SB_FEAT_INCOMPAT_NREXT64 | \
                 XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \
                 XFS_SB_FEAT_INCOMPAT_PARENT | \
+                XFS_SB_FEAT_INCOMPAT_RTGROUPS | \
                 XFS_SB_FEAT_INCOMPAT_METADIR)
 
 #define XFS_SB_FEAT_INCOMPAT_UNKNOWN   ~XFS_SB_FEAT_INCOMPAT_ALL
index a76c6a9ee695b33f349f37f72147c788659680d9..82bd8c81d614547000a0941669e813990d217457 100644 (file)
@@ -1129,6 +1129,75 @@ or logical volume containing the section.
 .BI noalign
 This option disables stripe size detection, enforcing a realtime device with no
 stripe geometry.
+.TP
+.BI rtsb= value
+Write a superblock to the realtime volume to make it easier to identify them.
+This feature is only available if the realtime allocation groups feature is
+enabled.
+.IP
+By default,
+.B mkfs.xfs
+will not enable this feature unless realtime allocation groups are also enabled.
+If the option
+.B \-r rtsb=0
+is used, the realtime superblock feature is not supported and is disabled.
+.TP
+.BI rtgroups= value
+This feature breaks the realtime section into multiple allocation groups for
+improved scalability.
+This feature is only available if the metadata directory tree feature is
+enabled.
+.IP
+By default,
+.B mkfs.xfs
+will not enable this feature.
+If the option
+.B \-r rtgroups=0
+is used, the rt group feature is not supported and is disabled.
+.TP
+.BI rgcount=
+This is used to specify the number of allocation groups in the realtime
+section.
+The realtime section of the filesystem can be divided into allocation groups to
+improve the performance of XFS.
+More allocation groups imply that more parallelism can be achieved when
+allocating blocks.
+The minimum allocation group size is 2 realtime extents; the maximum size is
+2^31 blocks.
+The rt section of the filesystem is divided into
+.I value
+allocation groups (default value is scaled automatically based
+on the underlying device size).
+.TP
+.BI rgsize= value
+This is an alternative to using the
+.B rgcount
+suboption. The
+.I value
+is the desired size of the realtime allocation group expressed in bytes
+(usually using the
+.BR m " or " g
+suffixes).
+This value must be a multiple of the realtime extent size,
+must be at least two realtime extents, and no more than 2^31 blocks.
+The
+.B rgcount
+and
+.B rgsize
+suboptions are mutually exclusive.
+.TP
+.BI rtsb= value
+This feature writes a superblock at the start of the realtime section so that
+they may be identified more easily.
+This feature is only available if the realtime groups feature is
+enabled.
+.IP
+By default,
+.B mkfs.xfs
+will automatically enable this feature if realtime groups are enabled.
+If the option
+.B \-r rtsb=0
+is used, the realtime superblock feature is not supported and is disabled.
 .RE
 .PP
 .PD 0
index 303e2cc77e0913d43354213654e048b71c6bbfa5..994c0b0346c4b0dcdf13bb3b2b9ac645c60258cc 100644 (file)
@@ -18,3 +18,7 @@ exchange=0
 
 [naming]
 parent=0
+
+[realtime]
+rtgroups=0
+rtsb=0
index 0894fc3c3203c4c373f1f6e6329198a45953afb1..60219ce2da13ade5f1b1d760be5d9156005d873c 100644 (file)
@@ -18,3 +18,7 @@ exchange=0
 
 [naming]
 parent=0
+
+[realtime]
+rtgroups=0
+rtsb=0
index aea67b5093c79756de27db935b8b66b3a8bc95a1..a1ff3eb07bb8cfd659c8756b62e66677653af4fa 100644 (file)
@@ -18,3 +18,7 @@ exchange=0
 
 [naming]
 parent=0
+
+[realtime]
+rtgroups=0
+rtsb=0
index d1e4075af88d3256c5029b9d17038e16acb0d3ca..78f8a706ce9baa4da79c7a4356340c7bf15fbdb8 100644 (file)
@@ -18,3 +18,7 @@ exchange=0
 
 [naming]
 parent=0
+
+[realtime]
+rtgroups=0
+rtsb=0
index 0d9b0aef773bb3ad3d8f02771cbc2372d9fbdf77..b878b4e3a02b3ba2792939d1584d6fcbbb64416f 100644 (file)
@@ -18,3 +18,7 @@ exchange=0
 
 [naming]
 parent=0
+
+[realtime]
+rtgroups=0
+rtsb=0
index 5b843205b96d3eaaf7947b317ad6e757cd5589e4..3e891665197349a11b4437446cda0e1472421cd8 100644 (file)
@@ -18,3 +18,7 @@ exchange=0
 
 [naming]
 parent=0
+
+[realtime]
+rtgroups=0
+rtsb=0
index c595d0b89e4a42ca5a084f2fefd78415fadd4afe..c6d3402f8d408e0391a4444a7986e1201797f610 100644 (file)
@@ -872,7 +872,8 @@ rtbitmap_create(
 
        ip->i_disk_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
        ip->i_diflags |= XFS_DIFLAG_NEWRTBM;
-       inode_set_atime(VFS_I(ip), 0, 0);
+       if (!xfs_has_rtgroups(mp))
+               inode_set_atime(VFS_I(ip), 0, 0);
 
        mp->m_sb.sb_rbmino = ip->i_ino;
 }
@@ -889,8 +890,8 @@ rtsummary_create(
 }
 
 /*
- * Free the whole realtime area using transactions.
- * Do one transaction per bitmap block.
+ * Free the whole realtime area using transactions.  Each transaction may clear
+ * up to 32 rtbitmap blocks.
  */
 static void
 rtfreesp_init(
@@ -898,8 +899,8 @@ rtfreesp_init(
 {
        struct xfs_mount        *mp = rtg->rtg_mount;
        struct xfs_trans        *tp;
-       xfs_rtxnum_t            rtx;
-       xfs_rtxnum_t            ertx;
+       const xfs_rtxnum_t      max_rtx = mp->m_rtx_per_rbmblock * 32;
+       xfs_rtxnum_t            start_rtx = 0;
        int                     error;
 
        /*
@@ -915,29 +916,51 @@ rtfreesp_init(
        if (error)
                fail(_("Initialization of rtsummary inode failed"), error);
 
+       if (!mp->m_sb.sb_rbmblocks)
+               return;
+
        /*
         * Then free the blocks into the allocator, one bitmap block at a time.
         */
-       for (rtx = 0; rtx < mp->m_sb.sb_rextents; rtx = ertx) {
+       while (start_rtx < rtg->rtg_extents) {
+               xfs_rtxlen_t    nr = min(rtg->rtg_extents - start_rtx, max_rtx);
+
+               /*
+                * The rt superblock, if present, must not be marked free.
+                * This may be the only rtx in the entire volume.
+                */
+               if (xfs_has_rtsb(mp) && rtg->rtg_rgno == 0 && start_rtx == 0) {
+                       start_rtx++;
+                       nr--;
+
+                       if (start_rtx == rtg->rtg_extents)
+                               break;
+               }
+
                error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate,
                                0, 0, 0, &tp);
                if (error)
                        res_failed(error);
 
                libxfs_trans_ijoin(tp, rtg->rtg_inodes[XFS_RTG_BITMAP], 0);
-               ertx = min(mp->m_sb.sb_rextents,
-                          rtx + NBBY * mp->m_sb.sb_blocksize);
-
-               error = -libxfs_rtfree_extent(tp, rtg, rtx,
-                               (xfs_rtxlen_t)(ertx - rtx));
+               error = -libxfs_rtfree_extent(tp, rtg, start_rtx, nr);
                if (error) {
-                       fail(_("Error initializing the realtime space"),
-                               error);
+                       fprintf(stderr,
+ _("Error initializing the realtime free space near rgno %u rtx %lld-%lld (max %lld): %s\n"),
+                                       rtg->rtg_rgno,
+                                       (unsigned long long)start_rtx,
+                                       (unsigned long long)start_rtx + nr - 1,
+                                       (unsigned long long)rtg->rtg_extents,
+                                       strerror(error));
+                       exit(1);
                }
+
                error = -libxfs_trans_commit(tp);
                if (error)
                        fail(_("Initialization of the realtime space failed"),
                                        error);
+
+               start_rtx += nr;
        }
 }
 
@@ -950,12 +973,30 @@ rtinit(
 {
        struct xfs_rtgroup      *rtg;
        xfs_rgnumber_t          rgno;
+       unsigned int            i;
+       int                     error;
+
+       if (xfs_has_rtgroups(mp)) {
+               error = -libxfs_rtginode_mkdir_parent(mp);
+               if (error)
+                       fail(_("rtgroup directory allocation failed"), error);
+       }
 
        for_each_rtgroup(mp, rgno, rtg) {
-               create_sb_metadata_file(rtg, XFS_RTG_BITMAP,
-                               rtbitmap_create);
-               create_sb_metadata_file(rtg, XFS_RTG_SUMMARY,
-                               rtsummary_create);
+               if (!xfs_has_rtgroups(mp)) {
+                       create_sb_metadata_file(rtg, XFS_RTG_BITMAP,
+                                       rtbitmap_create);
+                       create_sb_metadata_file(rtg, XFS_RTG_SUMMARY,
+                                       rtsummary_create);
+               } else {
+
+                       for (i = 0; i < XFS_RTG_MAX; i++) {
+                               error = -libxfs_rtginode_create(rtg, i, true);
+                               if (error)
+                                       fail(_("rt group inode creation failed"),
+                                                       error);
+                       }
+               }
 
                rtfreesp_init(rtg);
        }
index 0a05f65905d0027e3bdef7b447efd6ac07c61adb..5aae3b84a64c2de1b9f823d0a4f4f4d6a909819e 100644 (file)
@@ -132,6 +132,10 @@ enum {
        R_FILE,
        R_NAME,
        R_NOALIGN,
+       R_RTGROUPS,
+       R_RGCOUNT,
+       R_RGSIZE,
+       R_RTSB,
        R_MAX_OPTS,
 };
 
@@ -727,6 +731,10 @@ static struct opt_params ropts = {
                [R_FILE] = "file",
                [R_NAME] = "name",
                [R_NOALIGN] = "noalign",
+               [R_RTGROUPS] = "rtgroups",
+               [R_RGCOUNT] = "rgcount",
+               [R_RGSIZE] = "rgsize",
+               [R_RTSB] = "rtsb",
                [R_MAX_OPTS] = NULL,
        },
        .subopt_params = {
@@ -766,6 +774,33 @@ static struct opt_params ropts = {
                  .defaultval = 1,
                  .conflicts = { { NULL, LAST_CONFLICT } },
                },
+               { .index = R_RTGROUPS,
+                 .conflicts = { { NULL, LAST_CONFLICT } },
+                 .minval = 0,
+                 .maxval = 1,
+                 .defaultval = 1,
+               },
+               { .index = R_RGCOUNT,
+                 .conflicts = { { &dopts, R_RGSIZE },
+                                { NULL, LAST_CONFLICT } },
+                 .minval = 1,
+                 .maxval = XFS_MAX_RGNUMBER,
+                 .defaultval = SUBOPT_NEEDS_VAL,
+               },
+               { .index = R_RGSIZE,
+                 .conflicts = { { &dopts, R_RGCOUNT },
+                                { NULL, LAST_CONFLICT } },
+                 .convert = true,
+                 .minval = 0,
+                 .maxval = (unsigned long long)XFS_MAX_RGBLOCKS << XFS_MAX_BLOCKSIZE_LOG,
+                 .defaultval = SUBOPT_NEEDS_VAL,
+               },
+               { .index = R_RTSB,
+                 .conflicts = { { NULL, LAST_CONFLICT } },
+                 .minval = 0,
+                 .maxval = 1,
+                 .defaultval = 1,
+               },
        },
 };
 
@@ -927,6 +962,7 @@ struct sb_feat_args {
        bool    nrext64;
        bool    exchrange;              /* XFS_SB_FEAT_INCOMPAT_EXCHRANGE */
        bool    rtgroups;               /* XFS_SB_FEAT_INCOMPAT_RTGROUPS */
+       bool    rtsb;                   /* XFS_SB_FEAT_RO_COMPAT_RTSB */
 };
 
 struct cli_params {
@@ -941,6 +977,7 @@ struct cli_params {
        /* parameters that depend on sector/block size being validated. */
        char    *dsize;
        char    *agsize;
+       char    *rgsize;
        char    *dsu;
        char    *dirblocksize;
        char    *logsize;
@@ -962,6 +999,7 @@ struct cli_params {
 
        /* parameters where 0 is not a valid value */
        int64_t agcount;
+       int64_t rgcount;
        int     inodesize;
        int     inopblock;
        int     imaxpct;
@@ -1018,6 +1056,9 @@ struct mkfs_params {
        uint64_t        agsize;
        uint64_t        agcount;
 
+       uint64_t        rgsize;
+       uint64_t        rgcount;
+
        int             imaxpct;
 
        bool            loginternal;
@@ -1076,7 +1117,8 @@ usage( void )
 /* no-op info only */  [-N]\n\
 /* prototype file */   [-p fname]\n\
 /* quiet */            [-q]\n\
-/* realtime subvol */  [-r extsize=num,size=num,rtdev=xxx]\n\
+/* realtime subvol */  [-r extsize=num,size=num,rtdev=xxx,rtgroups=0|1,\n\
+                           rgcount=n,rgsize=n,rtsb=0|1]\n\
 /* sectorsize */       [-s size=num]\n\
 /* version */          [-V]\n\
                        devicename\n\
@@ -1990,6 +2032,18 @@ rtdev_opts_parser(
        case R_NOALIGN:
                cli->sb_feat.nortalign = getnum(value, opts, subopt);
                break;
+       case R_RTGROUPS:
+               cli->sb_feat.rtgroups = getnum(value, opts, subopt);
+               break;
+       case R_RGCOUNT:
+               cli->rgcount = getnum(value, opts, subopt);
+               break;
+       case R_RGSIZE:
+               cli->rgsize = getstr(value, opts, subopt);
+               break;
+       case R_RTSB:
+               cli->sb_feat.rtsb = getnum(value, opts, subopt);
+               break;
        default:
                return -EINVAL;
        }
@@ -2521,6 +2575,15 @@ _("cowextsize not supported without reflink support\n"));
                usage();
        }
 
+       if (cli->sb_feat.rtgroups && !cli->sb_feat.metadir) {
+               if (cli_opt_set(&mopts, M_METADIR)) {
+                       fprintf(stderr,
+_("realtime groups not supported without metadata directory support\n"));
+                       usage();
+               }
+               cli->sb_feat.metadir = true;
+       }
+
        /*
         * Turn on exchange-range if parent pointers are enabled and the caller
         * did not provide an explicit exchange-range parameter so that users
@@ -2532,6 +2595,40 @@ _("cowextsize not supported without reflink support\n"));
                cli->sb_feat.exchrange = true;
        }
 
+       /*
+        * Exchange-range will be needed for space reorganization on
+        * filesystems with realtime rmap or realtime reflink enabled.
+        */
+       if (cli->sb_feat.rtgroups && !cli->sb_feat.exchrange) {
+               if (cli_opt_set(&iopts, I_EXCHANGE)) {
+                       fprintf(stderr,
+_("realtime groups not supported without exchange-range support\n"));
+                       usage();
+               }
+               cli->sb_feat.exchrange = true;
+       }
+
+       /*
+        * Turn on realtime superblocks if realtime groups are enabled and the
+        * caller did not provide an explicit rtsb parameter so that users
+        * can take advantage of labelled rt volumes.  It's not required for
+        * correct operation, but it costs us nothing to enable it.
+        */
+       if (cli->sb_feat.rtgroups && !cli->sb_feat.rtsb &&
+           !cli_opt_set(&ropts, R_RTSB)) {
+               cli->sb_feat.rtsb = true;
+       }
+
+       /* Realtime superblocks require realtime groups. */
+       if (cli->sb_feat.rtsb && !cli->sb_feat.rtgroups) {
+               if (cli_opt_set(&ropts, R_RTSB)) {
+                       fprintf(stderr,
+_("realtime superblock not supported without realtime group support\n"));
+                       usage();
+               }
+               cli->sb_feat.rtsb = true;
+       }
+
        /*
         * Copy features across to config structure now.
         */
@@ -3211,7 +3308,6 @@ validate_rtdev(
        struct cli_params       *cli)
 {
        struct libxfs_init      *xi = cli->xi;
-       unsigned int            rbmblocksize = cfg->blocksize;
 
        if (!xi->rt.dev) {
                if (cli->rtsize) {
@@ -3255,10 +3351,13 @@ reported by the device (%u).\n"),
 _("cannot have an rt subvolume with zero extents\n"));
                usage();
        }
-       if (cfg->sb_feat.rtgroups)
-               rbmblocksize -= sizeof(struct xfs_rtbuf_blkinfo);
+
+       /*
+        * Note for rtgroup file systems this will be overriden in
+        * calculate_rtgroup_geometry.
+        */
        cfg->rtbmblocks = (xfs_extlen_t)howmany(cfg->rtextents,
-                                               NBBY * rbmblocksize);
+                                               NBBY * cfg->blocksize);
 }
 
 static bool
@@ -3499,6 +3598,189 @@ validate:
                             cfg->agsize, cfg->agcount);
 }
 
+static uint64_t
+calc_rgsize_extsize_nonpower(
+       struct mkfs_params      *cfg)
+{
+       uint64_t                try_rgsize, rgsize, rgcount;
+
+       /*
+        * For non-power-of-two rt extent sizes, round the rtgroup size down to
+        * the nearest extent.
+        */
+       calc_default_rtgroup_geometry(cfg->blocklog, cfg->rtblocks, &rgsize,
+                       &rgcount);
+       rgsize -= rgsize % cfg->rtextblocks;
+       rgsize = min(XFS_MAX_RGBLOCKS, rgsize);
+
+       /*
+        * If we would be left with a too-small rtgroup, increase or decrease
+        * the size of the group until we have a working geometry.
+        */
+       for (try_rgsize = rgsize;
+            try_rgsize <= XFS_MAX_RGBLOCKS - cfg->rtextblocks;
+            try_rgsize += cfg->rtextblocks) {
+               if (cfg->rtblocks % try_rgsize >= (2 * cfg->rtextblocks))
+                       return try_rgsize;
+       }
+       for (try_rgsize = rgsize;
+            try_rgsize > (2 * cfg->rtextblocks);
+            try_rgsize -= cfg->rtextblocks) {
+               if (cfg->rtblocks % try_rgsize >= (2 * cfg->rtextblocks))
+                       return try_rgsize;
+       }
+
+       fprintf(stderr,
+_("realtime group size (%llu) not at all congruent with extent size (%llu)\n"),
+                       (unsigned long long)rgsize,
+                       (unsigned long long)cfg->rtextblocks);
+       usage();
+       return 0;
+}
+
+static uint64_t
+calc_rgsize_extsize_power(
+       struct mkfs_params      *cfg)
+{
+       uint64_t                try_rgsize, rgsize, rgcount;
+       unsigned int            rgsizelog;
+
+       /*
+        * Find the rt group size that is both a power of two and yields at
+        * least as many rt groups as the default geometry specified.
+        */
+       calc_default_rtgroup_geometry(cfg->blocklog, cfg->rtblocks, &rgsize,
+                       &rgcount);
+       rgsizelog = log2_rounddown(rgsize);
+       rgsize = min(XFS_MAX_RGBLOCKS, 1U << rgsizelog);
+
+       /*
+        * If we would be left with a too-small rtgroup, increase or decrease
+        * the size of the group by powers of 2 until we have a working
+        * geometry.  If that doesn't work, try bumping by the extent size.
+        */
+       for (try_rgsize = rgsize;
+            try_rgsize <= XFS_MAX_RGBLOCKS - cfg->rtextblocks;
+            try_rgsize <<= 2) {
+               if (cfg->rtblocks % try_rgsize >= (2 * cfg->rtextblocks))
+                       return try_rgsize;
+       }
+       for (try_rgsize = rgsize;
+            try_rgsize > (2 * cfg->rtextblocks);
+            try_rgsize >>= 2) {
+               if (cfg->rtblocks % try_rgsize >= (2 * cfg->rtextblocks))
+                       return try_rgsize;
+       }
+       for (try_rgsize = rgsize;
+            try_rgsize <= XFS_MAX_RGBLOCKS - cfg->rtextblocks;
+            try_rgsize += cfg->rtextblocks) {
+               if (cfg->rtblocks % try_rgsize >= (2 * cfg->rtextblocks))
+                       return try_rgsize;
+       }
+       for (try_rgsize = rgsize;
+            try_rgsize > (2 * cfg->rtextblocks);
+            try_rgsize -= cfg->rtextblocks) {
+               if (cfg->rtblocks % try_rgsize >= (2 * cfg->rtextblocks))
+                       return try_rgsize;
+       }
+
+       fprintf(stderr,
+_("realtime group size (%llu) not at all congruent with extent size (%llu)\n"),
+                       (unsigned long long)rgsize,
+                       (unsigned long long)cfg->rtextblocks);
+       usage();
+       return 0;
+}
+
+static void
+calculate_rtgroup_geometry(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli)
+{
+       if (!cli->sb_feat.rtgroups) {
+               cfg->rgcount = 0;
+               cfg->rgsize = 0;
+               return;
+       }
+
+       if (cli->rgsize) {      /* User-specified rtgroup size */
+               cfg->rgsize = getnum(cli->rgsize, &ropts, R_RGSIZE);
+
+               /*
+                * Check specified agsize is a multiple of blocksize.
+                */
+               if (cfg->rgsize % cfg->blocksize) {
+                       fprintf(stderr,
+_("rgsize (%s) not a multiple of fs blk size (%d)\n"),
+                               cli->rgsize, cfg->blocksize);
+                       usage();
+               }
+               cfg->rgsize /= cfg->blocksize;
+               cfg->rgcount = cfg->rtblocks / cfg->rgsize +
+                               (cfg->rtblocks % cfg->rgsize != 0);
+
+       } else if (cli->rgcount) {      /* User-specified rtgroup count */
+               cfg->rgcount = cli->rgcount;
+               cfg->rgsize = cfg->rtblocks / cfg->rgcount +
+                               (cfg->rtblocks % cfg->rgcount != 0);
+       } else if (cfg->rtblocks == 0) {
+               /*
+                * If nobody specified a realtime device or the rtgroup size,
+                * try 1TB, rounded down to the nearest rt extent.
+                */
+               cfg->rgsize = TERABYTES(1, cfg->blocklog);
+               cfg->rgsize -= cfg->rgsize % cfg->rtextblocks;
+               cfg->rgcount = 0;
+       } else if (cfg->rtblocks < cfg->rtextblocks * 2) {
+               /* too small even for a single group */
+               cfg->rgsize = cfg->rtblocks;
+               cfg->rgcount = 0;
+       } else if (!is_power_of_2(cfg->rtextblocks)) {
+               cfg->rgsize = calc_rgsize_extsize_nonpower(cfg);
+               cfg->rgcount = cfg->rtblocks / cfg->rgsize +
+                               (cfg->rtblocks % cfg->rgsize != 0);
+       } else {
+               cfg->rgsize = calc_rgsize_extsize_power(cfg);
+               cfg->rgcount = cfg->rtblocks / cfg->rgsize +
+                               (cfg->rtblocks % cfg->rgsize != 0);
+       }
+
+       if (cfg->rgsize > XFS_MAX_RGBLOCKS) {
+               fprintf(stderr,
+_("realtime group size (%llu) must be less than the maximum (%u)\n"),
+                               (unsigned long long)cfg->rgsize,
+                               XFS_MAX_RGBLOCKS);
+               usage();
+       }
+
+       if (cfg->rgsize % cfg->rtextblocks != 0) {
+               fprintf(stderr,
+_("realtime group size (%llu) not a multiple of rt extent size (%llu)\n"),
+                               (unsigned long long)cfg->rgsize,
+                               (unsigned long long)cfg->rtextblocks);
+               usage();
+       }
+
+       if (cfg->rgsize <= cfg->rtextblocks) {
+               fprintf(stderr,
+_("realtime group size (%llu) must be at least two realtime extents\n"),
+                               (unsigned long long)cfg->rgsize);
+               usage();
+       }
+
+       if (cfg->rgcount > XFS_MAX_RGNUMBER) {
+               fprintf(stderr,
+_("realtime group count (%llu) must be less than the maximum (%u)\n"),
+                               (unsigned long long)cfg->rgcount,
+                               XFS_MAX_RGNUMBER);
+               usage();
+       }
+
+       if (cfg->rtextents)
+               cfg->rtbmblocks = howmany(cfg->rgsize / cfg->rtextblocks,
+                       NBBY * (cfg->blocksize - sizeof(struct xfs_rtbuf_blkinfo)));
+}
+
 static void
 calculate_imaxpct(
        struct mkfs_params      *cfg,
@@ -3649,6 +3931,13 @@ sb_set_features(
        }
        if (fp->metadir)
                sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_METADIR;
+       if (fp->rtsb)
+               sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_RTSB;
+       if (fp->rtgroups) {
+               sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_RTGROUPS;
+               sbp->sb_rgcount = cfg->rgcount;
+               sbp->sb_rgextents = cfg->rgsize / cfg->rtextblocks;
+       }
 }
 
 /*
@@ -4042,6 +4331,7 @@ start_superblock_setup(
        sbp->sb_rblocks = cfg->rtblocks;
        sbp->sb_rextsize = cfg->rtextblocks;
        mp->m_features |= libxfs_sb_version_to_features(sbp);
+       mp->m_rgblocks = cfg->rgsize;
 }
 
 static void
@@ -4102,7 +4392,7 @@ finish_superblock_setup(
        sbp->sb_unit = cfg->dsunit;
        sbp->sb_width = cfg->dswidth;
        mp->m_features |= libxfs_sb_version_to_features(sbp);
-
+       mp->m_rgblocks = cfg->rgsize;
 }
 
 /* Prepare an uncached buffer, ready to write something out. */
@@ -4480,6 +4770,39 @@ set_autofsck(
        libxfs_irele(args.dp);
 }
 
+/* Write the realtime superblock */
+static void
+write_rtsb(
+       struct xfs_mount        *mp)
+{
+       struct xfs_buf          *rtsb_bp;
+       struct xfs_buf          *sb_bp = libxfs_getsb(mp);
+       int                     error;
+
+       if (!sb_bp) {
+               fprintf(stderr,
+  _("%s: couldn't grab primary superblock buffer\n"), progname);
+               exit(1);
+       }
+
+       error = -libxfs_buf_get_uncached(mp->m_rtdev_targp,
+                               XFS_FSB_TO_BB(mp, 1), XFS_RTSB_DADDR,
+                               &rtsb_bp);
+       if (error) {
+               fprintf(stderr,
+ _("%s: couldn't grab realtime superblock buffer\n"), progname);
+                       exit(1);
+       }
+
+       rtsb_bp->b_maps[0].bm_bn = XFS_RTSB_DADDR;
+       rtsb_bp->b_ops = &xfs_rtsb_buf_ops;
+
+       libxfs_rtgroup_update_super(rtsb_bp, sb_bp);
+       libxfs_buf_mark_dirty(rtsb_bp);
+       libxfs_buf_relse(rtsb_bp);
+       libxfs_buf_relse(sb_bp);
+}
+
 int
 main(
        int                     argc,
@@ -4696,6 +5019,7 @@ main(
         */
        calculate_initial_ag_geometry(&cfg, &cli, &xi);
        align_ag_geometry(&cfg);
+       calculate_rtgroup_geometry(&cfg, &cli);
 
        calculate_imaxpct(&cfg, &cli);
 
@@ -4796,6 +5120,9 @@ main(
                exit(1);
        }
 
+       if (xfs_has_rtsb(mp) && cfg.rtblocks > 0)
+               write_rtsb(mp);
+
        /*
         * Initialise the freespace freelists (i.e. AGFLs) in each AG.
         */