summaryrefslogtreecommitdiffstats
path: root/sys/dev/nvme/nvme_ctrlr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/nvme/nvme_ctrlr.c')
-rw-r--r--sys/dev/nvme/nvme_ctrlr.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c
index 9d66b9c..5116e7e 100644
--- a/sys/dev/nvme/nvme_ctrlr.c
+++ b/sys/dev/nvme/nvme_ctrlr.c
@@ -28,10 +28,14 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/ioccom.h>
+#include <sys/proc.h>
#include <sys/smp.h>
+#include <sys/uio.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@@ -878,12 +882,103 @@ nvme_ctrlr_configure_intx(struct nvme_controller *ctrlr)
return (0);
}
+static void
+nvme_pt_done(void *arg, const struct nvme_completion *cpl)
+{
+ struct nvme_pt_command *pt = arg;
+
+ bzero(&pt->cpl, sizeof(pt->cpl));
+ pt->cpl.cdw0 = cpl->cdw0;
+ pt->cpl.status = cpl->status;
+ pt->cpl.status.p = 0;
+
+ mtx_lock(pt->driver_lock);
+ wakeup(pt);
+ mtx_unlock(pt->driver_lock);
+}
+
+int
+nvme_ctrlr_passthrough_cmd(struct nvme_controller *ctrlr,
+ struct nvme_pt_command *pt, uint32_t nsid, int is_user_buffer,
+ int is_admin_cmd)
+{
+ struct nvme_request *req;
+ struct mtx *mtx;
+ struct buf *buf = NULL;
+ int ret = 0;
+
+ if (pt->len > 0)
+ if (is_user_buffer) {
+ /*
+ * Ensure the user buffer is wired for the duration of
+ * this passthrough command.
+ */
+ PHOLD(curproc);
+ buf = getpbuf(NULL);
+ buf->b_saveaddr = buf->b_data;
+ buf->b_data = pt->buf;
+ buf->b_bufsize = pt->len;
+ buf->b_iocmd = pt->is_read ? BIO_READ : BIO_WRITE;
+#ifdef NVME_UNMAPPED_BIO_SUPPORT
+ if (vmapbuf(buf, 1) < 0) {
+#else
+ if (vmapbuf(buf) < 0) {
+#endif
+ ret = EFAULT;
+ goto err;
+ }
+ req = nvme_allocate_request_vaddr(buf->b_data, pt->len,
+ nvme_pt_done, pt);
+ } else
+ req = nvme_allocate_request_vaddr(pt->buf, pt->len,
+ nvme_pt_done, pt);
+ else
+ req = nvme_allocate_request_null(nvme_pt_done, pt);
+
+ req->cmd.opc = pt->cmd.opc;
+ req->cmd.cdw10 = pt->cmd.cdw10;
+ req->cmd.cdw11 = pt->cmd.cdw11;
+ req->cmd.cdw12 = pt->cmd.cdw12;
+ req->cmd.cdw13 = pt->cmd.cdw13;
+ req->cmd.cdw14 = pt->cmd.cdw14;
+ req->cmd.cdw15 = pt->cmd.cdw15;
+
+ req->cmd.nsid = nsid;
+
+ if (is_admin_cmd)
+ mtx = &ctrlr->lock;
+ else
+ mtx = &ctrlr->ns[nsid-1].lock;
+
+ mtx_lock(mtx);
+ pt->driver_lock = mtx;
+
+ if (is_admin_cmd)
+ nvme_ctrlr_submit_admin_request(ctrlr, req);
+ else
+ nvme_ctrlr_submit_io_request(ctrlr, req);
+
+ mtx_sleep(pt, mtx, PRIBIO, "nvme_pt", 0);
+ mtx_unlock(mtx);
+
+ pt->driver_lock = NULL;
+
+err:
+ if (buf != NULL) {
+ relpbuf(buf, NULL);
+ PRELE(curproc);
+ }
+
+ return (ret);
+}
+
static int
nvme_ctrlr_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int flag,
struct thread *td)
{
struct nvme_completion_poll_status status;
struct nvme_controller *ctrlr;
+ struct nvme_pt_command *pt;
ctrlr = cdev->si_drv1;
@@ -912,6 +1007,10 @@ nvme_ctrlr_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int flag,
case NVME_RESET_CONTROLLER:
nvme_ctrlr_reset(ctrlr);
break;
+ case NVME_PASSTHROUGH_CMD:
+ pt = (struct nvme_pt_command *)arg;
+ return (nvme_ctrlr_passthrough_cmd(ctrlr, pt, pt->cmd.nsid,
+ 1 /* is_user_buffer */, 1 /* is_admin_cmd */));
default:
return (ENOTTY);
}
OpenPOWER on IntegriCloud