svn commit: r362752 - head/usr.sbin/bhyve
Chuck Tuffli
chuck at FreeBSD.org
Mon Jun 29 00:32:03 UTC 2020
Author: chuck
Date: Mon Jun 29 00:31:37 2020
New Revision: 362752
URL: https://svnweb.freebsd.org/changeset/base/362752
Log:
bhyve: fix NVMe queue creation and deletion
Add checks for various types of invalid I/O Queue Create and Delete
command parameters, including:
- QID=0
- QID>MAX
- QID already in use
- Delete an Active CQ
- Invalid QSIZE
- Invalid CQID (SQ creation)
- Invalid interrupt vector (CQ creation)
Fixes UNH Tests 1.4.2-5,7-8
Tested by: Jason Tubnor
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D24886
Modified:
head/usr.sbin/bhyve/pci_nvme.c
Modified: head/usr.sbin/bhyve/pci_nvme.c
==============================================================================
--- head/usr.sbin/bhyve/pci_nvme.c Mon Jun 29 00:31:34 2020 (r362751)
+++ head/usr.sbin/bhyve/pci_nvme.c Mon Jun 29 00:31:37 2020 (r362752)
@@ -705,7 +705,8 @@ nvme_opc_delete_io_sq(struct pci_nvme_softc* sc, struc
uint16_t qid = command->cdw10 & 0xffff;
DPRINTF("%s DELETE_IO_SQ %u", __func__, qid);
- if (qid == 0 || qid > sc->num_squeues) {
+ if (qid == 0 || qid > sc->num_squeues ||
+ (sc->submit_queues[qid].qbase == NULL)) {
WPRINTF("%s NOT PERMITTED queue id %u / num_squeues %u",
__func__, qid, sc->num_squeues);
pci_nvme_status_tc(&compl->status, NVME_SCT_COMMAND_SPECIFIC,
@@ -714,6 +715,7 @@ nvme_opc_delete_io_sq(struct pci_nvme_softc* sc, struc
}
sc->submit_queues[qid].qbase = NULL;
+ sc->submit_queues[qid].cqid = 0;
pci_nvme_status_genc(&compl->status, NVME_SC_SUCCESS);
return (1);
}
@@ -726,7 +728,8 @@ nvme_opc_create_io_sq(struct pci_nvme_softc* sc, struc
uint16_t qid = command->cdw10 & 0xffff;
struct nvme_submission_queue *nsq;
- if ((qid == 0) || (qid > sc->num_squeues)) {
+ if ((qid == 0) || (qid > sc->num_squeues) ||
+ (sc->submit_queues[qid].qbase != NULL)) {
WPRINTF("%s queue index %u > num_squeues %u",
__func__, qid, sc->num_squeues);
pci_nvme_status_tc(&compl->status,
@@ -737,12 +740,39 @@ nvme_opc_create_io_sq(struct pci_nvme_softc* sc, struc
nsq = &sc->submit_queues[qid];
nsq->size = ONE_BASED((command->cdw10 >> 16) & 0xffff);
+ DPRINTF("%s size=%u (max=%u)", __func__, nsq->size, sc->max_qentries);
+ if ((nsq->size < 2) || (nsq->size > sc->max_qentries)) {
+ /*
+ * Queues must specify at least two entries
+ * NOTE: "MAXIMUM QUEUE SIZE EXCEEDED" was renamed to
+ * "INVALID QUEUE SIZE" in the NVM Express 1.3 Spec
+ */
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED);
+ return (1);
+ }
- nsq->qbase = vm_map_gpa(sc->nsc_pi->pi_vmctx, command->prp1,
- sizeof(struct nvme_command) * (size_t)nsq->size);
nsq->cqid = (command->cdw11 >> 16) & 0xffff;
+ if ((nsq->cqid == 0) || (nsq->cqid > sc->num_cqueues)) {
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_INVALID_QUEUE_IDENTIFIER);
+ return (1);
+ }
+
+ if (sc->compl_queues[nsq->cqid].qbase == NULL) {
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_COMPLETION_QUEUE_INVALID);
+ return (1);
+ }
+
nsq->qpriority = (command->cdw11 >> 1) & 0x03;
+ nsq->qbase = vm_map_gpa(sc->nsc_pi->pi_vmctx, command->prp1,
+ sizeof(struct nvme_command) * (size_t)nsq->size);
+
DPRINTF("%s sq %u size %u gaddr %p cqid %u", __func__,
qid, nsq->size, nsq->qbase, nsq->cqid);
@@ -768,9 +798,11 @@ nvme_opc_delete_io_cq(struct pci_nvme_softc* sc, struc
struct nvme_completion* compl)
{
uint16_t qid = command->cdw10 & 0xffff;
+ uint16_t sqid;
DPRINTF("%s DELETE_IO_CQ %u", __func__, qid);
- if (qid == 0 || qid > sc->num_cqueues) {
+ if (qid == 0 || qid > sc->num_cqueues ||
+ (sc->compl_queues[qid].qbase == NULL)) {
WPRINTF("%s queue index %u / num_cqueues %u",
__func__, qid, sc->num_cqueues);
pci_nvme_status_tc(&compl->status, NVME_SCT_COMMAND_SPECIFIC,
@@ -778,6 +810,15 @@ nvme_opc_delete_io_cq(struct pci_nvme_softc* sc, struc
return (1);
}
+ /* Deleting an Active CQ is an error */
+ for (sqid = 1; sqid < sc->num_squeues + 1; sqid++)
+ if (sc->submit_queues[sqid].cqid == qid) {
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_INVALID_QUEUE_DELETION);
+ return (1);
+ }
+
sc->compl_queues[qid].qbase = NULL;
pci_nvme_status_genc(&compl->status, NVME_SC_SUCCESS);
return (1);
@@ -787,41 +828,57 @@ static int
nvme_opc_create_io_cq(struct pci_nvme_softc* sc, struct nvme_command* command,
struct nvme_completion* compl)
{
+ struct nvme_completion_queue *ncq;
+ uint16_t qid = command->cdw10 & 0xffff;
- if (command->cdw11 & NVME_CMD_CDW11_PC) {
- uint16_t qid = command->cdw10 & 0xffff;
- struct nvme_completion_queue *ncq;
+ /* Only support Physically Contiguous queues */
+ if ((command->cdw11 & NVME_CMD_CDW11_PC) == 0) {
+ WPRINTF("%s unsupported non-contig (list-based) "
+ "create i/o completion queue",
+ __func__);
- if ((qid == 0) || (qid > sc->num_cqueues)) {
- WPRINTF("%s queue index %u > num_cqueues %u",
- __func__, qid, sc->num_cqueues);
- pci_nvme_status_tc(&compl->status,
- NVME_SCT_COMMAND_SPECIFIC,
- NVME_SC_INVALID_QUEUE_IDENTIFIER);
- return (1);
- }
+ pci_nvme_status_genc(&compl->status, NVME_SC_INVALID_FIELD);
+ return (1);
+ }
- ncq = &sc->compl_queues[qid];
- ncq->intr_en = (command->cdw11 & NVME_CMD_CDW11_IEN) >> 1;
- ncq->intr_vec = (command->cdw11 >> 16) & 0xffff;
- ncq->size = ONE_BASED((command->cdw10 >> 16) & 0xffff);
+ if ((qid == 0) || (qid > sc->num_cqueues) ||
+ (sc->compl_queues[qid].qbase != NULL)) {
+ WPRINTF("%s queue index %u > num_cqueues %u",
+ __func__, qid, sc->num_cqueues);
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_INVALID_QUEUE_IDENTIFIER);
+ return (1);
+ }
- ncq->qbase = vm_map_gpa(sc->nsc_pi->pi_vmctx,
- command->prp1,
- sizeof(struct nvme_command) * (size_t)ncq->size);
+ ncq = &sc->compl_queues[qid];
+ ncq->intr_en = (command->cdw11 & NVME_CMD_CDW11_IEN) >> 1;
+ ncq->intr_vec = (command->cdw11 >> 16) & 0xffff;
+ if (ncq->intr_vec > (sc->max_queues + 1)) {
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_INVALID_INTERRUPT_VECTOR);
+ return (1);
+ }
- pci_nvme_status_genc(&compl->status, NVME_SC_SUCCESS);
- } else {
- /*
- * Non-contig completion queue unsupported.
+ ncq->size = ONE_BASED((command->cdw10 >> 16) & 0xffff);
+ if ((ncq->size < 2) || (ncq->size > sc->max_qentries)) {
+ /*
+ * Queues must specify at least two entries
+ * NOTE: "MAXIMUM QUEUE SIZE EXCEEDED" was renamed to
+ * "INVALID QUEUE SIZE" in the NVM Express 1.3 Spec
*/
- WPRINTF("%s unsupported non-contig (list-based) "
- "create i/o completion queue",
- __func__);
-
- /* 0x12 = Invalid Use of Controller Memory Buffer */
- pci_nvme_status_genc(&compl->status, 0x12);
+ pci_nvme_status_tc(&compl->status,
+ NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED);
+ return (1);
}
+ ncq->qbase = vm_map_gpa(sc->nsc_pi->pi_vmctx,
+ command->prp1,
+ sizeof(struct nvme_command) * (size_t)ncq->size);
+
+ pci_nvme_status_genc(&compl->status, NVME_SC_SUCCESS);
+
return (1);
}
More information about the svn-src-all
mailing list