mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
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
This commit is contained in:
1 parent
4623bbfe7b
commit
b991e2dd82
2 files changed
+113
-27
No files matched your search
+102
-24
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user