]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - mkfs/xfs_mkfs.c
mkfs: factor log size calculations
[thirdparty/xfsprogs-dev.git] / mkfs / xfs_mkfs.c
index 0ab6221dcffde1cf5bb5e66ba9718184a8416fb2..19ffa38bfdb15c97615cea38e2684e8a1b1f38c0 100644 (file)
@@ -981,91 +981,6 @@ check_device_type(
        usage();
 }
 
-static void
-fixup_log_stripe_unit(
-       int             lsflag,
-       int             sunit,
-       xfs_rfsblock_t  *logblocks,
-       int             blocklog)
-{
-       uint64_t        tmp_logblocks;
-
-       /*
-        * Make sure that the log size is a multiple of the stripe unit
-        */
-       if ((*logblocks % sunit) != 0) {
-               if (!lsflag) {
-                       tmp_logblocks = ((*logblocks + (sunit - 1))
-                                               / sunit) * sunit;
-                       /*
-                        * If the log is too large, round down
-                        * instead of round up
-                        */
-                       if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
-                           ((tmp_logblocks << blocklog) > XFS_MAX_LOG_BYTES)) {
-                               tmp_logblocks = (*logblocks / sunit) * sunit;
-                       }
-                       *logblocks = tmp_logblocks;
-               } else {
-                       fprintf(stderr, _("log size %lld is not a multiple "
-                                         "of the log stripe unit %d\n"),
-                               (long long) *logblocks, sunit);
-                       usage();
-               }
-       }
-}
-
-static xfs_fsblock_t
-fixup_internal_log_stripe(
-       xfs_mount_t     *mp,
-       int             lsflag,
-       xfs_fsblock_t   logstart,
-       uint64_t        agsize,
-       int             sunit,
-       xfs_rfsblock_t  *logblocks,
-       int             blocklog,
-       int             *lalign)
-{
-       if ((logstart % sunit) != 0) {
-               logstart = ((logstart + (sunit - 1))/sunit) * sunit;
-               *lalign = 1;
-       }
-
-       fixup_log_stripe_unit(lsflag, sunit, logblocks, blocklog);
-
-       if (*logblocks > agsize - XFS_FSB_TO_AGBNO(mp, logstart)) {
-               fprintf(stderr,
-                       _("Due to stripe alignment, the internal log size "
-                       "(%lld) is too large.\n"), (long long) *logblocks);
-               fprintf(stderr, _("Must fit within an allocation group.\n"));
-               usage();
-       }
-       return logstart;
-}
-
-void
-validate_log_size(uint64_t logblocks, int blocklog, int min_logblocks)
-{
-       if (logblocks < min_logblocks) {
-               fprintf(stderr,
-       _("log size %lld blocks too small, minimum size is %d blocks\n"),
-                       (long long)logblocks, min_logblocks);
-               usage();
-       }
-       if (logblocks > XFS_MAX_LOG_BLOCKS) {
-               fprintf(stderr,
-       _("log size %lld blocks too large, maximum size is %lld blocks\n"),
-                       (long long)logblocks, XFS_MAX_LOG_BLOCKS);
-               usage();
-       }
-       if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES) {
-               fprintf(stderr,
-       _("log size %lld bytes too large, maximum size is %lld bytes\n"),
-                       (long long)(logblocks << blocklog), XFS_MAX_LOG_BYTES);
-               usage();
-       }
-}
-
 static void
 validate_ag_geometry(
        int             blocklog,
@@ -2909,6 +2824,216 @@ sb_set_features(
 
 }
 
+/*
+ * Make sure that the log size is a multiple of the stripe unit
+ */
+static void
+align_log_size(
+       struct mkfs_params      *cfg,
+       int                     sunit)
+{
+       uint64_t        tmp_logblocks;
+
+       /* nothing to do if it's already aligned. */
+       if ((cfg->logblocks % sunit) == 0)
+               return;
+
+       if (cli_opt_set(&lopts, L_SIZE)) {
+               fprintf(stderr,
+_("log size %lld is not a multiple of the log stripe unit %d\n"),
+                       (long long) cfg->logblocks, sunit);
+               usage();
+       }
+
+       tmp_logblocks = ((cfg->logblocks + (sunit - 1)) / sunit) * sunit;
+
+       /* If the log is too large, round down instead of round up */
+       if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
+           ((tmp_logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES)) {
+               tmp_logblocks = (cfg->logblocks / sunit) * sunit;
+       }
+       cfg->logblocks = tmp_logblocks;
+}
+
+/*
+ * Make sure that the internal log is correctly aligned to the specified
+ * stripe unit.
+ */
+static void
+align_internal_log(
+       struct mkfs_params      *cfg,
+       struct xfs_mount        *mp,
+       int                     sunit)
+{
+       /* round up log start if necessary */
+       if ((cfg->logstart % sunit) != 0)
+               cfg->logstart = ((cfg->logstart + (sunit - 1)) / sunit) * sunit;
+
+       /* round up/down the log size now */
+       align_log_size(cfg, sunit);
+
+       /* check the aligned log still fits in an AG. */
+       if (cfg->logblocks > cfg->agsize - XFS_FSB_TO_AGBNO(mp, cfg->logstart)) {
+               fprintf(stderr,
+_("Due to stripe alignment, the internal log size (%lld) is too large.\n"
+  "Must fit within an allocation group.\n"),
+                       (long long) cfg->logblocks);
+               usage();
+       }
+}
+
+void
+validate_log_size(uint64_t logblocks, int blocklog, int min_logblocks)
+{
+       if (logblocks < min_logblocks) {
+               fprintf(stderr,
+       _("log size %lld blocks too small, minimum size is %d blocks\n"),
+                       (long long)logblocks, min_logblocks);
+               usage();
+       }
+       if (logblocks > XFS_MAX_LOG_BLOCKS) {
+               fprintf(stderr,
+       _("log size %lld blocks too large, maximum size is %lld blocks\n"),
+                       (long long)logblocks, XFS_MAX_LOG_BLOCKS);
+               usage();
+       }
+       if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES) {
+               fprintf(stderr,
+       _("log size %lld bytes too large, maximum size is %lld bytes\n"),
+                       (long long)(logblocks << blocklog), XFS_MAX_LOG_BYTES);
+               usage();
+       }
+}
+
+static void
+calculate_log_size(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct xfs_mount        *mp)
+{
+       struct sb_feat_args     *fp = &cfg->sb_feat;
+       struct xfs_sb           *sbp = &mp->m_sb;
+       int                     min_logblocks;
+
+       min_logblocks = max_trans_res(sbp->sb_agblocks, fp->crcs_enabled,
+                                     fp->dir_version, cfg->sectorlog,
+                                     cfg->blocklog, cfg->inodelog,
+                                     cfg->dirblocklog, fp->log_version,
+                                     cfg->lsunit, fp->finobt, fp->rmapbt,
+                                     fp->reflink, fp->inode_align);
+
+       ASSERT(min_logblocks);
+       min_logblocks = MAX(XFS_MIN_LOG_BLOCKS, min_logblocks);
+
+       /* if we have lots of blocks, check against XFS_MIN_LOG_BYTES, too */
+       if (!cli->logsize &&
+           cfg->dblocks >= (1024*1024*1024) >> cfg->blocklog)
+               min_logblocks = MAX(min_logblocks,
+                                   XFS_MIN_LOG_BYTES >> cfg->blocklog);
+
+       /*
+        * external logs will have a device and size by now, so all we have
+        * to do is validate it against minimum size and align it.
+        */
+       if (!cfg->loginternal) {
+               if (min_logblocks > cfg->logblocks) {
+                       fprintf(stderr,
+_("external log device %lld too small, must be at least %lld blocks\n"),
+                               (long long)cfg->logblocks,
+                               (long long)min_logblocks);
+                       usage();
+               }
+               cfg->logstart = 0;
+               cfg->logagno = 0;
+               if (cfg->lsunit)
+                       align_log_size(cfg, cfg->lsunit);
+
+               validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+               return;
+       }
+
+       /* internal log - if no size specified, calculate automatically */
+       if (!cfg->logblocks) {
+               if (cfg->dblocks < GIGABYTES(1, cfg->blocklog)) {
+                       /* tiny filesystems get minimum sized logs. */
+                       cfg->logblocks = min_logblocks;
+               } else if (cfg->dblocks < GIGABYTES(16, cfg->blocklog)) {
+
+                       /*
+                        * For small filesystems, we want to use the
+                        * XFS_MIN_LOG_BYTES for filesystems smaller than 16G if
+                        * at all possible, ramping up to 128MB at 256GB.
+                        */
+                       cfg->logblocks = MIN(XFS_MIN_LOG_BYTES >> cfg->blocklog,
+                                       min_logblocks * XFS_DFL_LOG_FACTOR);
+               } else {
+                       /*
+                        * With a 2GB max log size, default to maximum size
+                        * at 4TB. This keeps the same ratio from the older
+                        * max log size of 128M at 256GB fs size. IOWs,
+                        * the ratio of fs size to log size is 2048:1.
+                        */
+                       cfg->logblocks = (cfg->dblocks << cfg->blocklog) / 2048;
+                       cfg->logblocks = cfg->logblocks >> cfg->blocklog;
+               }
+
+               /* Ensure the chosen size meets minimum log size requirements */
+               cfg->logblocks = MAX(min_logblocks, cfg->logblocks);
+
+               /*
+                * Make sure the log fits wholly within an AG
+                *
+                * XXX: If agf->freeblks ends up as 0 because the log uses all
+                * the free space, it causes the kernel all sorts of problems
+                * with per-ag reservations. Right now just back it off one
+                * block, but there's a whole can of worms here that needs to be
+                * opened to decide what is the valid maximum size of a log in
+                * an AG.
+                */
+               cfg->logblocks = MIN(cfg->logblocks,
+                                    libxfs_alloc_ag_max_usable(mp) - 1);
+
+               /* and now clamp the size to the maximum supported size */
+               cfg->logblocks = MIN(cfg->logblocks, XFS_MAX_LOG_BLOCKS);
+               if ((cfg->logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES)
+                       cfg->logblocks = XFS_MAX_LOG_BYTES >> cfg->blocklog;
+
+               validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+       }
+
+       if (cfg->logblocks > sbp->sb_agblocks - libxfs_prealloc_blocks(mp)) {
+               fprintf(stderr,
+_("internal log size %lld too large, must fit in allocation group\n"),
+                       (long long)cfg->logblocks);
+               usage();
+       }
+
+       if (cli_opt_set(&lopts, L_AGNUM)) {
+               if (cli->logagno >= sbp->sb_agcount) {
+                       fprintf(stderr,
+_("log ag number %lld too large, must be less than %lld\n"),
+                               (long long)cli->logagno,
+                               (long long)sbp->sb_agcount);
+                       usage();
+               }
+               cfg->logagno = cli->logagno;
+       } else
+               cfg->logagno = (xfs_agnumber_t)(sbp->sb_agcount / 2);
+
+       cfg->logstart = XFS_AGB_TO_FSB(mp, cfg->logagno,
+                                      libxfs_prealloc_blocks(mp));
+
+       /*
+        * Align the logstart at stripe unit boundary.
+        */
+       if (cfg->lsunit) {
+               align_internal_log(cfg, mp, cfg->lsunit);
+       } else if (cfg->dsunit) {
+               align_internal_log(cfg, mp, cfg->dsunit);
+       }
+       validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
+}
+
 /*
  * Set up mount and superblock with the minimum parameters required for
  * the libxfs macros needed by the log sizing code to run successfully.
@@ -3569,19 +3694,14 @@ main(
        int                     inopblock;
        int                     isize;
        char                    *label = NULL;
-       int                     laflag;
-       int                     lalign;
        xfs_agnumber_t          logagno;
        xfs_rfsblock_t          logblocks;
        char                    *logfile;
        int                     loginternal;
-       char                    *logsize;
        xfs_fsblock_t           logstart;
-       int                     lsflag;
        int                     lsectorlog;
        int                     lsectorsize;
        int                     lsunit;
-       int                     min_logblocks;
        xfs_extlen_t            nbmblocks;
        int                     dry_run = 0;
        int                     discard = 1;
@@ -3660,13 +3780,12 @@ main(
        cli.loginternal = 1;    /* internal by default */
 
        agsize = dblocks = 0;
-       laflag = lsflag = 0;
        loginternal = 1;
        logagno = logblocks = rtblocks = rtextblocks = 0;
        imaxpct = inodelog = inopblock = isize = 0;
        dfile = logfile = rtfile = NULL;
-       logsize = protofile = NULL;
-       dsunit = dswidth = lalign = lsunit = 0;
+       protofile = NULL;
+       dsunit = dswidth = lsunit = 0;
        force_overwrite = 0;
        worst_freelist = 0;
        memset(&fsx, 0, sizeof(fsx));
@@ -3683,6 +3802,7 @@ main(
                        break;
                case 'b':
                case 'i':
+               case 'l':
                case 'n':
                case 'r':
                case 's':
@@ -3697,14 +3817,6 @@ main(
                        fsx.fsx_extsize = cli.fsx.fsx_extsize;
                        /* end temp don't break code */
                        break;
-               case 'l':
-                       parse_subopts(c, optarg, &cli);
-
-                       /* temp don't break code */
-                       logagno = cli.logagno;
-                       laflag = cli_opt_set(&lopts, L_AGNUM);
-                       /* end temp don't break code */
-                       break;
                case 'L':
                        if (strlen(optarg) > sizeof(sbp->sb_fname))
                                illegal(optarg, "L");
@@ -3806,6 +3918,14 @@ main(
         */
        initialise_mount(&cfg, mp, sbp);
 
+       /*
+        * With the mount set up, we can finally calculate the log size
+        * constraints and do default size calculations and final validation
+        */
+       calculate_log_size(&cfg, &cli, mp);
+
+       protostring = setup_proto(protofile);
+
        /* temp don't break code */
        sectorsize = cfg.sectorsize;
        sectorlog = cfg.sectorlog;
@@ -3832,110 +3952,10 @@ main(
        agsize = cfg.agsize;
        agcount = cfg.agcount;
        imaxpct = cfg.imaxpct;
+       logagno = cfg.logagno;
+       logstart = cfg.logstart;
        /* end temp don't break code */
 
-       min_logblocks = max_trans_res(agsize,
-                                  sb_feat.crcs_enabled, sb_feat.dir_version,
-                                  sectorlog, blocklog, inodelog, dirblocklog,
-                                  sb_feat.log_version, lsunit, sb_feat.finobt,
-                                  sb_feat.rmapbt, sb_feat.reflink,
-                                  sb_feat.inode_align);
-       ASSERT(min_logblocks);
-       min_logblocks = MAX(XFS_MIN_LOG_BLOCKS, min_logblocks);
-       if (!logsize && dblocks >= (1024*1024*1024) >> blocklog)
-               min_logblocks = MAX(min_logblocks, XFS_MIN_LOG_BYTES>>blocklog);
-       if (loginternal && !logsize) {
-
-               if (dblocks < GIGABYTES(1, blocklog)) {
-                       /* tiny filesystems get minimum sized logs. */
-                       logblocks = min_logblocks;
-               } else if (dblocks < GIGABYTES(16, blocklog)) {
-
-                       /*
-                        * For small filesystems, we want to use the
-                        * XFS_MIN_LOG_BYTES for filesystems smaller than 16G if
-                        * at all possible, ramping up to 128MB at 256GB.
-                        */
-                       logblocks = MIN(XFS_MIN_LOG_BYTES >> blocklog,
-                                       min_logblocks * XFS_DFL_LOG_FACTOR);
-               } else {
-                       /*
-                        * With a 2GB max log size, default to maximum size
-                        * at 4TB. This keeps the same ratio from the older
-                        * max log size of 128M at 256GB fs size. IOWs,
-                        * the ratio of fs size to log size is 2048:1.
-                        */
-                       logblocks = (dblocks << blocklog) / 2048;
-                       logblocks = logblocks >> blocklog;
-               }
-
-               /* Ensure the chosen size meets minimum log size requirements */
-               logblocks = MAX(min_logblocks, logblocks);
-
-               /* make sure the log fits wholly within an AG */
-               if (logblocks >= agsize)
-                       logblocks = min_logblocks;
-
-               /* and now clamp the size to the maximum supported size */
-               logblocks = MIN(logblocks, XFS_MAX_LOG_BLOCKS);
-               if ((logblocks << blocklog) > XFS_MAX_LOG_BYTES)
-                       logblocks = XFS_MAX_LOG_BYTES >> blocklog;
-
-       }
-       validate_log_size(logblocks, blocklog, min_logblocks);
-
-       protostring = setup_proto(protofile);
-
-       if (loginternal) {
-               /*
-                * Readjust the log size to fit within an AG if it was sized
-                * automatically.
-                */
-               if (!logsize) {
-                       logblocks = MIN(logblocks,
-                                       libxfs_alloc_ag_max_usable(mp));
-
-                       /* revalidate the log size is valid if we changed it */
-                       validate_log_size(logblocks, blocklog, min_logblocks);
-               }
-               if (logblocks > agsize - libxfs_prealloc_blocks(mp)) {
-                       fprintf(stderr,
-       _("internal log size %lld too large, must fit in allocation group\n"),
-                               (long long)logblocks);
-                       usage();
-               }
-
-               if (laflag) {
-                       if (logagno >= agcount) {
-                               fprintf(stderr,
-               _("log ag number %d too large, must be less than %lld\n"),
-                                       logagno, (long long)agcount);
-                               usage();
-                       }
-               } else
-                       logagno = (xfs_agnumber_t)(agcount / 2);
-
-               logstart = XFS_AGB_TO_FSB(mp, logagno, libxfs_prealloc_blocks(mp));
-               /*
-                * Align the logstart at stripe unit boundary.
-                */
-               if (lsunit) {
-                       logstart = fixup_internal_log_stripe(mp,
-                                       lsflag, logstart, agsize, lsunit,
-                                       &logblocks, blocklog, &lalign);
-               } else if (dsunit) {
-                       logstart = fixup_internal_log_stripe(mp,
-                                       lsflag, logstart, agsize, dsunit,
-                                       &logblocks, blocklog, &lalign);
-               }
-       } else {
-               logstart = 0;
-               if (lsunit)
-                       fixup_log_stripe_unit(lsflag, lsunit,
-                                       &logblocks, blocklog);
-       }
-       validate_log_size(logblocks, blocklog, min_logblocks);
-
        /* Temp support code  to set up mkfs cfg parameters */
        cfg.blocksize = blocksize;
        cfg.blocklog = blocklog;