scsi-generic: correct error management
PATCH 1/1 scsi-generic: correct error management
Hi,
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...
--- hw/scsi-generic.c | 94 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 18 deletions(-)
hw/scsi-generic.c +76 -18
@@ -154,25 +154,26 @@ static void scsi_command_complete(void *SCSIRequest *r = (SCSIRequest *)opaque;SCSIDeviceState *s = r->dev;uint32_t tag; -int sense; - +int status; s->driver_status = r->io_header.driver_status;if (ret !=0 ) - sense =HARDWARE_ERROR ; + status =BUSY <<1 ;else {if (s->driver_status &SG_ERR_DRIVER_TIMEOUT ) { - sense =HARDWARE_ERROR ; + status =BUSY <<1 ;BADF ("Driver Timeout\n" ); - }else if ((s->driver_status &SG_ERR_DRIVER_SENSE ) ==0 ) - sense =NO_SENSE ; + }else if (r->io_header.status) + status = r->io_header.status; +else if (s->driver_status &SG_ERR_DRIVER_SENSE ) + status =CHECK_CONDITION <<1 ;else - sense = s->sensebuf[2 ]; + status =GOOD <<1 ; } - -DPRINTF ("Command complete 0x%p tag=0x%x sense=%d\n" , r, r->tag, sense); +DPRINTF ("Command complete 0x%p tag=0x%x status=%d\n" , + r, r->tag, status); tag = r->tag;scsi_remove_request (r); - s->completion (s->opaque,SCSI_REASON_DONE , tag, sense); + s->completion (s->opaque,SCSI_REASON_DONE , tag, status); }/* Cancel a pending data transfer. */
@@ -251,6 +252,8 @@ static void scsi_read_complete(void * op r->len =-1 ; s->completion (s->opaque,SCSI_REASON_DATA , r->tag, len); +if (len ==0 ) +scsi_command_complete (r,0 ); }/* Read more data from scsi device into buffer. */
@@ -305,6 +308,12 @@ static void scsi_write_complete(void * oreturn ; } +if (r->cmd[0 ] ==MODE_SELECT && r->cmd[4 ] ==12 && + r->dev->type ==TYPE_TAPE ) { + r->dev->blocksize = (r->buf[9 ] <<16 ) | (r->buf[10 ] <<8 ) | r->buf[11 ]; +DPRINTF ("block size %d\n" , r->dev->blocksize); + } +scsi_command_complete (r, ret); }
@@ -437,6 +446,9 @@ static int scsi_length(uint8_t *cmd, intcase READ_12 : *len *= blocksize;break ; +case INQUIRY : + *len = cmd[4 ] | (cmd[3 ] <<8 ); +break ; }return 0 ; }
@@ -619,6 +631,43 @@ static int get_blocksize(BlockDriverStatreturn (buf[4 ] <<24 ) | (buf[5 ] <<16 ) | (buf[6 ] <<8 ) | buf[7 ]; } +static int get_stream_blocksize (BlockDriverState *bdrv) +{ +uint8_t cmd[6 ]; +uint8_t buf[12 ]; +uint8_t sensebuf[8 ]; +sg_io_hdr_t io_header; +int ret; + +memset (cmd,0 ,sizeof (cmd)); +memset (buf,0 ,sizeof (buf)); + cmd[0 ] =MODE_SENSE ; + cmd[4 ] =sizeof (buf); + +memset (&io_header,0 ,sizeof (io_header)); + io_header.interface_id ='S' ; + io_header.dxfer_direction =SG_DXFER_FROM_DEV ; + io_header.dxfer_len =sizeof (buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len =sizeof (cmd); + io_header.mx_sb_len =sizeof (sensebuf); + io_header.sbp = sensebuf; + io_header.timeout =6000 ;/* XXX */ + + ret =bdrv_pwrite (bdrv,-1 , &io_header,sizeof (io_header)); +if (ret ==-1 ) +return -1 ; + +while ((ret =bdrv_pread (bdrv,-1 , &io_header,sizeof (io_header))) ==-1 && + errno ==EINTR ); + +if (ret ==-1 ) +return -1 ; + +return (buf[9 ] <<16 ) | (buf[10 ] <<8 ) | buf[11 ]; +} +static void scsi_destroy (SCSIDevice *d) {SCSIRequest *r, *n;
@@ -673,17 +722,26 @@ SCSIDevice *scsi_generic_init(BlockDrive s->completion = completion; s->opaque = opaque; s->lun = scsiid.lun; +DPRINTF ("LUN %d\n" , s->lun); s->type = scsiid.scsi_type; - s->blocksize =get_blocksize (s->bdrv); +DPRINTF ("device type %d\n" , s->type); +if (s->type ==TYPE_TAPE ) { + s->blocksize =get_stream_blocksize (s->bdrv); +if (s->blocksize ==-1 ) + s->blocksize =0 ; + }else { + s->blocksize =get_blocksize (s->bdrv); +/* removable media returns 0 if not present */ +if (s->blocksize <=0 ) { +if (s->type ==TYPE_ROM || s->type ==TYPE_WORM ) + s->blocksize =2048 ; +else + s->blocksize =512 ; + } + } +DPRINTF ("block size %d\n" , s->blocksize); s->driver_status =0 ;memset (s->sensebuf,0 ,sizeof (s->sensebuf)); -/* removable media returns 0 if not present */ -if (s->blocksize <=0 ) { -if (s->type ==TYPE_ROM || s->type ==TYPE_WORM ) - s->blocksize =2048 ; -else - s->blocksize =512 ; - }/* define function to manage device */