Index | Thread | Search

From:
joshua stein <jcs@jcs.org>
Subject:
nvme: use I/O submission queue entry size reported by controller
To:
tech@openbsd.org
Date:
Sun, 17 May 2026 12:25:57 -0500

Download raw body.

Thread
  • joshua stein:

    nvme: use I/O submission queue entry size reported by controller

On at least the Apple T2 NVMe, 128-byte submission queue entries on
I/O queues are required instead of the standard 64 bytes.

This gets NVMe working on the 2018 Mac Mini.  Also tested on a 
non-Apple NVMe but more tests would be helpful.


diff --git sys/dev/ic/nvme.c sys/dev/ic/nvme.c
index 67f88e1ecb9..de3607e2972 100644
--- sys/dev/ic/nvme.c
+++ sys/dev/ic/nvme.c
@@ -1090,20 +1090,25 @@ void
 nvme_q_submit(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb,
     void (*fill)(struct nvme_softc *, struct nvme_ccb *, void *))
 {
-	struct nvme_sqe *sqe = NVME_DMA_KVA(q->q_sq_dmamem);
+	struct nvme_sqe *sqe;
 	u_int32_t tail;
+	u_int sqe_size;
+
+	sqe_size = (q->q_id == NVME_ADMIN_Q) ? sizeof(struct nvme_sqe) :
+	    sc->sc_sqe_size;
 
 	tail = sc->sc_ops->op_sq_enter(sc, q, ccb);
 
-	sqe += tail;
+	sqe = (struct nvme_sqe *)((char *)NVME_DMA_KVA(q->q_sq_dmamem) +
+	    (tail * sqe_size));
 
 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
-	    sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_POSTWRITE);
-	memset(sqe, 0, sizeof(*sqe));
+	    sqe_size * tail, sqe_size, BUS_DMASYNC_POSTWRITE);
+	memset(sqe, 0, sqe_size);
 	(*fill)(sc, ccb, sqe);
 	sqe->cid = ccb->ccb_id;
 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
-	    sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_PREWRITE);
+	    sqe_size * tail, sqe_size, BUS_DMASYNC_PREWRITE);
 
 	sc->sc_ops->op_sq_leave(sc, q, ccb);
 }
@@ -1283,6 +1288,9 @@ nvme_identify(struct nvme_softc *sc, u_int mpsmin)
 
 	sc->sc_nn = lemtoh32(&identify->nn);
 
+	/* use maximum I/O SQE size reported */
+	sc->sc_sqe_size = 1 << (identify->sqes >> 4);
+
 	/*
 	 * At least one Apple NVMe device presents a second, bogus disk that is
 	 * inaccessible, so cap targets at 1.
@@ -1495,7 +1503,8 @@ nvme_q_alloc(struct nvme_softc *sc, u_int16_t id, u_int entries, u_int dstrd)
 		return (NULL);
 
 	q->q_sq_dmamem = nvme_dmamem_alloc(sc,
-	    sizeof(struct nvme_sqe) * entries);
+	    (id == NVME_ADMIN_Q ? sizeof(struct nvme_sqe) : sc->sc_sqe_size) *
+	    entries);
 	if (q->q_sq_dmamem == NULL)
 		goto free;
 
diff --git sys/dev/ic/nvmevar.h sys/dev/ic/nvmevar.h
index efbf737ed23..a5292f51f4f 100644
--- sys/dev/ic/nvmevar.h
+++ sys/dev/ic/nvmevar.h
@@ -111,6 +111,7 @@ struct nvme_softc {
 	size_t			sc_mdts;
 	u_int			sc_max_prpl;
 	u_int			sc_dstrd;
+	u_int			sc_sqe_size;
 
 	struct nvm_identify_controller
 				sc_identify;
diff --git sys/dev/pci/nvme_pci.c sys/dev/pci/nvme_pci.c
index 7ad4daffb9f..a42065274cc 100644
--- sys/dev/pci/nvme_pci.c
+++ sys/dev/pci/nvme_pci.c
@@ -67,7 +67,8 @@ nvme_pci_match(struct device *parent, void *match, void *aux)
 
 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE &&
 	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_NVME1 ||
-	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_NVME2))
+	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_NVME2 ||
+	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_NVME3))
 	    	return (1);
 
 	return (0);