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;
}
}
/*
- * 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(
{
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;
/*
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;
}
}
{
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);
}
R_FILE,
R_NAME,
R_NOALIGN,
+ R_RTGROUPS,
+ R_RGCOUNT,
+ R_RGSIZE,
+ R_RTSB,
R_MAX_OPTS,
};
[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 = {
.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,
+ },
},
};
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 {
/* parameters that depend on sector/block size being validated. */
char *dsize;
char *agsize;
+ char *rgsize;
char *dsu;
char *dirblocksize;
char *logsize;
/* parameters where 0 is not a valid value */
int64_t agcount;
+ int64_t rgcount;
int inodesize;
int inopblock;
int imaxpct;
uint64_t agsize;
uint64_t agcount;
+ uint64_t rgsize;
+ uint64_t rgcount;
+
int imaxpct;
bool loginternal;
/* 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\
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;
}
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
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.
*/
struct cli_params *cli)
{
struct libxfs_init *xi = cli->xi;
- unsigned int rbmblocksize = cfg->blocksize;
if (!xi->rt.dev) {
if (cli->rtsize) {
_("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
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,
}
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;
+ }
}
/*
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
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. */
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,
*/
calculate_initial_ag_geometry(&cfg, &cli, &xi);
align_ag_geometry(&cfg);
+ calculate_rtgroup_geometry(&cfg, &cli);
calculate_imaxpct(&cfg, &cli);
exit(1);
}
+ if (xfs_has_rtsb(mp) && cfg.rtblocks > 0)
+ write_rtsb(mp);
+
/*
* Initialise the freespace freelists (i.e. AGFLs) in each AG.
*/