scsi: move sense handling to generic code
With this patch, sense data is stored in the generic data structures
for SCSI devices and requests. The SCSI layer takes care of storing
sense data in the SCSIDevice for the subsequent REQUEST SENSE command.
At the same time, get_sense is removed and scsi_req_get_sense can use
an entirely generic implementation.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 9637ccb..af95670 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -7,6 +7,8 @@
#include "trace.h"
static char *scsibus_get_fw_dev_path(DeviceState *dev);
+static int scsi_build_sense(uint8_t *in_buf, int in_len,
+ uint8_t *buf, int len, bool fixed);
static struct BusInfo scsi_bus_info = {
.name = "SCSI",
@@ -144,6 +146,7 @@
req->lun = lun;
req->hba_private = hba_private;
req->status = -1;
+ req->sense_len = 0;
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
return req;
}
@@ -161,11 +164,28 @@
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
{
- if (req->dev->info->get_sense) {
- return req->dev->info->get_sense(req, buf, len);
- } else {
+ assert(len >= 14);
+ if (!req->sense_len) {
return 0;
}
+ return scsi_build_sense(req->sense, req->sense_len, buf, len, true);
+}
+
+int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed)
+{
+ return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed);
+}
+
+void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
+{
+ trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag,
+ sense.key, sense.asc, sense.ascq);
+ memset(req->sense, 0, 18);
+ req->sense[0] = 0xf0;
+ req->sense[2] = sense.key;
+ req->sense[12] = sense.asc;
+ req->sense[13] = sense.ascq;
+ req->sense_len = 18;
}
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
@@ -484,14 +504,40 @@
/*
* scsi_build_sense
*
- * Build a sense buffer
+ * Convert between fixed and descriptor sense buffers
*/
-int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed)
+int scsi_build_sense(uint8_t *in_buf, int in_len,
+ uint8_t *buf, int len, bool fixed)
{
+ bool fixed_in;
+ SCSISense sense;
if (!fixed && len < 8) {
return 0;
}
+ if (in_len == 0) {
+ sense.key = NO_SENSE;
+ sense.asc = 0;
+ sense.ascq = 0;
+ } else {
+ fixed_in = (in_buf[0] & 2) == 0;
+
+ if (fixed == fixed_in) {
+ memcpy(buf, in_buf, MIN(len, in_len));
+ return MIN(len, in_len);
+ }
+
+ if (fixed_in) {
+ sense.key = in_buf[2];
+ sense.asc = in_buf[12];
+ sense.ascq = in_buf[13];
+ } else {
+ sense.key = in_buf[1];
+ sense.asc = in_buf[2];
+ sense.ascq = in_buf[3];
+ }
+ }
+
memset(buf, 0, len);
if (fixed) {
/* Return fixed format sense buffer */
@@ -686,6 +732,17 @@
{
assert(req->status == -1);
req->status = status;
+
+ assert(req->sense_len < sizeof(req->sense));
+ if (status == GOOD) {
+ req->sense_len = 0;
+ }
+
+ if (req->sense_len) {
+ memcpy(req->dev->sense, req->sense, req->sense_len);
+ }
+ req->dev->sense_len = req->sense_len;
+
scsi_req_ref(req);
scsi_req_dequeue(req);
req->bus->ops->complete(req, req->status);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 38ebe04..e266d6f 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -71,7 +71,6 @@
QEMUBH *bh;
char *version;
char *serial;
- SCSISense sense;
};
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
@@ -97,20 +96,13 @@
qemu_vfree(r->iov.iov_base);
}
-static void scsi_disk_clear_sense(SCSIDiskState *s)
+/* Helper function for command completion with sense. */
+static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense)
{
- memset(&s->sense, 0, sizeof(s->sense));
-}
-
-/* Helper function for command completion. */
-static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-
DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n",
r->req.tag, status, sense.key, sense.asc, sense.ascq);
- s->sense = sense;
- scsi_req_complete(&r->req, status);
+ scsi_req_build_sense(&r->req, sense);
+ scsi_req_complete(&r->req, CHECK_CONDITION);
}
/* Cancel a pending data transfer. */
@@ -162,7 +154,8 @@
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
if (r->sector_count == 0) {
- scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
+ /* This also clears the sense buffer for REQUEST SENSE. */
+ scsi_req_complete(&r->req, GOOD);
return;
}
@@ -210,16 +203,13 @@
} else {
switch (error) {
case ENOMEM:
- scsi_command_complete(r, CHECK_CONDITION,
- SENSE_CODE(TARGET_FAILURE));
+ scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE));
break;
case EINVAL:
- scsi_command_complete(r, CHECK_CONDITION,
- SENSE_CODE(INVALID_FIELD));
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
break;
default:
- scsi_command_complete(r, CHECK_CONDITION,
- SENSE_CODE(IO_ERROR));
+ scsi_check_condition(r, SENSE_CODE(IO_ERROR));
break;
}
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
@@ -245,7 +235,7 @@
r->sector += n;
r->sector_count -= n;
if (r->sector_count == 0) {
- scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
+ scsi_req_complete(&r->req, GOOD);
} else {
len = r->sector_count * 512;
if (len > SCSI_DMA_BUF_SIZE) {
@@ -314,7 +304,7 @@
case SCSI_REQ_STATUS_RETRY_FLUSH:
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
if (ret == 0) {
- scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
+ scsi_req_complete(&r->req, GOOD);
}
}
}
@@ -342,14 +332,6 @@
return (uint8_t *)r->iov.iov_base;
}
-/* Copy sense information into the provided buffer */
-static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-
- return scsi_build_sense(s->sense, outbuf, len, len > 14);
-}
-
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
@@ -827,9 +809,8 @@
case REQUEST_SENSE:
if (req->cmd.xfer < 4)
goto illegal_request;
- buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer,
- req->cmd.xfer > 13);
- scsi_disk_clear_sense(s);
+ buflen = scsi_device_get_sense(&s->qdev, outbuf, req->cmd.xfer,
+ (req->cmd.buf[1] & 1) == 0);
break;
case INQUIRY:
buflen = scsi_disk_emulate_inquiry(req, outbuf);
@@ -960,21 +941,21 @@
case VERIFY_10:
break;
default:
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return -1;
}
return buflen;
not_ready:
if (!bdrv_is_inserted(s->bs)) {
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM));
+ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
} else {
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY));
+ scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
}
return -1;
illegal_request:
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
return -1;
}
@@ -998,7 +979,7 @@
if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command);
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return 0;
}
#ifdef DEBUG_SCSI
@@ -1015,8 +996,7 @@
/* Only LUN 0 supported. */
DPRINTF("Unimplemented LUN %d\n", req->lun);
if (command != REQUEST_SENSE && command != INQUIRY) {
- scsi_command_complete(r, CHECK_CONDITION,
- SENSE_CODE(LUN_NOT_SUPPORTED));
+ scsi_check_condition(r, SENSE_CODE(LUN_NOT_SUPPORTED));
return 0;
}
}
@@ -1124,17 +1104,17 @@
break;
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return 0;
fail:
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
return 0;
illegal_lba:
- scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE));
+ scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
return 0;
}
if (r->sector_count == 0 && r->iov.iov_len == 0) {
- scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
+ scsi_req_complete(&r->req, GOOD);
}
len = r->sector_count * 512 + r->iov.iov_len;
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
@@ -1266,7 +1246,6 @@
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
- .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
@@ -1287,7 +1266,6 @@
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
- .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_END_OF_LIST(),
@@ -1307,7 +1285,6 @@
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
- .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 71cbfb0..37c5982 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -61,41 +61,8 @@
SCSIDevice qdev;
BlockDriverState *bs;
int lun;
- int driver_status;
- uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
- uint8_t senselen;
};
-static void scsi_set_sense(SCSIGenericState *s, SCSISense sense)
-{
- s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0);
- s->driver_status = SG_ERR_DRIVER_SENSE;
-}
-
-static void scsi_clear_sense(SCSIGenericState *s)
-{
- memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE);
- s->senselen = 0;
- s->driver_status = 0;
-}
-
-static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
-{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
- int size = SCSI_SENSE_BUF_SIZE;
-
- if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) {
- size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf,
- SCSI_SENSE_BUF_SIZE, 0);
- }
- if (size > len) {
- size = len;
- }
- memcpy(outbuf, s->sensebuf, size);
-
- return size;
-}
-
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
void *hba_private)
{
@@ -117,12 +84,10 @@
{
int status;
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
r->req.aiocb = NULL;
- s->driver_status = r->io_header.driver_status;
- if (s->driver_status & SG_ERR_DRIVER_SENSE)
- s->senselen = r->io_header.sb_len_wr;
+ if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE)
+ r->req.sense_len = r->io_header.sb_len_wr;
if (ret != 0) {
switch (ret) {
@@ -131,24 +96,24 @@
break;
case -EINVAL:
status = CHECK_CONDITION;
- scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
+ scsi_req_build_sense(&r->req, SENSE_CODE(INVALID_FIELD));
break;
case -ENOMEM:
status = CHECK_CONDITION;
- scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE));
+ scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
break;
default:
status = CHECK_CONDITION;
- scsi_set_sense(s, SENSE_CODE(IO_ERROR));
+ scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
break;
}
} else {
- if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
+ if (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) {
status = BUSY;
BADF("Driver Timeout\n");
} else if (r->io_header.status) {
status = r->io_header.status;
- } else if (s->driver_status & SG_ERR_DRIVER_SENSE) {
+ } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
status = CHECK_CONDITION;
} else {
status = GOOD;
@@ -176,16 +141,14 @@
SCSIGenericReq *r, int direction,
BlockDriverCompletionFunc *complete)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
-
r->io_header.interface_id = 'S';
r->io_header.dxfer_direction = direction;
r->io_header.dxferp = r->buf;
r->io_header.dxfer_len = r->buflen;
r->io_header.cmdp = r->req.cmd.buf;
r->io_header.cmd_len = r->req.cmd.len;
- r->io_header.mx_sb_len = sizeof(s->sensebuf);
- r->io_header.sbp = s->sensebuf;
+ r->io_header.mx_sb_len = sizeof(r->req.sense);
+ r->io_header.sbp = r->req.sense;
r->io_header.timeout = MAX_UINT;
r->io_header.usr_ptr = r;
r->io_header.flags |= SG_FLAG_DIRECT_IO;
@@ -234,21 +197,19 @@
return;
}
- if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
- {
- s->senselen = MIN(r->len, s->senselen);
- memcpy(r->buf, s->sensebuf, s->senselen);
+ if (r->req.cmd.buf[0] == REQUEST_SENSE) {
r->io_header.driver_status = 0;
r->io_header.status = 0;
- r->io_header.dxfer_len = s->senselen;
+ r->io_header.dxfer_len =
+ scsi_device_get_sense(&s->qdev, r->buf, r->req.cmd.xfer,
+ (r->req.cmd.buf[1] & 1) == 0);
r->len = -1;
- DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen);
+ DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, r->io_header.dxfer_len);
DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
r->buf[0], r->buf[1], r->buf[2], r->buf[3],
r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
- scsi_req_data(&r->req, s->senselen);
- /* Clear sensebuf after REQUEST_SENSE */
- scsi_clear_sense(s);
+ scsi_req_data(&r->req, r->io_header.dxfer_len);
+ /* The sense buffer is cleared when we return GOOD */
return;
}
@@ -342,7 +303,7 @@
if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) {
DPRINTF("Unimplemented LUN %d\n", req->lun);
- scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
+ scsi_req_build_sense(&r->req, SENSE_CODE(LUN_NOT_SUPPORTED));
scsi_req_complete(&r->req, CHECK_CONDITION);
return 0;
}
@@ -533,8 +494,6 @@
}
}
DPRINTF("block size %d\n", s->qdev.blocksize);
- s->driver_status = 0;
- memset(s->sensebuf, 0, sizeof(s->sensebuf));
bdrv_set_removable(s->bs, 0);
return 0;
}
@@ -553,7 +512,6 @@
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
- .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/scsi.h b/hw/scsi.h
index 18d3643..c1cb987 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -27,6 +27,8 @@
uint8_t ascq;
} SCSISense;
+#define SCSI_SENSE_BUF_SIZE 96
+
struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
@@ -42,6 +44,8 @@
enum SCSIXferMode mode;
} cmd;
BlockDriverAIOCB *aiocb;
+ uint8_t sense[SCSI_SENSE_BUF_SIZE];
+ uint32_t sense_len;
bool enqueued;
void *hba_private;
QTAILQ_ENTRY(SCSIRequest) next;
@@ -53,6 +57,8 @@
uint32_t id;
BlockConf conf;
SCSIDeviceInfo *info;
+ uint8_t sense[SCSI_SENSE_BUF_SIZE];
+ uint32_t sense_len;
QTAILQ_HEAD(, SCSIRequest) requests;
int blocksize;
int type;
@@ -76,7 +82,6 @@
void (*write_data)(SCSIRequest *req);
void (*cancel_io)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
- int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len);
};
struct SCSIBusOps {
@@ -137,7 +142,6 @@
#define SENSE_CODE(x) sense_code_ ## x
-int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag,
@@ -149,6 +153,7 @@
SCSIRequest *scsi_req_ref(SCSIRequest *req);
void scsi_req_unref(SCSIRequest *req);
+void scsi_req_build_sense(SCSIRequest *req, SCSISense sense);
int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
void scsi_req_continue(SCSIRequest *req);
@@ -159,5 +164,6 @@
void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev);
+int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
#endif
diff --git a/trace-events b/trace-events
index 19d31e3..0daf2cc 100644
--- a/trace-events
+++ b/trace-events
@@ -251,6 +251,7 @@
disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d"
disable scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64""
disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
+disable scsi_req_build_sense(int target, int lun, int tag, int key, int asc, int ascq) "target %d lun %d tag %d key %#02x asc %#02x ascq %#02x"
# vl.c
disable vm_state_notify(int running, int reason) "running %d reason %d"