svn commit: r348806 - head/usr.sbin/camdd

Chuck Tuffli chuck at FreeBSD.org
Sat Jun 8 17:17:19 UTC 2019


Author: chuck
Date: Sat Jun  8 17:17:17 2019
New Revision: 348806
URL: https://svnweb.freebsd.org/changeset/base/348806

Log:
  Add NVMe support to camdd(8)
  
  Reviewed by:	ken
  Approved by:	ken (mentor)
  MFC after:	1 week
  Differential Review: https://reviews.freebsd.org/D12141

Modified:
  head/usr.sbin/camdd/camdd.c

Modified: head/usr.sbin/camdd/camdd.c
==============================================================================
--- head/usr.sbin/camdd/camdd.c	Sat Jun  8 16:26:56 2019	(r348805)
+++ head/usr.sbin/camdd/camdd.c	Sat Jun  8 17:17:17 2019	(r348806)
@@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$");
 #include <cam/scsi/scsi_pass.h>
 #include <cam/scsi/scsi_message.h>
 #include <cam/scsi/smp_all.h>
+#include <cam/nvme/nvme_all.h>
 #include <camlib.h>
 #include <mtlib.h>
 #include <zlib.h>
@@ -463,6 +464,9 @@ int camdd_probe_tape(int fd, char *filename, uint64_t 
 int camdd_probe_pass_scsi(struct cam_device *cam_dev, union ccb *ccb,
          camdd_argmask arglist, int probe_retry_count,
          int probe_timeout, uint64_t *maxsector, uint32_t *block_len);
+int camdd_probe_pass_nvme(struct cam_device *cam_dev, union ccb *ccb,
+         camdd_argmask arglist, int probe_retry_count,
+         int probe_timeout, uint64_t *maxsector, uint32_t *block_len);
 struct camdd_dev *camdd_probe_file(int fd, struct camdd_io_opts *io_opts,
 				   int retry_count, int timeout);
 struct camdd_dev *camdd_probe_pass(struct cam_device *cam_dev,
@@ -470,6 +474,11 @@ struct camdd_dev *camdd_probe_pass(struct cam_device *
 				   camdd_argmask arglist, int probe_retry_count,
 				   int probe_timeout, int io_retry_count,
 				   int io_timeout);
+void nvme_read_write(struct ccb_nvmeio *nvmeio, uint32_t retries,
+		void (*cbfcnp)(struct cam_periph *, union ccb *),
+		uint32_t nsid, int readop, uint64_t lba,
+		uint32_t block_count, uint8_t *data_ptr, uint32_t dxfer_len,
+		uint32_t timeout);
 void *camdd_file_worker(void *arg);
 camdd_buf_status camdd_ccb_status(union ccb *ccb, int protocol);
 int camdd_get_cgd(struct cam_device *device, struct ccb_getdev *cgd);
@@ -1379,6 +1388,72 @@ bailout:
 	return retval;
 }
 
+int
+camdd_probe_pass_nvme(struct cam_device *cam_dev, union ccb *ccb,
+		 camdd_argmask arglist, int probe_retry_count,
+		 int probe_timeout, uint64_t *maxsector, uint32_t *block_len)
+{
+	struct nvme_command *nc = NULL;
+	struct nvme_namespace_data nsdata;
+	uint32_t nsid = cam_dev->target_lun & UINT32_MAX;
+	uint8_t format = 0, lbads = 0;
+	int retval = -1;
+
+	if (ccb == NULL) {
+		warnx("%s: error passed ccb is NULL", __func__);
+		goto bailout;
+	}
+
+	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->nvmeio);
+
+	/* Send Identify Namespace to get block size and capacity */
+	nc = &ccb->nvmeio.cmd;
+	nc->opc = NVME_OPC_IDENTIFY;
+
+	nc->nsid = nsid;
+	nc->cdw10 = 0; /* Identify Namespace is CNS = 0 */
+
+	cam_fill_nvmeadmin(&ccb->nvmeio,
+			/*retries*/ probe_retry_count,
+			/*cbfcnp*/ NULL,
+			CAM_DIR_IN,
+			(uint8_t *)&nsdata,
+			sizeof(nsdata),
+			probe_timeout);
+
+	/* Disable freezing the device queue */
+	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+	if (arglist & CAMDD_ARG_ERR_RECOVER)
+		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+	if (cam_send_ccb(cam_dev, ccb) < 0) {
+		warn("error sending Identify Namespace command");
+
+		cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
+				CAM_EPF_ALL, stderr);
+
+		goto bailout;
+	}
+
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
+		goto bailout;
+	}
+
+	*maxsector = nsdata.nsze;
+	/* The LBA Data Size (LBADS) is reported as a power of 2 */
+	format = nsdata.flbas & NVME_NS_DATA_FLBAS_FORMAT_MASK;
+	lbads = (nsdata.lbaf[format] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
+	    NVME_NS_DATA_LBAF_LBADS_MASK;
+	*block_len = 1 << lbads;
+
+	retval = 0;
+
+bailout:
+	return retval;
+}
+
 /*
  * Need to implement this.  Do a basic probe:
  * - Check the inquiry data, make sure we're talking to a device that we
@@ -1442,6 +1517,13 @@ camdd_probe_pass(struct cam_device *cam_dev, struct ca
 			goto bailout;
 		}
 		break;
+	case PROTO_NVME:
+		if ((retval = camdd_probe_pass_nvme(cam_dev, ccb, probe_retry_count,
+						arglist, probe_timeout, &maxsector,
+						&block_len))) {
+			goto bailout;
+		}
+		break;
 	default:
 		errx(1, "Unsupported PROTO type %d", cgd.protocol);
 		break; /*NOTREACHED*/
