Commit 89c0f6438d16ebceccdcd096bbc0b5536146a443

Authored by aurel32
1 parent e65bdffa

scsi-generic: correct error management

this patch allows to fully use a tape device connected to qemu through
the scsi-generic interface.

Previous patch introduced tape SCSI commands management, this one
improve error case management:

- the SCSI controller command completion must be called with the status
value, not the sense value. In the case of scsi-generic, the SCSI status
is given by the field status of sg_io_hdr_t (the value is left shifted
by one regarding status codes defined in /usr/include/scsi/scsi.h)

- when a read is aborted due to a mark/EOF/EOD/EOM, the len reported to
controller can be 0. LSI controller emulation doesn't know how to manage
this. A workaround found is to call the completion routine with
SCSI_REASON_DONE just after calling it with SCSI_REASON_DATA with len=0.

This patch also manages correctly the block size of the tape device.

This patch has been tested with a real tape device "HP C5683A", linux
guest (debian etch) and tools like "mt", "tar" and "btape".

Windows guest is not better supported than before...

Signed-off-by: Laurent Vivier <Laurent.Vivier@bull.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5497 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 103 additions and 29 deletions
hw/scsi-generic.c
... ... @@ -84,6 +84,7 @@ struct SCSIDeviceState
84 84 void *opaque;
85 85 int driver_status;
86 86 uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
  87 + uint8_t senselen;
87 88 };
88 89  
89 90 /* Global pool of SCSIRequest structures. */
... ... @@ -154,25 +155,30 @@ static void scsi_command_complete(void *opaque, int ret)
154 155 SCSIRequest *r = (SCSIRequest *)opaque;
155 156 SCSIDeviceState *s = r->dev;
156 157 uint32_t tag;
157   - int sense;
  158 + int status;
158 159  
159 160 s->driver_status = r->io_header.driver_status;
  161 + if (s->driver_status & SG_ERR_DRIVER_SENSE)
  162 + s->senselen = r->io_header.sb_len_wr;
  163 +
160 164 if (ret != 0)
161   - sense = HARDWARE_ERROR;
  165 + status = BUSY << 1;
162 166 else {
163 167 if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
164   - sense = HARDWARE_ERROR;
  168 + status = BUSY << 1;
165 169 BADF("Driver Timeout\n");
166   - } else if ((s->driver_status & SG_ERR_DRIVER_SENSE) == 0)
167   - sense = NO_SENSE;
  170 + } else if (r->io_header.status)
  171 + status = r->io_header.status;
  172 + else if (s->driver_status & SG_ERR_DRIVER_SENSE)
  173 + status = CHECK_CONDITION << 1;
168 174 else
169   - sense = s->sensebuf[2];
  175 + status = GOOD << 1;
170 176 }
171   -
172   - DPRINTF("Command complete 0x%p tag=0x%x sense=%d\n", r, r->tag, sense);
  177 + DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
  178 + r, r->tag, status);
173 179 tag = r->tag;
174 180 scsi_remove_request(r);
175   - s->completion(s->opaque, SCSI_REASON_DONE, tag, sense);
  181 + s->completion(s->opaque, SCSI_REASON_DONE, tag, status);
176 182 }
177 183  
178 184 /* Cancel a pending data transfer. */
... ... @@ -251,6 +257,8 @@ static void scsi_read_complete(void * opaque, int ret)
251 257  
252 258 r->len = -1;
253 259 s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
  260 + if (len == 0)
  261 + scsi_command_complete(r, 0);
254 262 }
255 263  
256 264 /* Read more data from scsi device into buffer. */
... ... @@ -276,14 +284,17 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
276 284  
277 285 if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
278 286 {
279   - int len = MIN(r->len, SCSI_SENSE_BUF_SIZE);
280   - memcpy(r->buf, s->sensebuf, len);
  287 + s->senselen = MIN(r->len, s->senselen);
  288 + memcpy(r->buf, s->sensebuf, s->senselen);
281 289 r->io_header.driver_status = 0;
  290 + r->io_header.status = 0;
  291 + r->io_header.dxfer_len = s->senselen;
282 292 r->len = -1;
  293 + DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, s->senselen);
283 294 DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
284 295 r->buf[0], r->buf[1], r->buf[2], r->buf[3],
285 296 r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
286   - s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
  297 + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, s->senselen);
287 298 return;
288 299 }
289 300  
... ... @@ -305,6 +316,12 @@ static void scsi_write_complete(void * opaque, int ret)
305 316 return;
306 317 }
307 318  
  319 + if (r->cmd[0] == MODE_SELECT && r->cmd[4] == 12 &&
  320 + r->dev->type == TYPE_TAPE) {
  321 + r->dev->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
  322 + DPRINTF("block size %d\n", r->dev->blocksize);
  323 + }
  324 +
308 325 scsi_command_complete(r, ret);
309 326 }
310 327  
... ... @@ -437,6 +454,9 @@ static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len)
437 454 case READ_12:
438 455 *len *= blocksize;
439 456 break;
  457 + case INQUIRY:
  458 + *len = cmd[4] | (cmd[3] << 8);
  459 + break;
