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;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to