From: Darrick J. Wong Date: Mon, 5 Aug 2024 18:20:30 +0000 (-0700) Subject: mkfs: format realtime groups X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Fxfs-per-rtg-bitmap-rebase;p=users%2Fhch%2Fxfsprogs.git mkfs: format realtime groups Create filesystems with the realtime group feature enabled. Signed-off-by: Darrick J. Wong --- diff --git a/libfrog/div64.h b/libfrog/div64.h index 673b01cba..4b0d4c3b3 100644 --- a/libfrog/div64.h +++ b/libfrog/div64.h @@ -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_ */ diff --git a/libfrog/util.c b/libfrog/util.c index 46047571a..4e130c884 100644 --- a/libfrog/util.c +++ b/libfrog/util.c @@ -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; +} diff --git a/libfrog/util.h b/libfrog/util.h index 8b4ee7c13..d1c4dd40f 100644 --- a/libfrog/util.h +++ b/libfrog/util.h @@ -9,6 +9,7 @@ #include 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; }) diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index 0ad18a389..e42ecf8c4 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -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 diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h index 0f42f8a90..e141d6ac7 100644 --- a/libxfs/libxfs_priv.h +++ b/libxfs/libxfs_priv.h @@ -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 */ diff --git a/libxfs/topology.c b/libxfs/topology.c index 94adb5be7..8c6affb4c 100644 --- a/libxfs/topology.c +++ b/libxfs/topology.c @@ -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: diff --git a/libxfs/topology.h b/libxfs/topology.h index fa0a23b77..207a8a7f1 100644 --- a/libxfs/topology.h +++ b/libxfs/topology.h @@ -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); diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h index 46621b29d..73a0de2da 100644 --- a/libxfs/xfs_format.h +++ b/libxfs/xfs_format.h @@ -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 diff --git a/man/man8/mkfs.xfs.8.in b/man/man8/mkfs.xfs.8.in index a76c6a9ee..82bd8c81d 100644 --- a/man/man8/mkfs.xfs.8.in +++ b/man/man8/mkfs.xfs.8.in @@ -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 diff --git a/mkfs/lts_4.19.conf b/mkfs/lts_4.19.conf index 303e2cc77..994c0b034 100644 --- a/mkfs/lts_4.19.conf +++ b/mkfs/lts_4.19.conf @@ -18,3 +18,7 @@ exchange=0 [naming] parent=0 + +[realtime] +rtgroups=0 +rtsb=0 diff --git a/mkfs/lts_5.10.conf b/mkfs/lts_5.10.conf index 0894fc3c3..60219ce2d 100644 --- a/mkfs/lts_5.10.conf +++ b/mkfs/lts_5.10.conf @@ -18,3 +18,7 @@ exchange=0 [naming] parent=0 + +[realtime] +rtgroups=0 +rtsb=0 diff --git a/mkfs/lts_5.15.conf b/mkfs/lts_5.15.conf index aea67b509..a1ff3eb07 100644 --- a/mkfs/lts_5.15.conf +++ b/mkfs/lts_5.15.conf @@ -18,3 +18,7 @@ exchange=0 [naming] parent=0 + +[realtime] +rtgroups=0 +rtsb=0 diff --git a/mkfs/lts_5.4.conf b/mkfs/lts_5.4.conf index d1e4075af..78f8a706c 100644 --- a/mkfs/lts_5.4.conf +++ b/mkfs/lts_5.4.conf @@ -18,3 +18,7 @@ exchange=0 [naming] parent=0 + +[realtime] +rtgroups=0 +rtsb=0 diff --git a/mkfs/lts_6.1.conf b/mkfs/lts_6.1.conf index 0d9b0aef7..b878b4e3a 100644 --- a/mkfs/lts_6.1.conf +++ b/mkfs/lts_6.1.conf @@ -18,3 +18,7 @@ exchange=0 [naming] parent=0 + +[realtime] +rtgroups=0 +rtsb=0 diff --git a/mkfs/lts_6.6.conf b/mkfs/lts_6.6.conf index 5b843205b..3e8916651 100644 --- a/mkfs/lts_6.6.conf +++ b/mkfs/lts_6.6.conf @@ -18,3 +18,7 @@ exchange=0 [naming] parent=0 + +[realtime] +rtgroups=0 +rtsb=0 diff --git a/mkfs/proto.c b/mkfs/proto.c index c595d0b89..c6d3402f8 100644 --- a/mkfs/proto.c +++ b/mkfs/proto.c @@ -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); } diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c index 0a05f6590..5aae3b84a 100644 --- a/mkfs/xfs_mkfs.c +++ b/mkfs/xfs_mkfs.c @@ -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. */