git: 2c41f7d8a034 - main - nvme: honor FLBAS Format Index Upper when selecting the LBA format

From: Abdelkader Boudih <seuros_at_FreeBSD.org>
Date: Thu, 24 Sep 2026 14:20:33 UTC
The branch main has been updated by seuros:

URL: https://cgit.FreeBSD.org/src/commit/?id=2c41f7d8a034ee64a0aaa69a1aa5cb07748a1a32

commit 2c41f7d8a034ee64a0aaa69a1aa5cb07748a1a32
Author:     Abdelkader Boudih <seuros@FreeBSD.org>
AuthorDate: 2026-09-24 14:19:51 +0000
Commit:     Abdelkader Boudih <seuros@FreeBSD.org>
CommitDate: 2026-09-24 14:19:51 +0000

    nvme: honor FLBAS Format Index Upper when selecting the LBA format
    
    Added nvme_ns_data_format_index() in the nvme, nda, and nvmf
    host paths as well as nvmecontrol and camdd.
    
    Reviewed by:    imp, adrian
    Differential Revision:  https://reviews.freebsd.org/D59627
---
 sbin/nvmecontrol/devlist.c  |  2 +-
 sbin/nvmecontrol/format.c   |  2 +-
 sbin/nvmecontrol/identify.c |  2 +-
 sys/cam/nvme/nvme_da.c      |  2 +-
 sys/dev/nvme/nvme.h         | 18 ++++++++++++++++++
 sys/dev/nvme/nvme_ns.c      |  4 ++--
 sys/dev/nvmf/host/nvmf_ns.c |  4 ++--
 usr.sbin/camdd/camdd.c      |  2 +-
 8 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/sbin/nvmecontrol/devlist.c b/sbin/nvmecontrol/devlist.c
index ff492591197c..9fa8d27014e6 100644
--- a/sbin/nvmecontrol/devlist.c
+++ b/sbin/nvmecontrol/devlist.c
@@ -86,7 +86,7 @@ ns_get_sector_size(struct nvme_namespace_data *nsdata)
 {
 	uint8_t flbas_fmt, lbads;
 
-	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas);
+	flbas_fmt = nvme_ns_data_format_index(nsdata);
 	lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsdata->lbaf[flbas_fmt]);
 
 	return (1 << lbads);
diff --git a/sbin/nvmecontrol/format.c b/sbin/nvmecontrol/format.c
index 16699605ea8c..f339acc6e305 100644
--- a/sbin/nvmecontrol/format.c
+++ b/sbin/nvmecontrol/format.c
@@ -177,7 +177,7 @@ format(const struct cmd *f, int argc, char *argv[])
 		if (read_namespace_data(fd, nsid, &nsd))
 			errx(EX_IOERR, "Identify request failed");
 		if (lbaf < 0)
-			lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsd.flbas);
+			lbaf = nvme_ns_data_format_index(&nsd);
 		if (lbaf > nsd.nlbaf)
 			errx(EX_USAGE, "LBA format is out of range");
 		if (ms < 0)
diff --git a/sbin/nvmecontrol/identify.c b/sbin/nvmecontrol/identify.c
index 98a3141bf9ad..be5a343806ff 100644
--- a/sbin/nvmecontrol/identify.c
+++ b/sbin/nvmecontrol/identify.c
@@ -68,7 +68,7 @@ print_namespace(struct nvme_namespace_data *nsdata)
 
 	thin_prov = NVMEV(NVME_NS_DATA_NSFEAT_THIN_PROV, nsdata->nsfeat);
 
-	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas);
+	flbas_fmt = nvme_ns_data_format_index(nsdata);
 
 	printf("Size:                        %lld blocks\n",
 	    (long long)nsdata->nsze);
diff --git a/sys/cam/nvme/nvme_da.c b/sys/cam/nvme/nvme_da.c
index 31bb7c4b7d3c..e1831331f8f3 100644
--- a/sys/cam/nvme/nvme_da.c
+++ b/sys/cam/nvme/nvme_da.c
@@ -310,7 +310,7 @@ ndasetgeom(struct nda_softc *softc, struct cam_periph *periph)
 	 */
 	flags = disk->d_flags & DISKFLAG_UNMAPPED_BIO;	/* Need to preserve */
 
-	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsd->flbas);
+	flbas_fmt = nvme_ns_data_format_index(nsd);
 	lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsd->lbaf[flbas_fmt]);
 	disk->d_sectorsize = 1 << lbads;
 	disk->d_mediasize = (off_t)(disk->d_sectorsize * nsd->nsze);
diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h
index b7e82e83a13f..25e2bd67f258 100644
--- a/sys/dev/nvme/nvme.h
+++ b/sys/dev/nvme/nvme.h
@@ -559,6 +559,11 @@ enum nvme_psdt {
 #define NVME_NS_DATA_FLBAS_FORMAT_MASK			(0xF)
 #define NVME_NS_DATA_FLBAS_EXTENDED_SHIFT		(4)
 #define NVME_NS_DATA_FLBAS_EXTENDED_MASK		(0x1)
+#define NVME_NS_DATA_FLBAS_FORMAT_MSB_SHIFT		(5)
+#define NVME_NS_DATA_FLBAS_FORMAT_MSB_MASK		(0x3)
+/* FIDXL width, and the format count below which FIDXU is reserved. */
+#define NVME_NS_DATA_FLBAS_FIDXL_BITS			(4)
+#define NVME_NS_DATA_LBAF_BASE_COUNT			(16)
 
 /** metadata capabilities */
 /* metadata can be transferred as part of data prp list */
@@ -2083,6 +2088,19 @@ extern int nvme_use_nvd;
 
 #endif /* _KERNEL */
 
+static inline uint8_t
+nvme_ns_data_format_index(const struct nvme_namespace_data *nsdata)
+{
+	uint8_t fmt;
+
+	fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas);
+	/* FIDXU is valid only above the base count; NLBAF is 0's based. */
+	if (nsdata->nlbaf + 1 > NVME_NS_DATA_LBAF_BASE_COUNT)
+		fmt |= NVMEV(NVME_NS_DATA_FLBAS_FORMAT_MSB, nsdata->flbas) <<
+		    NVME_NS_DATA_FLBAS_FIDXL_BITS;
+	return (fmt);
+}
+
 /* Endianess conversion functions for NVMe structs */
 static inline
 void	nvme_completion_swapbytes(struct nvme_completion *s __unused)
diff --git a/sys/dev/nvme/nvme_ns.c b/sys/dev/nvme/nvme_ns.c
index f52e75bfa243..1124aaa4cad9 100644
--- a/sys/dev/nvme/nvme_ns.c
+++ b/sys/dev/nvme/nvme_ns.c
@@ -192,7 +192,7 @@ nvme_ns_get_sector_size(struct nvme_namespace *ns)
 {
 	uint8_t flbas_fmt, lbads;
 
-	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, ns->data.flbas);
+	flbas_fmt = nvme_ns_data_format_index(&ns->data);
 	lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, ns->data.lbaf[flbas_fmt]);
 
 	return (1 << lbads);
@@ -568,7 +568,7 @@ nvme_ns_construct(struct nvme_namespace *ns, uint32_t id,
 	 * Check the validity of the format specified. Note: format is a 0-based
 	 * value, so > is appropriate here, not >=.
 	 */
-	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, ns->data.flbas);
+	flbas_fmt = nvme_ns_data_format_index(&ns->data);
 	if (flbas_fmt > ns->data.nlbaf) {
 		nvme_printf(ctrlr, "nsid %d lba format %d invalid (> %d)\n",
 		    id, flbas_fmt, ns->data.nlbaf + 1);
diff --git a/sys/dev/nvmf/host/nvmf_ns.c b/sys/dev/nvmf/host/nvmf_ns.c
index d57280e19d41..572649870e05 100644
--- a/sys/dev/nvmf/host/nvmf_ns.c
+++ b/sys/dev/nvmf/host/nvmf_ns.c
@@ -353,7 +353,7 @@ nvmf_init_ns(struct nvmf_softc *sc, uint32_t id,
 		goto fail;
 	}
 
-	lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, data->flbas);
+	lbaf = nvme_ns_data_format_index(data);
 	if (lbaf > data->nlbaf) {
 		ns_printf(ns, "Invalid LBA format index\n");
 		goto fail;
@@ -505,7 +505,7 @@ nvmf_update_ns(struct nvmf_namespace *ns,
 		return (false);
 	}
 
-	lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, data->flbas);
+	lbaf = nvme_ns_data_format_index(data);
 	if (lbaf > data->nlbaf) {
 		ns_printf(ns, "Invalid LBA format index\n");
 		return (false);
diff --git a/usr.sbin/camdd/camdd.c b/usr.sbin/camdd/camdd.c
index 1f067113c562..3f36704646e3 100644
--- a/usr.sbin/camdd/camdd.c
+++ b/usr.sbin/camdd/camdd.c
@@ -1430,7 +1430,7 @@ camdd_probe_pass_nvme(struct cam_device *cam_dev, union ccb *ccb,
 
 	*maxsector = nsdata.nsze;
 	/* The LBA Data Size (LBADS) is reported as a power of 2 */
-	format = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata.flbas);
+	format = nvme_ns_data_format_index(&nsdata);
 	lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsdata.lbaf[format]);
 	*block_len = 1 << lbads;