git: 4623bbfe7bfc - main - nvmecontrol: select format by sector size

From: Warner Losh <imp_at_FreeBSD.org>
Date: Mon, 19 Oct 2026 06:24:40 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=4623bbfe7bfc81d2626a5c2a7a5cfb2ff15c0360

commit 4623bbfe7bfc81d2626a5c2a7a5cfb2ff15c0360
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-10-19 04:42:12 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-10-19 06:22:51 +0000

    nvmecontrol: select format by sector size
    
    Add automatic selection of lbaf value based on a single sector size. The
    lbas (the data sector size) is selected based on the power of 2 in the
    sector size, and the metadata size is selected by the remainder. If an
    lbaf matches these values is used, otherwise we exit with an error.
    When there's a tie, we prefer the higher performance.
    
    Manual selection of these values remains as well for times when we
    select incorrectly.
    
    Reviewed by:            seuros, chuck
    Differential Revision:  https://reviews.freebsd.org/D59833
---
 sbin/nvmecontrol/format.c      | 43 ++++++++++++++++++++++++++++++++++++++++--
 sbin/nvmecontrol/nvmecontrol.8 | 11 +++++++++++
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/sbin/nvmecontrol/format.c b/sbin/nvmecontrol/format.c
index f339acc6e305..53f2f0a1bb0a 100644
--- a/sbin/nvmecontrol/format.c
+++ b/sbin/nvmecontrol/format.c
@@ -55,6 +55,7 @@ static struct options {
 	uint32_t	ms;
 	uint32_t	pi;
 	uint32_t	pil;
+	uint32_t	sector_size;
 	uint32_t	ses;
 	bool		Eflag;
 	bool		Cflag;
@@ -64,6 +65,7 @@ static struct options {
 	.ms = NONE,
 	.pi = NONE,
 	.pil = NONE,
+	.sector_size = NONE,
 	.ses = SES_NONE,
 	.Eflag = false,
 	.Cflag = false,
@@ -84,7 +86,9 @@ static const struct opts format_opts[] = {
 	    "Protective information"),
 	OPT("pil", 'l', arg_uint32, opt, pil,
 	    "Protective information location"),
-	OPT("ses", 's', arg_uint32, opt, ses,
+	OPT("sector-size", 's', arg_uint32, opt, sector_size,
+	    "Sector size"),
+	OPT("ses", 0, arg_uint32, opt, ses,
 	    "Secure erase settings"),
 	{ NULL, 0, arg_none, NULL, NULL }
 };
@@ -118,18 +122,25 @@ format(const struct cmd *f, int argc, char *argv[])
 	const char			*target;
 	uint32_t			nsid;
 	int				lbaf, ms, pi, pil, ses, fd;
+	int				best_rp, i, lbads;
+	uint32_t			sector_size;
 
 	if (arg_parse(argc, argv, f))
 		return;
 
 	if ((int)opt.Eflag + opt.Cflag + (opt.ses != SES_NONE) > 1) {
 		fprintf(stderr,
-		    "Only one of -E, -C or -s may be specified\n");
+		    "Only one of -E, -C or --ses may be specified\n");
 		arg_help(argc, argv, f);
 	}
 
 	target = opt.dev;
 	lbaf = opt.lbaf;
+	sector_size = opt.sector_size;
+	if (lbaf >= 0 && sector_size != NONE) {
+		fprintf(stderr, "Only one of -f or -s may be specified\n");
+		arg_help(argc, argv, f);
+	}
 	ms = opt.ms;
 	pi = opt.pi;
 	pil = opt.pil;
@@ -155,6 +166,8 @@ format(const struct cmd *f, int argc, char *argv[])
 		open_dev(path, &fd, 1, 1);
 	}
 	free(path);
+	if (sector_size != NONE && nsid == NVME_GLOBAL_NAMESPACE_TAG)
+		errx(EX_USAGE, "sector size requires a namespace target");
 
 	/* Check that controller can execute this command. */
 	if (read_controller_data(fd, &cd))
@@ -176,6 +189,32 @@ format(const struct cmd *f, int argc, char *argv[])
 		/* Try to keep previous namespace parameters. */
 		if (read_namespace_data(fd, nsid, &nsd))
 			errx(EX_IOERR, "Identify request failed");
+		if (sector_size != NONE) {
+			if (sector_size == 0)
+				errx(EX_USAGE, "sector size must be non-zero");
+			lbads = fls(sector_size) - 1;
+			lbaf = -1;
+			best_rp = 4;
+			for (i = 0; i <= nsd.nlbaf; i++) {
+				uint32_t format;
+				int rp;
+
+				format = nsd.lbaf[i];
+				if (NVMEV(NVME_NS_DATA_LBAF_LBADS, format) != lbads ||
+				    NVMEV(NVME_NS_DATA_LBAF_MS, format) !=
+				    sector_size % (1U << lbads))
+					continue;
+				rp = NVMEV(NVME_NS_DATA_LBAF_RP, format);
+				if (rp < best_rp) {
+					lbaf = i;
+					best_rp = rp;
+				}
+			}
+			if (lbaf < 0)
+				errx(EX_USAGE,
+				    "namespace does not support sector size %u",
+				    sector_size);
+		}
 		if (lbaf < 0)
 			lbaf = nvme_ns_data_format_index(&nsd);
 		if (lbaf > nsd.nlbaf)
diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8
index 97e995967269..c80dac444774 100644
--- a/sbin/nvmecontrol/nvmecontrol.8
+++ b/sbin/nvmecontrol/nvmecontrol.8
@@ -154,6 +154,7 @@
 .Nm
 .Ic format
 .Op Fl f Ar fmt
+.Op Fl s Ar sector-size
 .Op Fl m Ar mset
 .Op Fl p Ar pi
 .Op Fl l Ar pil
@@ -511,6 +512,16 @@ of the parameters to use.
 LBA Format #, as specified in the identification of the namespace using
 .Dq nvmecontrol identify
 command with a namespace specified maps this index into these parameters.
+.It Fl s Ar sector-size
+Select the LBA format matching the specified total sector size.
+The data size is the largest power of two not greater than
+.Ar sector-size ,
+and any remainder is the metadata size.
+If multiple LBA formats match, the one with the best relative performance is
+selected, with the lowest format index used to break ties.
+This option may only be used when formatting a namespace and is mutually
+exclusive with
+.Fl f .
 .It Fl m Ar mset
 Metadata Setting.
 .Ar mset