440 460 }
441 461 return 0;
442 462 }
... ... @@ -519,15 +539,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
519 539 SCSIRequest *r;
520 540 int ret;
521 541  
522   - /* ??? Tags are not unique for different luns. We only implement a
523   - single lun, so this should not matter. */
524   -
525   - if (lun != s->lun || (cmd[1] >> 5) != s->lun) {
526   - DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
527   - s->completion(s->opaque, SCSI_REASON_DONE, tag, ILLEGAL_REQUEST);
528   - return 0;
529   - }
530   -
531 542 if (s->type == TYPE_TAPE) {
532 543 if (scsi_stream_length(cmd, s->blocksize, &cmdlen, &len) == -1) {
533 544 BADF("Unsupported command length, command %x\n", cmd[0]);
... ... @@ -543,6 +554,23 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
543 554 DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag,
544 555 cmd[0], len);
545 556  
  557 + if (cmd[0] != REQUEST_SENSE &&
  558 + (lun != s->lun || (cmd[1] >> 5) != s->lun)) {
  559 + DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
  560 +
  561 + s->sensebuf[0] = 0x70;
  562 + s->sensebuf[1] = 0x00;
  563 + s->sensebuf[2] = ILLEGAL_REQUEST;
  564 + s->sensebuf[3] = 0x00;
  565 + s->sensebuf[4] = 0x00;
  566 + s->sensebuf[5] = 0x00;
  567 + s->sensebuf[6] = 0x00;
  568 + s->senselen = 7;
  569 + s->driver_status = SG_ERR_DRIVER_SENSE;
  570 + s->completion(s->opaque, SCSI_REASON_DONE, tag, CHECK_CONDITION << 1);
  571 + return 0;
  572 + }
  573 +
546 574 r = scsi_find_request(s, tag);
547 575 if (r) {
548 576 BADF("Tag 0x%x already in use %p\n", tag, r);
... ... @@ -619,6 +647,43 @@ static int get_blocksize(BlockDriverState *bdrv)
619 647 return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
620 648 }
621 649  
  650 +static int get_stream_blocksize(BlockDriverState *bdrv)
  651 +{
  652 + uint8_t cmd[6];
  653 + uint8_t buf[12];
  654 + uint8_t sensebuf[8];
  655 + sg_io_hdr_t io_header;
  656 + int ret;
  657 +
  658 + memset(cmd, 0, sizeof(cmd));
  659 + memset(buf, 0, sizeof(buf));
  660 + cmd[0] = MODE_SENSE;
  661 + cmd[4] = sizeof(buf);
  662 +
  663 + memset(&io_header, 0, sizeof(io_header));
  664 + io_header.interface_id = 'S';
  665 + io_header.dxfer_direction = SG_DXFER_FROM_DEV;
  666 + io_header.dxfer_len = sizeof(buf);
  667 + io_header.dxferp = buf;
  668 + io_header.cmdp = cmd;
  669 + io_header.cmd_len = sizeof(cmd);
  670 + io_header.mx_sb_len = sizeof(sensebuf);
  671 + io_header.sbp = sensebuf;
  672 + io_header.timeout = 6000; /* XXX */
  673 +
  674 + ret = bdrv_pwrite(bdrv, -1, &io_header, sizeof(io_header));
  675 + if (ret == -1)
  676 + return -1;
  677 +
  678 + while ((ret = bdrv_pread(bdrv, -1, &io_header, sizeof(io_header))) == -1 &&
  679 + errno == EINTR);
  680 +
  681 + if (ret == -1)
  682 + return -1;
  683 +
  684 + return (buf[9] << 16) | (buf[10] << 8) | buf[11];
  685 +}
  686 +
622 687 static void scsi_destroy(SCSIDevice *d)
623 688 {
624 689 SCSIRequest *r, *n;
... ... @@ -673,17 +738,26 @@ SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
673 738 s->completion = completion;
674 739 s->opaque = opaque;
675 740 s->lun = scsiid.lun;
  741 + DPRINTF("LUN %d\n", s->lun);
676 742 s->type = scsiid.scsi_type;
677   - s->blocksize = get_blocksize(s->bdrv);
  743 + DPRINTF("device type %d\n", s->type);
  744 + if (s->type == TYPE_TAPE) {
  745 + s->blocksize = get_stream_blocksize(s->bdrv);
  746 + if (s->blocksize == -1)
  747 + s->blocksize = 0;
  748 + } else {
  749 + s->blocksize = get_blocksize(s->bdrv);
  750 + /* removable media returns 0 if not present */
  751 + if (s->blocksize <= 0) {
  752 + if (s->type == TYPE_ROM || s->type == TYPE_WORM)
  753 + s->blocksize = 2048;
  754 + else
  755 + s->blocksize = 512;
  756 + }
  757 + }
  758 + DPRINTF("block size %d\n", s->blocksize);
678 759 s->driver_status = 0;
679 760 memset(s->sensebuf, 0, sizeof(s->sensebuf));
680   - /* removable media returns 0 if not present */
681   - if (s->blocksize <= 0) {
682   - if (s->type == TYPE_ROM || s->type == TYPE_WORM)
683   - s->blocksize = 2048;
684   - else
685   - s->blocksize = 512;
686   - }
687 761  
688 762 /* define function to manage device */
689 763  
... ...