scsi: move host_status handling into SCSI drivers

Some SCSI drivers like virtio have an internal mapping for the
host_status. This patch moves the host_status translation into
the SCSI drivers to allow those drivers to set up the correct
values.

Signed-off-by: Hannes Reinecke <hare@suse.de>.
[Added default handling to avoid touching all drivers. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Hannes Reinecke 2021-02-24 19:14:50 +01:00 committed by Paolo Bonzini
parent a108557bbf
commit f3126d65b3
6 changed files with 128 additions and 13 deletions

View File

@ -692,6 +692,7 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
req->lun = lun;
req->hba_private = hba_private;
req->status = -1;
req->host_status = -1;
req->ops = reqops;
object_ref(OBJECT(d));
object_ref(OBJECT(qbus->parent));
@ -1455,10 +1456,38 @@ void scsi_req_print(SCSIRequest *req)
}
}
void scsi_req_complete_failed(SCSIRequest *req, int host_status)
{
SCSISense sense;
int status;
assert(req->status == -1 && req->host_status == -1);
assert(req->ops != &reqops_unit_attention);
if (!req->bus->info->fail) {
status = scsi_sense_from_host_status(req->host_status, &sense);
if (status == CHECK_CONDITION) {
scsi_req_build_sense(req, sense);
}
scsi_req_complete(req, status);
return;
}
req->host_status = host_status;
scsi_req_ref(req);
scsi_req_dequeue(req);
req->bus->info->fail(req);
/* Cancelled requests might end up being completed instead of cancelled */
notifier_list_notify(&req->cancel_notifiers, req);
scsi_req_unref(req);
}
void scsi_req_complete(SCSIRequest *req, int status)
{
assert(req->status == -1);
assert(req->status == -1 && req->host_status == -1);
req->status = status;
req->host_status = SCSI_HOST_OK;
assert(req->sense_len <= sizeof(req->sense));
if (status == GOOD) {
@ -1646,7 +1675,7 @@ static int put_scsi_requests(QEMUFile *f, void *pv, size_t size,
QTAILQ_FOREACH(req, &s->requests, next) {
assert(!req->io_canceled);
assert(req->status == -1);
assert(req->status == -1 && req->host_status == -1);
assert(req->enqueued);
qemu_put_sbyte(f, req->retry ? 1 : 2);

View File

@ -2704,15 +2704,15 @@ static void scsi_block_sgio_complete(void *opaque, int ret)
SCSIDiskReq *r = &req->req;
SCSIDevice *s = r->req.dev;
sg_io_hdr_t *io_hdr = &req->io_header;
SCSISense sense;
if (ret == 0) {
if (io_hdr->host_status != SCSI_HOST_OK) {
ret = scsi_sense_from_host_status(io_hdr->host_status, &sense);
if (ret == CHECK_CONDITION) {
scsi_req_build_sense(&r->req, sense);
scsi_req_complete_failed(&r->req, io_hdr->host_status);
scsi_req_unref(&r->req);
return;
}
} else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
ret = BUSY;
} else {
ret = io_hdr->status;

View File

@ -89,10 +89,8 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
scsi_req_build_sense(&r->req, sense);
}
} else if (io_hdr->host_status != SCSI_HOST_OK) {
status = scsi_sense_from_host_status(io_hdr->host_status, &sense);
if (status == CHECK_CONDITION) {
scsi_req_build_sense(&r->req, sense);
}
scsi_req_complete_failed(&r->req, io_hdr->host_status);
goto done;
} else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
status = BUSY;
} else {

View File

@ -500,6 +500,51 @@ static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req)
virtio_scsi_complete_req(req);
}
static void virtio_scsi_command_failed(SCSIRequest *r)
{
VirtIOSCSIReq *req = r->hba_private;
if (r->io_canceled) {
return;
}
req->resp.cmd.status = GOOD;
switch (r->host_status) {
case SCSI_HOST_NO_LUN:
req->resp.cmd.response = VIRTIO_SCSI_S_INCORRECT_LUN;
break;
case SCSI_HOST_BUSY:
req->resp.cmd.response = VIRTIO_SCSI_S_BUSY;
break;
case SCSI_HOST_TIME_OUT:
case SCSI_HOST_ABORTED:
req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED;
break;
case SCSI_HOST_BAD_RESPONSE:
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
break;
case SCSI_HOST_RESET:
req->resp.cmd.response = VIRTIO_SCSI_S_RESET;
break;
case SCSI_HOST_TRANSPORT_DISRUPTED:
req->resp.cmd.response = VIRTIO_SCSI_S_TRANSPORT_FAILURE;
break;
case SCSI_HOST_TARGET_FAILURE:
req->resp.cmd.response = VIRTIO_SCSI_S_TARGET_FAILURE;
break;
case SCSI_HOST_RESERVATION_ERROR:
req->resp.cmd.response = VIRTIO_SCSI_S_NEXUS_FAILURE;
break;
case SCSI_HOST_ALLOCATION_FAILURE:
case SCSI_HOST_MEDIUM_ERROR:
case SCSI_HOST_ERROR:
default:
req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE;
break;
}
virtio_scsi_complete_cmd_req(req);
}
static void virtio_scsi_command_complete(SCSIRequest *r, size_t resid)
{
VirtIOSCSIReq *req = r->hba_private;
@ -908,6 +953,7 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
.max_lun = VIRTIO_SCSI_MAX_LUN,
.complete = virtio_scsi_command_complete,
.fail = virtio_scsi_command_failed,
.cancel = virtio_scsi_request_cancelled,
.change = virtio_scsi_change,
.parse_cdb = virtio_scsi_parse_cdb,

View File

@ -510,6 +510,44 @@ pvscsi_write_sense(PVSCSIRequest *r, uint8_t *sense, int len)
cpu_physical_memory_write(r->req.senseAddr, sense, r->cmp.senseLen);
}
static void
pvscsi_command_failed(SCSIRequest *req)
{
PVSCSIRequest *pvscsi_req = req->hba_private;
PVSCSIState *s;
if (!pvscsi_req) {
trace_pvscsi_command_complete_not_found(req->tag);
return;
}
s = pvscsi_req->dev;
switch (req->host_status) {
case SCSI_HOST_NO_LUN:
pvscsi_req->cmp.hostStatus = BTSTAT_LUNMISMATCH;
break;
case SCSI_HOST_BUSY:
pvscsi_req->cmp.hostStatus = BTSTAT_ABORTQUEUE;
break;
case SCSI_HOST_TIME_OUT:
case SCSI_HOST_ABORTED:
pvscsi_req->cmp.hostStatus = BTSTAT_SENTRST;
break;
case SCSI_HOST_BAD_RESPONSE:
pvscsi_req->cmp.hostStatus = BTSTAT_SELTIMEO;
break;
case SCSI_HOST_RESET:
pvscsi_req->cmp.hostStatus = BTSTAT_BUSRESET;
break;
default:
pvscsi_req->cmp.hostStatus = BTSTAT_HASOFTWARE;
break;
}
pvscsi_req->cmp.scsiStatus = GOOD;
qemu_sglist_destroy(&pvscsi_req->sgl);
pvscsi_complete_request(s, pvscsi_req);
}
static void
pvscsi_command_complete(SCSIRequest *req, size_t resid)
{
@ -1103,6 +1141,7 @@ static const struct SCSIBusInfo pvscsi_scsi_info = {
.get_sg_list = pvscsi_get_sg_list,
.complete = pvscsi_command_complete,
.cancel = pvscsi_request_cancelled,
.fail = pvscsi_command_failed,
};
static void

View File

@ -27,7 +27,8 @@ struct SCSIRequest {
uint32_t refcount;
uint32_t tag;
uint32_t lun;
uint32_t status;
int16_t status;
int16_t host_status;
void *hba_private;
size_t resid;
SCSICommand cmd;
@ -123,6 +124,7 @@ struct SCSIBusInfo {
int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf,
void *hba_private);
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
void (*fail)(SCSIRequest *req);
void (*complete)(SCSIRequest *req, size_t resid);
void (*cancel)(SCSIRequest *req);
void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense);
@ -177,6 +179,7 @@ void scsi_req_print(SCSIRequest *req);
void scsi_req_continue(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req, int status);
void scsi_req_complete_failed(SCSIRequest *req, int host_status);
uint8_t *scsi_req_get_buf(SCSIRequest *req);
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
void scsi_req_cancel_complete(SCSIRequest *req);