Index: usr.sbin/camdd/camdd.c =================================================================== --- usr.sbin/camdd/camdd.c +++ usr.sbin/camdd/camdd.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include @@ -481,6 +482,9 @@ 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, @@ -488,6 +492,11 @@ 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); @@ -1401,6 +1410,68 @@ 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; + 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 */ + *block_len = 1 << nsdata.lbaf[nsdata.flbas.format].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 @@ -1464,6 +1535,13 @@ 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*/ @@ -1598,6 +1676,34 @@ 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) { @@ -1853,6 +1959,16 @@ 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; @@ -2255,6 +2371,10 @@ 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; @@ -2577,6 +2697,23 @@ 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; goto bailout;