From owner-svn-src-head@FreeBSD.ORG Fri Apr 12 17:52:18 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id 27FAA39F; Fri, 12 Apr 2013 17:52:18 +0000 (UTC) (envelope-from jimharris@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 199721493; Fri, 12 Apr 2013 17:52:18 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.6/8.14.6) with ESMTP id r3CHqHSA095289; Fri, 12 Apr 2013 17:52:17 GMT (envelope-from jimharris@svn.freebsd.org) Received: (from jimharris@localhost) by svn.freebsd.org (8.14.6/8.14.5/Submit) id r3CHqH1H095286; Fri, 12 Apr 2013 17:52:17 GMT (envelope-from jimharris@svn.freebsd.org) Message-Id: <201304121752.r3CHqH1H095286@svn.freebsd.org> From: Jim Harris Date: Fri, 12 Apr 2013 17:52:17 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r249421 - head/sys/dev/nvme X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 12 Apr 2013 17:52:18 -0000 Author: jimharris Date: Fri Apr 12 17:52:17 2013 New Revision: 249421 URL: http://svnweb.freebsd.org/changeset/base/249421 Log: Add support for passthrough NVMe commands. This includes a new IOCTL to support a generic method for nvmecontrol(8) to pass IDENTIFY, GET_LOG_PAGE, GET_FEATURES and other commands to the controller, rather than separate IOCTLs for each. Sponsored by: Intel Modified: head/sys/dev/nvme/nvme.h head/sys/dev/nvme/nvme_ctrlr.c head/sys/dev/nvme/nvme_ns.c Modified: head/sys/dev/nvme/nvme.h ============================================================================== --- head/sys/dev/nvme/nvme.h Fri Apr 12 17:48:45 2013 (r249420) +++ head/sys/dev/nvme/nvme.h Fri Apr 12 17:52:17 2013 (r249421) @@ -38,6 +38,7 @@ #define NVME_IO_TEST _IOWR('n', 2, struct nvme_io_test) #define NVME_BIO_TEST _IOWR('n', 4, struct nvme_io_test) #define NVME_RESET_CONTROLLER _IO('n', 5) +#define NVME_PASSTHROUGH_CMD _IOWR('n', 6, struct nvme_pt_command) /* * Use to mark a command to apply to all namespaces, or to retrieve global @@ -716,6 +717,59 @@ enum nvme_io_test_flags { NVME_TEST_FLAG_REFTHREAD = 0x1, }; +struct nvme_pt_command { + + /* + * cmd is used to specify a passthrough command to a controller or + * namespace. + * + * The following fields from cmd may be specified by the caller: + * * opc (opcode) + * * nsid (namespace id) - for admin commands only + * * cdw10-cdw15 + * + * Remaining fields must be set to 0 by the caller. + */ + struct nvme_command cmd; + + /* + * cpl returns completion status for the passthrough command + * specified by cmd. + * + * The following fields will be filled out by the driver, for + * consumption by the caller: + * * cdw0 + * * status (except for phase) + * + * Remaining fields will be set to 0 by the driver. + */ + struct nvme_completion cpl; + + /* buf is the data buffer associated with this passthrough command. */ + void * buf; + + /* + * len is the length of the data buffer associated with this + * passthrough command. + */ + uint32_t len; + + /* + * is_read = 1 if the passthrough command will read data into the + * supplied buffer. + * + * is_read = 0 if the passthrough command will write data into the + * supplied buffer. + */ + uint32_t is_read; + + /* + * driver_lock is used by the driver only. It must be set to 0 + * by the caller. + */ + struct mtx * driver_lock; +}; + #define nvme_completion_is_error(cpl) \ ((cpl)->status.sc != 0 || (cpl)->status.sct != 0) @@ -740,6 +794,11 @@ enum nvme_namespace_flags { NVME_NS_FLUSH_SUPPORTED = 0x2, }; +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); + /* Admin functions */ void nvme_ctrlr_cmd_set_feature(struct nvme_controller *ctrlr, uint8_t feature, uint32_t cdw11, Modified: head/sys/dev/nvme/nvme_ctrlr.c ============================================================================== --- head/sys/dev/nvme/nvme_ctrlr.c Fri Apr 12 17:48:45 2013 (r249420) +++ head/sys/dev/nvme/nvme_ctrlr.c Fri Apr 12 17:52:17 2013 (r249421) @@ -28,10 +28,14 @@ __FBSDID("$FreeBSD$"); #include +#include +#include #include #include #include +#include #include +#include #include #include @@ -878,12 +882,103 @@ nvme_ctrlr_configure_intx(struct nvme_co 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_lo 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); } Modified: head/sys/dev/nvme/nvme_ns.c ============================================================================== --- head/sys/dev/nvme/nvme_ns.c Fri Apr 12 17:48:45 2013 (r249420) +++ head/sys/dev/nvme/nvme_ns.c Fri Apr 12 17:52:17 2013 (r249421) @@ -48,6 +48,7 @@ nvme_ns_ioctl(struct cdev *cdev, u_long struct nvme_completion_poll_status status; struct nvme_namespace *ns; struct nvme_controller *ctrlr; + struct nvme_pt_command *pt; ns = cdev->si_drv1; ctrlr = ns->ctrlr; @@ -78,6 +79,10 @@ nvme_ns_ioctl(struct cdev *cdev, u_long case NVME_BIO_TEST: nvme_ns_test(ns, cmd, arg); break; + case NVME_PASSTHROUGH_CMD: + pt = (struct nvme_pt_command *)arg; + return (nvme_ctrlr_passthrough_cmd(ctrlr, pt, ns->id, + 1 /* is_user_buffer */, 0 /* is_admin_cmd */)); case DIOCGMEDIASIZE: *(off_t *)arg = (off_t)nvme_ns_get_size(ns); break;