Commit 89c0f6438d16ebceccdcd096bbc0b5536146a443
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,6 +84,7 @@ struct SCSIDeviceState | ||
| 84 | void *opaque; | 84 | void *opaque; |
| 85 | int driver_status; | 85 | int driver_status; |
| 86 | uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; | 86 | uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; |
| 87 | + uint8_t senselen; | ||
| 87 | }; | 88 | }; |
| 88 | 89 | ||
| 89 | /* Global pool of SCSIRequest structures. */ | 90 | /* Global pool of SCSIRequest structures. */ |
| @@ -154,25 +155,30 @@ static void scsi_command_complete(void *opaque, int ret) | @@ -154,25 +155,30 @@ static void scsi_command_complete(void *opaque, int ret) | ||
| 154 | SCSIRequest *r = (SCSIRequest *)opaque; | 155 | SCSIRequest *r = (SCSIRequest *)opaque; |
| 155 | SCSIDeviceState *s = r->dev; | 156 | SCSIDeviceState *s = r->dev; |
| 156 | uint32_t tag; | 157 | uint32_t tag; |
| 157 | - int sense; | 158 | + int status; |
| 158 | 159 | ||
| 159 | s->driver_status = r->io_header.driver_status; | 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 | if (ret != 0) | 164 | if (ret != 0) |
| 161 | - sense = HARDWARE_ERROR; | 165 | + status = BUSY << 1; |
| 162 | else { | 166 | else { |
| 163 | if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { | 167 | if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { |
| 164 | - sense = HARDWARE_ERROR; | 168 | + status = BUSY << 1; |
| 165 | BADF("Driver Timeout\n"); | 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 | else | 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 | tag = r->tag; | 179 | tag = r->tag; |
| 174 | scsi_remove_request(r); | 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 | /* Cancel a pending data transfer. */ | 184 | /* Cancel a pending data transfer. */ |
| @@ -251,6 +257,8 @@ static void scsi_read_complete(void * opaque, int ret) | @@ -251,6 +257,8 @@ static void scsi_read_complete(void * opaque, int ret) | ||
| 251 | 257 | ||
| 252 | r->len = -1; | 258 | r->len = -1; |
| 253 | s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len); | 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 | /* Read more data from scsi device into buffer. */ | 264 | /* Read more data from scsi device into buffer. */ |
| @@ -276,14 +284,17 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) | @@ -276,14 +284,17 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) | ||
| 276 | 284 | ||
| 277 | if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) | 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 | r->io_header.driver_status = 0; | 289 | r->io_header.driver_status = 0; |
| 290 | + r->io_header.status = 0; | ||
| 291 | + r->io_header.dxfer_len = s->senselen; | ||
| 282 | r->len = -1; | 292 | r->len = -1; |
| 293 | + DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, s->senselen); | ||
| 283 | DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", | 294 | DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", |
| 284 | r->buf[0], r->buf[1], r->buf[2], r->buf[3], | 295 | r->buf[0], r->buf[1], r->buf[2], r->buf[3], |
| 285 | r->buf[4], r->buf[5], r->buf[6], r->buf[7]); | 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 | return; | 298 | return; |
| 288 | } | 299 | } |
| 289 | 300 | ||
| @@ -305,6 +316,12 @@ static void scsi_write_complete(void * opaque, int ret) | @@ -305,6 +316,12 @@ static void scsi_write_complete(void * opaque, int ret) | ||
| 305 | return; | 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 | scsi_command_complete(r, ret); | 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,6 +454,9 @@ static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) | ||
| 437 | case READ_12: | 454 | case READ_12: |
| 438 | *len *= blocksize; | 455 | *len *= blocksize; |
| 439 | break; | 456 | break; |
| 457 | + case INQUIRY: | ||
| 458 | + *len = cmd[4] | (cmd[3] << 8); | ||
| 459 | + break; | ||
| 440 | } | 460 | } |
| 441 | return 0; | 461 | return 0; |
| 442 | } | 462 | } |
| @@ -519,15 +539,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, | @@ -519,15 +539,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, | ||
| 519 | SCSIRequest *r; | 539 | SCSIRequest *r; |
| 520 | int ret; | 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 | if (s->type == TYPE_TAPE) { | 542 | if (s->type == TYPE_TAPE) { |
| 532 | if (scsi_stream_length(cmd, s->blocksize, &cmdlen, &len) == -1) { | 543 | if (scsi_stream_length(cmd, s->blocksize, &cmdlen, &len) == -1) { |
| 533 | BADF("Unsupported command length, command %x\n", cmd[0]); | 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,6 +554,23 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, | ||
| 543 | DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, | 554 | DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, |
| 544 | cmd[0], len); | 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 | r = scsi_find_request(s, tag); | 574 | r = scsi_find_request(s, tag); |
| 547 | if (r) { | 575 | if (r) { |
| 548 | BADF("Tag 0x%x already in use %p\n", tag, r); | 576 | BADF("Tag 0x%x already in use %p\n", tag, r); |
| @@ -619,6 +647,43 @@ static int get_blocksize(BlockDriverState *bdrv) | @@ -619,6 +647,43 @@ static int get_blocksize(BlockDriverState *bdrv) | ||
| 619 | return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; | 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 | static void scsi_destroy(SCSIDevice *d) | 687 | static void scsi_destroy(SCSIDevice *d) |
| 623 | { | 688 | { |
| 624 | SCSIRequest *r, *n; | 689 | SCSIRequest *r, *n; |
| @@ -673,17 +738,26 @@ SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, | @@ -673,17 +738,26 @@ SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, | ||
| 673 | s->completion = completion; | 738 | s->completion = completion; |
| 674 | s->opaque = opaque; | 739 | s->opaque = opaque; |
| 675 | s->lun = scsiid.lun; | 740 | s->lun = scsiid.lun; |
| 741 | + DPRINTF("LUN %d\n", s->lun); | ||
| 676 | s->type = scsiid.scsi_type; | 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 | s->driver_status = 0; | 759 | s->driver_status = 0; |
| 679 | memset(s->sensebuf, 0, sizeof(s->sensebuf)); | 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 | /* define function to manage device */ | 762 | /* define function to manage device */ |
| 689 | 763 |