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 | 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 | ... | ... |