summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/nvme/nvme.c47
1 files changed, 36 insertions, 11 deletions
diff --git a/usr/src/uts/common/io/nvme/nvme.c b/usr/src/uts/common/io/nvme/nvme.c
index 0edb37f16e..68d6855803 100644
--- a/usr/src/uts/common/io/nvme/nvme.c
+++ b/usr/src/uts/common/io/nvme/nvme.c
@@ -72,6 +72,14 @@
* as the configured queue length.
*
*
+ * Polled I/O Support:
+ *
+ * For kernel core dump support the driver can do polled I/O. As interrupts are
+ * turned off while dumping the driver will just submit a command in the regular
+ * way, and then repeatedly attempt a command retrieval until it gets the
+ * command back.
+ *
+ *
* Namespace Support:
*
* NVMe devices can have multiple namespaces, each being a independent data
@@ -176,7 +184,6 @@
*
* TODO:
* - figure out sane default for I/O queue depth reported to blkdev
- * - polled I/O support to support kernel core dumping
* - FMA handling of media errors
* - support for devices supporting very large I/O requests using chained PRPs
* - support for configuring hardware parameters like interrupt coalescing
@@ -856,20 +863,21 @@ nvme_retrieve_cmd(nvme_t *nvme, nvme_qpair_t *qp)
(void) ddi_dma_sync(qp->nq_cqdma->nd_dmah, 0,
sizeof (nvme_cqe_t) * qp->nq_nentry, DDI_DMA_SYNC_FORKERNEL);
+ mutex_enter(&qp->nq_mutex);
cqe = &qp->nq_cq[qp->nq_cqhead];
/* Check phase tag of CQE. Hardware inverts it for new entries. */
- if (cqe->cqe_sf.sf_p == qp->nq_phase)
+ if (cqe->cqe_sf.sf_p == qp->nq_phase) {
+ mutex_exit(&qp->nq_mutex);
return (NULL);
+ }
ASSERT(nvme->n_ioq[cqe->cqe_sqid] == qp);
ASSERT(cqe->cqe_cid < qp->nq_nentry);
- mutex_enter(&qp->nq_mutex);
cmd = qp->nq_cmd[cqe->cqe_cid];
qp->nq_cmd[cqe->cqe_cid] = NULL;
qp->nq_active_cmds--;
- mutex_exit(&qp->nq_mutex);
ASSERT(cmd != NULL);
ASSERT(cmd->nc_nvme == nvme);
@@ -886,6 +894,7 @@ nvme_retrieve_cmd(nvme_t *nvme, nvme_qpair_t *qp)
qp->nq_phase = qp->nq_phase ? 0 : 1;
nvme_put32(cmd->nc_nvme, qp->nq_cqhdbl, head.r);
+ mutex_exit(&qp->nq_mutex);
return (cmd);
}
@@ -3269,26 +3278,42 @@ static int
nvme_bd_cmd(nvme_namespace_t *ns, bd_xfer_t *xfer, uint8_t opc)
{
nvme_t *nvme = ns->ns_nvme;
- nvme_cmd_t *cmd;
+ nvme_cmd_t *cmd, *ret;
+ nvme_qpair_t *ioq;
+ boolean_t poll;
if (nvme->n_dead)
return (EIO);
- /* No polling for now */
- if (xfer->x_flags & BD_XFER_POLL)
- return (EIO);
-
cmd = nvme_create_nvm_cmd(ns, opc, xfer);
if (cmd == NULL)
return (ENOMEM);
cmd->nc_sqid = (CPU->cpu_id % nvme->n_ioq_count) + 1;
ASSERT(cmd->nc_sqid <= nvme->n_ioq_count);
+ ioq = nvme->n_ioq[cmd->nc_sqid];
- if (nvme_submit_cmd(nvme->n_ioq[cmd->nc_sqid], cmd)
- != DDI_SUCCESS)
+ /*
+ * Get the polling flag before submitting the command. The command may
+ * complete immediately after it was submitted, which means we must
+ * treat both cmd and xfer as if they have been freed already.
+ */
+ poll = (xfer->x_flags & BD_XFER_POLL) != 0;
+
+ if (nvme_submit_cmd(ioq, cmd) != DDI_SUCCESS)
return (EAGAIN);
+ if (!poll)
+ return (0);
+
+ do {
+ ret = nvme_retrieve_cmd(nvme, ioq);
+ if (ret != NULL)
+ nvme_bd_xfer_done(ret);
+ else
+ drv_usecwait(10);
+ } while (ioq->nq_active_cmds != 0);
+
return (0);
}