Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 12 Apr 2013 17:52:17 +0000 (UTC)
From:      Jim Harris <jimharris@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r249421 - head/sys/dev/nvme
Message-ID:  <201304121752.r3CHqH1H095286@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <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_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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201304121752.r3CHqH1H095286>