summaryrefslogtreecommitdiffstats
path: root/hw/scsi-generic.c
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2011-04-18 12:53:14 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2011-05-26 12:14:16 +0200
commita1f0cce2ac0243572ff72aa561da67fe3766a395 (patch)
tree4680cabf1c9098c422e6c40abc2620f9f9a69cba /hw/scsi-generic.c
parent2b8b3bb9dd1c67d8296b7610dca909539e25a196 (diff)
downloadhqemu-a1f0cce2ac0243572ff72aa561da67fe3766a395.zip
hqemu-a1f0cce2ac0243572ff72aa561da67fe3766a395.tar.gz
scsi: Update sense code handling
The SCSI spec has a quite detailed list of sense codes available. It even mandates the use of specific ones for some failure cases. The current implementation just has one type of generic error which is actually a violation of the spec in certain cases. This patch introduces various predefined sense codes to have the sense code reporting more in line with the spec. On top of Hannes's patch I fixed the reply to REQUEST SENSE commands with DESC=0 and a small (<18) length. Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'hw/scsi-generic.c')
-rw-r--r--hw/scsi-generic.c63
1 files changed, 41 insertions, 22 deletions
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 229d24f..64cbe8b 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -66,6 +66,19 @@ struct SCSIGenericState
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 SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
@@ -92,9 +105,22 @@ static void scsi_command_complete(void *opaque, int ret)
if (s->driver_status & SG_ERR_DRIVER_SENSE)
s->senselen = r->io_header.sb_len_wr;
- if (ret != 0)
- r->req.status = BUSY;
- else {
+ if (ret != 0) {
+ switch (ret) {
+ case -EINVAL:
+ r->req.status = CHECK_CONDITION;
+ scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
+ break;
+ case -ENOMEM:
+ r->req.status = CHECK_CONDITION;
+ scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE));
+ break;
+ default:
+ r->req.status = CHECK_CONDITION;
+ scsi_set_sense(s, SENSE_CODE(IO_ERROR));
+ break;
+ }
+ } else {
if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
r->req.status = BUSY;
BADF("Driver Timeout\n");
@@ -144,7 +170,7 @@ static int execute_command(BlockDriverState *bdrv,
r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
if (r->req.aiocb == NULL) {
BADF("execute_command: read failed !\n");
- return -1;
+ return -ENOMEM;
}
return 0;
@@ -198,12 +224,14 @@ static void scsi_read_data(SCSIRequest *req)
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);
return;
}
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
- if (ret == -1) {
- scsi_command_complete(r, -EINVAL);
+ if (ret < 0) {
+ scsi_command_complete(r, ret);
return;
}
}
@@ -246,8 +274,8 @@ static int scsi_write_data(SCSIRequest *req)
}
ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
- if (ret == -1) {
- scsi_command_complete(r, -EINVAL);
+ if (ret < 0) {
+ scsi_command_complete(r, ret);
return 1;
}
@@ -296,16 +324,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
if (cmd[0] != REQUEST_SENSE &&
(req->lun != s->lun || (cmd[1] >> 5) != s->lun)) {
DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5);
-
- s->sensebuf[0] = 0x70;
- s->sensebuf[1] = 0x00;
- s->sensebuf[2] = ILLEGAL_REQUEST;
- s->sensebuf[3] = 0x00;
- s->sensebuf[4] = 0x00;
- s->sensebuf[5] = 0x00;
- s->sensebuf[6] = 0x00;
- s->senselen = 7;
- s->driver_status = SG_ERR_DRIVER_SENSE;
+ scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
r->req.status = CHECK_CONDITION;
scsi_req_complete(&r->req);
return 0;
@@ -313,8 +332,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
if (-1 == scsi_req_parse(&r->req, cmd)) {
BADF("Unsupported command length, command %x\n", cmd[0]);
- scsi_req_dequeue(&r->req);
- scsi_req_unref(&r->req);
+ scsi_command_complete(r, -EINVAL);
return 0;
}
scsi_req_fixup(&r->req);
@@ -338,8 +356,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
r->buflen = 0;
r->buf = NULL;
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
- if (ret == -1) {
- scsi_command_complete(r, -EINVAL);
+ if (ret < 0) {
+ scsi_command_complete(r, ret);
+ return 0;
}
return 0;
}
OpenPOWER on IntegriCloud