git: b991e2dd824c - main - nvmecontrol: support controller-wide sector sizes
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 19 Oct 2026 06:24:41 UTC
The branch main has been updated by imp:
URL: https://cgit.FreeBSD.org/src/commit/?id=b991e2dd824cc6a120a346a183837875ff75b66d
commit b991e2dd824cc6a120a346a183837875ff75b66d
Author: Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-10-19 04:42:43 +0000
Commit: Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-10-19 06:22:51 +0000
nvmecontrol: support controller-wide sector sizes
Extend sector-size selection to controller-wide formats when every
namespace has one compatible format index and matching effective
metadata and protection settings. Reject ambiguous or inconsistent
layouts so heterogeneous fleets can request a sector size without
risking an unintended controller-wide format.
Reviewed by: seuros, chuck
Differential Revision: https://reviews.freebsd.org/D59834
---
sbin/nvmecontrol/format.c | 126 +++++++++++++++++++++++++++++++++--------
sbin/nvmecontrol/nvmecontrol.8 | 14 ++++-
2 files changed, 113 insertions(+), 27 deletions(-)
diff --git a/sbin/nvmecontrol/format.c b/sbin/nvmecontrol/format.c
index 53f2f0a1bb0a..ee0ecc34aeaa 100644
--- a/sbin/nvmecontrol/format.c
+++ b/sbin/nvmecontrol/format.c
@@ -112,18 +112,85 @@ CMD_COMMAND(format_cmd);
/* End of tables for command line parsing */
+static int
+find_lbaf(const struct nvme_namespace_data *nsd, uint32_t sector_size,
+ bool require_unique)
+{
+ uint32_t format;
+ int best, best_rp, i, lbads, matches, rp;
+
+ lbads = fls(sector_size) - 1;
+ best = -1;
+ best_rp = 4;
+ matches = 0;
+ for (i = 0; i <= nsd->nlbaf; i++) {
+ 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;
+ matches++;
+ rp = NVMEV(NVME_NS_DATA_LBAF_RP, format);
+ if (rp < best_rp) {
+ best = i;
+ best_rp = rp;
+ }
+ }
+ if (require_unique && matches > 1)
+ return (-2);
+ return (best);
+}
+
+static void
+check_controller_namespace(int fd, uint32_t nsid, uint32_t sector_size,
+ int *lbaf, int *ms, int *pi, int *pil, bool *found)
+{
+ struct nvme_namespace_data nsd;
+ int fmt, ns_ms, ns_pi, ns_pil;
+
+ if (read_namespace_data(fd, nsid, &nsd))
+ errx(EX_IOERR, "Identify request failed for namespace %u", nsid);
+ fmt = find_lbaf(&nsd, sector_size, true);
+ if (fmt == -2)
+ errx(EX_USAGE,
+ "namespace %u has multiple formats for sector size %u",
+ nsid, sector_size);
+ if (fmt < 0)
+ errx(EX_USAGE, "namespace %u does not support sector size %u",
+ nsid, sector_size);
+ ns_ms = opt.ms == NONE ?
+ NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsd.flbas) : opt.ms;
+ ns_pi = opt.pi == NONE ?
+ NVMEV(NVME_NS_DATA_DPS_MD_START, nsd.dps) : opt.pi;
+ ns_pil = opt.pil == NONE ?
+ NVMEV(NVME_NS_DATA_DPS_PIT, nsd.dps) : opt.pil;
+ if (!*found) {
+ *lbaf = fmt;
+ *ms = ns_ms;
+ *pi = ns_pi;
+ *pil = ns_pil;
+ *found = true;
+ } else if (*lbaf != fmt || *ms != ns_ms || *pi != ns_pi ||
+ *pil != ns_pil) {
+ errx(EX_USAGE,
+ "namespace %u has incompatible format parameters", nsid);
+ }
+}
+
static void
format(const struct cmd *f, int argc, char *argv[])
{
struct nvme_controller_data cd;
struct nvme_namespace_data nsd;
+ struct nvme_ns_list nslist;
struct nvme_pt_command pt;
char *path;
const char *target;
- uint32_t nsid;
+ uint32_t nsid, scan_nsid;
int lbaf, ms, pi, pil, ses, fd;
- int best_rp, i, lbads;
+ u_int i;
uint32_t sector_size;
+ bool done, found;
if (arg_parse(argc, argv, f))
return;
@@ -141,6 +208,8 @@ format(const struct cmd *f, int argc, char *argv[])
fprintf(stderr, "Only one of -f or -s may be specified\n");
arg_help(argc, argv, f);
}
+ if (sector_size == 0)
+ errx(EX_USAGE, "sector size must be non-zero");
ms = opt.ms;
pi = opt.pi;
pil = opt.pil;
@@ -166,8 +235,6 @@ 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))
@@ -190,26 +257,7 @@ format(const struct cmd *f, int argc, char *argv[])
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;
- }
- }
+ lbaf = find_lbaf(&nsd, sector_size, false);
if (lbaf < 0)
errx(EX_USAGE,
"namespace does not support sector size %u",
@@ -228,6 +276,36 @@ format(const struct cmd *f, int argc, char *argv[])
} else {
/* We have no previous parameters, so default to zeroes. */
+ if (sector_size != NONE) {
+ found = false;
+ if (cd.ver == 0) {
+ for (scan_nsid = 1; scan_nsid <= cd.nn; scan_nsid++)
+ check_controller_namespace(fd, scan_nsid,
+ sector_size, &lbaf, &ms, &pi, &pil,
+ &found);
+ } else {
+ scan_nsid = 0;
+ done = false;
+ while (!done) {
+ if (read_active_namespaces(fd, scan_nsid,
+ &nslist))
+ errx(EX_IOERR,
+ "Identify request failed");
+ for (i = 0; i < nitems(nslist.ns); i++) {
+ scan_nsid = nslist.ns[i];
+ if (scan_nsid == 0) {
+ done = true;
+ break;
+ }
+ check_controller_namespace(fd, scan_nsid,
+ sector_size, &lbaf, &ms, &pi, &pil,
+ &found);
+ }
+ }
+ }
+ if (!found)
+ errx(EX_USAGE, "controller has no active namespaces");
+ }
if (lbaf < 0)
lbaf = 0;
if (ms < 0)
diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8
index c80dac444774..793016652e73 100644
--- a/sbin/nvmecontrol/nvmecontrol.8
+++ b/sbin/nvmecontrol/nvmecontrol.8
@@ -517,10 +517,18 @@ 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
+When formatting a namespace, the format 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
+When formatting a controller, every active namespace must have exactly one
+matching format, and that format must have the same index in every namespace.
+The effective
+.Ar mset ,
+.Ar pi ,
+and
+.Ar pil
+values must also match across all active namespaces.
+Otherwise, the operation is rejected.
+This option is mutually exclusive with
.Fl f .
.It Fl m Ar mset
Metadata Setting.