@@ -1576,6 +1658,34 @@ bailout_error:
 	return (NULL);
 }
 
+void
+nvme_read_write(struct ccb_nvmeio *nvmeio, uint32_t retries,
+		void (*cbfcnp)(struct cam_periph *, union ccb *),
+		uint32_t nsid, int readop, uint64_t lba,
+		uint32_t block_count, uint8_t *data_ptr, uint32_t dxfer_len,
+		uint32_t timeout)
+{
+	struct nvme_command *nc = &nvmeio->cmd;
+
+	nc->opc = readop ? NVME_OPC_READ : NVME_OPC_WRITE;
+
+	nc->nsid = nsid;
+
+	nc->cdw10 = lba & UINT32_MAX;
+	nc->cdw11 = lba >> 32;
+
+	/* NLB (bits 15:0) is a zero based value */
+	nc->cdw12 = (block_count - 1) & UINT16_MAX;
+
+	cam_fill_nvmeio(nvmeio,
+			retries,
+			cbfcnp,
+			readop ? CAM_DIR_IN : CAM_DIR_OUT,
+			data_ptr,
+			dxfer_len,
+			timeout);
+}
+
 void *
 camdd_worker(void *arg)
 {
@@ -1831,6 +1941,16 @@ camdd_ccb_status(union ccb *ccb, int protocol)
 			break;
 		}
 		break;
+	case PROTO_NVME:
+		switch (ccb_status) {
+		case CAM_REQ_CMP:
+			status = CAMDD_STATUS_OK;
+			break;
+		default:
+			status = CAMDD_STATUS_ERROR;
+			break;
+		}
+		break;
 	default:
 		status = CAMDD_STATUS_ERROR;
 		break;
@@ -2233,6 +2353,10 @@ camdd_pass_fetch(struct camdd_dev *dev)
 			data->resid = ccb.csio.resid;
 			dev->bytes_transferred += (ccb.csio.dxfer_len - ccb.csio.resid);
 			break;
+		case PROTO_NVME:
+			data->resid = 0;
+			dev->bytes_transferred += ccb.nvmeio.dxfer_len;
+			break;
 		default:
 			return -1;
 			break;
@@ -2554,6 +2678,23 @@ camdd_pass_run(struct camdd_dev *dev)
 		if (data->sg_count != 0) {
 			ccb->csio.sglist_cnt = data->sg_count;
 		}
+		break;
+	case PROTO_NVME:
+		CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->nvmeio);
+
+		nvme_read_write(&ccb->nvmeio,
+				/*retries*/ dev->retry_count,
+				/*cbfcnp*/ NULL,
+				/*nsid*/ pass_dev->dev->target_lun & UINT32_MAX,
+				/*readop*/ dev->write_dev == 0,
+				/*lba*/ buf->lba,
+				/*block_count*/ num_blocks,
+				/*data_ptr*/ (data->sg_count != 0) ?
+					     (uint8_t *)data->segs : data->buf,
+				/*dxfer_len*/ (num_blocks * pass_dev->block_len),
+				/*timeout*/ dev->io_timeout);
+
+		ccb->nvmeio.sglist_cnt = data->sg_count;
 		break;
 	default:
 		retval = -1;


More information about the svn-src-all mailing list