diff --git a/include/spdk/scsi_spec.h b/include/spdk/scsi_spec.h index 53ef52b2ca..10b0a5d954 100644 --- a/include/spdk/scsi_spec.h +++ b/include/spdk/scsi_spec.h @@ -232,6 +232,8 @@ enum spdk_sbc_opcode { SPDK_SBC_VL_XPWRITE_32 = 0x0006, }; +#define SPDK_SBC_START_STOP_UNIT_START_BIT (1 << 0) + enum spdk_mmc_opcode { /* MMC6 */ SPDK_MMC_READ_DISC_STRUCTURE = 0xad, diff --git a/lib/bdev/virtio/bdev_virtio.c b/lib/bdev/virtio/bdev_virtio.c index 04ebd6f4a1..b592225d4d 100644 --- a/lib/bdev/virtio/bdev_virtio.c +++ b/lib/bdev/virtio/bdev_virtio.c @@ -723,18 +723,97 @@ send_read_cap_16(struct virtio_scsi_scan_base *base, uint8_t target_id, struct v } } +static void +send_test_unit_ready(struct virtio_scsi_scan_base *base, uint8_t target_id, struct virtio_req *vreq) +{ + struct virtio_scsi_cmd_req *req = vreq->iov_req.iov_base; + int rc; + + memset(req, 0, sizeof(*req)); + req->lun[0] = 1; + req->lun[1] = target_id; + req->cdb[0] = SPDK_SPC_TEST_UNIT_READY; + + rc = virtio_xmit_pkt(base->vq, vreq); + if (rc != 0) { + assert(false); + } +} + +static void +send_start_stop_unit(struct virtio_scsi_scan_base *base, uint8_t target_id, struct virtio_req *vreq) +{ + struct virtio_scsi_cmd_req *req = vreq->iov_req.iov_base; + int rc; + + memset(req, 0, sizeof(*req)); + req->lun[0] = 1; + req->lun[1] = target_id; + req->cdb[0] = SPDK_SBC_START_STOP_UNIT; + req->cdb[4] = SPDK_SBC_START_STOP_UNIT_START_BIT; + + rc = virtio_xmit_pkt(base->vq, vreq); + if (rc != 0) { + assert(false); + } +} + +static int +process_scan_start_stop_unit(struct virtio_scsi_scan_base *base, struct virtio_req *vreq) +{ + struct virtio_scsi_cmd_req *req = vreq->iov_req.iov_base; + struct virtio_scsi_cmd_resp *resp = vreq->iov_resp.iov_base; + uint8_t target_id = req->lun[1]; + int rc = 0; + + if (resp->response == VIRTIO_SCSI_S_OK && resp->status == SPDK_SCSI_STATUS_GOOD) { + send_inquiry_vpd(base, target_id, vreq, SPDK_SPC_VPD_SUPPORTED_VPD_PAGES); + } else { + rc = -1; + } + + return rc; +} + +static int +process_scan_test_unit_ready(struct virtio_scsi_scan_base *base, struct virtio_req *vreq) +{ + struct virtio_scsi_cmd_req *req = vreq->iov_req.iov_base; + struct virtio_scsi_cmd_resp *resp = vreq->iov_resp.iov_base; + uint8_t target_id = req->lun[1]; + int sk, asc, ascq; + int rc = 0; + + get_scsi_status(resp, &sk, &asc, &ascq); + + /* check response, get VPD if spun up otherwise send SSU */ + if (resp->response == VIRTIO_SCSI_S_OK && resp->status == SPDK_SCSI_STATUS_GOOD) { + send_inquiry_vpd(base, target_id, vreq, SPDK_SPC_VPD_SUPPORTED_VPD_PAGES); + } else if (resp->response == VIRTIO_SCSI_S_OK && + resp->status == SPDK_SCSI_STATUS_CHECK_CONDITION && + sk == SPDK_SCSI_SENSE_UNIT_ATTENTION && + asc == SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_READY) { + send_start_stop_unit(base, target_id, vreq); + } else { + rc = -1; + } + + return rc; +} + static int process_scan_inquiry_standard(struct virtio_scsi_scan_base *base, struct virtio_req *vreq) { struct virtio_scsi_cmd_req *req = vreq->iov_req.iov_base; struct virtio_scsi_cmd_resp *resp = vreq->iov_resp.iov_base; struct spdk_scsi_cdb_inquiry_data *inquiry_data = vreq->iov[0].iov_base; - uint8_t target_id; + uint8_t target_id = req->lun[1]; if (resp->response != VIRTIO_SCSI_S_OK || resp->status != SPDK_SCSI_STATUS_GOOD) { return -1; } + /* check to make sure its a supported device */ if (inquiry_data->peripheral_device_type != SPDK_SPC_PERIPHERAL_DEVICE_TYPE_DISK || inquiry_data->peripheral_qualifier != SPDK_SPC_PERIPHERAL_QUALIFIER_CONNECTED) { SPDK_WARNLOG("Unsupported peripheral device type 0x%02x (qualifier 0x%02x)\n", @@ -743,8 +822,7 @@ process_scan_inquiry_standard(struct virtio_scsi_scan_base *base, struct virtio_ return -1; } - target_id = req->lun[1]; - send_inquiry_vpd(base, target_id, vreq, SPDK_SPC_VPD_SUPPORTED_VPD_PAGES); + send_test_unit_ready(base, target_id, vreq); return 0; } @@ -956,6 +1034,12 @@ process_scan_resp(struct virtio_scsi_scan_base *base, struct virtio_req *vreq) case SPDK_SPC_INQUIRY: rc = process_scan_inquiry(base, vreq); break; + case SPDK_SPC_TEST_UNIT_READY: + rc = process_scan_test_unit_ready(base, vreq); + break; + case SPDK_SBC_START_STOP_UNIT: + rc = process_scan_start_stop_unit(base, vreq); + break; case SPDK_SBC_READ_CAPACITY_10: rc = process_read_cap_10(base, vreq); break;