Real SCSI device passthrough (v5)
cover Real SCSI device passthrough (v5)
This series of patches allows to connect real SCSI device to the virtual SCSI controller of Qemu using the SCSI Generic interface (/dev/sg)
for instance:
qemu -hda my_disk.qcow2 -drive file=/dev/sg3,if=scsi
[PATCH 1/2] Real SCSI device passthrough (v5)
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host.
[PATCH 2/2] Real SCSI device DMA split (v5)
This patch allows to split a READ or WRITE into several READ or WRITE.
Laurent
PATCH 1/2 Real SCSI device passthrough (v5)
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host.
Using the devices /dev/sg, it allows to send the SCSI commands from the virtual SCSI interface to the real device.
It has been tested with a SATA disk and an ATA CD burner with ide-scsi module and wodim to burn a CD-RW.
v5 allows to read movies from a DVD. It has been tested with PowerDVD and XP with unprotected DVD.
Laurent
--- Makefile | 1 block-raw-posix.c | 27 +- block.c | 16 + block.h | 2 block_int.h | 4 hw/esp.c | 4 hw/lsi53c895a.c | 8 hw/scsi-disk.h | 2 hw/scsi-generic.c | 653 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 710 insertions(+), 7 deletions(-)
Makefile +1 -0
@@ -56,6 +56,7 @@ OBJS+=irq.o OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o OBJS+=scsi-disk.o cdrom.o +OBJS+=scsi-generic.o OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o OBJS+=sd.o ssi-sd.o
block.c +16 -0
@@ -786,6 +786,11 @@ int bdrv_is_read_only(BlockDriverState *return bs->read_only; } +int bdrv_is_sg (BlockDriverState *bs) +{ +return bs->sg; +} +/* XXX: no longer used */ void bdrv_set_change_cb (BlockDriverState *bs,void (*change_cb)(void *opaque),void *opaque)
@@ -1394,3 +1399,14 @@ void bdrv_set_locked(BlockDriverState *b drv->bdrv_set_locked (bs, locked); } } + +/* needed for generic scsi interface */ + +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BlockDriver *drv = bs->drv; + +if (drv && drv->bdrv_ioctl) +return drv->bdrv_ioctl (bs, req, buf); +return -ENOTSUP ; +}
block.h +2 -0
@@ -119,6 +119,7 @@ int bdrv_get_type_hint(BlockDriverStateint bdrv_get_translation_hint (BlockDriverState *bs);int bdrv_is_removable (BlockDriverState *bs);int bdrv_is_read_only (BlockDriverState *bs); +int bdrv_is_sg (BlockDriverState *bs);int bdrv_is_inserted (BlockDriverState *bs);int bdrv_media_changed (BlockDriverState *bs);int bdrv_is_locked (BlockDriverState *bs);
@@ -148,6 +149,7 @@ int bdrv_snapshot_delete(BlockDriverStatint bdrv_snapshot_list (BlockDriverState *bs,QEMUSnapshotInfo **psn_info);char *bdrv_snapshot_dump (char *buf,int buf_size,QEMUSnapshotInfo *sn); +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf);char *get_human_readable_size (char *buf,int buf_size,int64_t size);int path_is_absolute (const char *path);
block_int.h +4 -0
@@ -82,6 +82,9 @@ struct BlockDriver {int (*bdrv_eject)(BlockDriverState *bs,int eject_flag);int (*bdrv_set_locked)(BlockDriverState *bs,int locked); +/* to control generic scsi devices */ +int (*bdrv_ioctl)(BlockDriverState *bs,unsigned long int req,char *buf); +BlockDriverAIOCB *free_aiocb;struct BlockDriver *next; };
@@ -93,6 +96,7 @@ struct BlockDriverState {int removable;/* if true, the media can be removed */ int locked;/* if true, the media cannot temporarily be ejected */ int encrypted;/* if true, the media is encrypted */ +int sg;/* if true, the device is a /dev/sg* */ /* event callback when inserting/removing */ void (*change_cb)(void *opaque);void *change_opaque;
hw/esp.c +3 -1
@@ -615,7 +615,9 @@ void esp_scsi_attach(void *opaque, Block }DPRINTF ("Attaching block device %d\n" , id);/* Command queueing is not implemented. */ - s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,0 , esp_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); } void *esp_init(target_phys_addr_t espaddr,
hw/lsi53c895a.c +7 -1
@@ -1236,6 +1236,8 @@ static uint8_t lsi_reg_readb(LSIState *sreturn s->sdid;case 0x07 :/* GPREG0 */ return 0x7f ; +case 0x08 :/* Revision ID */ +return 0x00 ;case 0xa :/* SSID */ return s->ssid;case 0xb :/* SBCL */
@@ -1281,6 +1283,8 @@ static uint8_t lsi_reg_readb(LSIState *sreturn s->ctest4;case 0x22 :/* CTEST5 */ return s->ctest5; +case 0x23 :/* CTEST6 */ +return 0 ;case 0x24 :/* DBC[0:7] */ return s->dbc &0xff ;case 0x25 :/* DBC[8:15] */
@@ -1838,7 +1842,9 @@ void lsi_scsi_attach(void *opaque, Block s->scsi_dev[id]->destroy (s->scsi_dev[id]); }DPRINTF ("Attaching block device %d\n" , id); - s->scsi_dev[id] =scsi_disk_init (bd,1 , lsi_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,1 , lsi_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,1 , lsi_command_complete, s); }void *lsi_scsi_init (PCIBus *bus,int devfn)
hw/scsi-disk.h +2 -0
@@ -26,6 +26,8 @@ struct SCSIDeviceSCSIDevice *scsi_disk_init (BlockDriverState *bdrv,int tcq,scsi_completionfn completion,void *opaque); +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque);/* cdrom.c */ int cdrom_read_toc (int nb_sectors,uint8_t *buf,int msf,int start_track);
hw/scsi-generic.c new file +653 -0
653 lines not on this page · raw message
block-raw-posix.c +22 -5
@@ -151,7 +151,7 @@ static int raw_pread(BlockDriverState *bif (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt <=10 ) { DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
@@ -204,7 +204,7 @@ static int raw_pwrite(BlockDriverState *if (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt) { DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%"
@@ -276,8 +276,8 @@ void qemu_aio_init(void)seems to fix the problem. */ struct aioinit ai;memset (&ai,0 ,sizeof (ai)); - ai.aio_threads =1 ; - ai.aio_num =1 ; + ai.aio_threads =16 ; + ai.aio_num =16 ; ai.aio_idle_time =365 *100000 ;aio_init (&ai); }
@@ -387,7 +387,10 @@ static RawAIOCB *raw_aio_setup(BlockDriv acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num; acb->aiocb.aio_sigevent.sigev_notify =SIGEV_SIGNAL ; acb->aiocb.aio_buf = buf; - acb->aiocb.aio_nbytes = nb_sectors *512 ; +if (nb_sectors <0 ) + acb->aiocb.aio_nbytes = -nb_sectors; +else + acb->aiocb.aio_nbytes = nb_sectors *512 ; acb->aiocb.aio_offset = sector_num *512 ; acb->next = first_aio; first_aio = acb;
@@ -679,6 +682,8 @@ static int hdev_open(BlockDriverState *b s->fd_open_flags = open_flags;/* open will not fail even if no floppy is inserted */ open_flags |= O_NONBLOCK; + }else if (strstart (filename,"/dev/sg" ,NULL )) { + bs->sg =1 ; }#endif fd =open (filename, open_flags,0644 );
@@ -858,6 +863,12 @@ static int raw_set_locked(BlockDriverSta return 0; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BDRVRawState *s = bs->opaque; + +return ioctl (s->fd, req, buf); +}#else static int raw_is_inserted (BlockDriverState *bs)
@@ -880,6 +891,10 @@ static int raw_set_locked(BlockDriverStareturn -ENOTSUP ; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +return -ENOTSUP ; +}#endif /* !linux */ BlockDriver bdrv_host_device = {
@@ -906,4 +921,6 @@ BlockDriver bdrv_host_device = { .bdrv_media_changed = raw_media_changed, .bdrv_eject = raw_eject, .bdrv_set_locked = raw_set_locked, + /* generic scsi device */ + .bdrv_ioctl = raw_ioctl, };
PATCH 2/2 Real SCSI device DMA split (v5)
With some emulated SCSI devices, like usb-storage or ide-scsi, DMA transfers are limited to 64 kiB or 32 kiB.
This patch allows to split a READ or WRITE into several READ or WRITE.
Laurent
--- hw/scsi-generic.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 4 deletions(-)
hw/scsi-generic.c +100 -4
@@ -61,6 +61,8 @@ do { fprintf(stderr, "scsi-generic: " fm#define MAX_UINT ((unsigned int)-1)#endif +#define MAX_CHUNK 65536 + typedef struct SCSIRequest { BlockDriverAIOCB *aiocb; struct SCSIRequest *next;
@@ -72,6 +74,8 @@ typedef struct SCSIRequest {int buflen;int len;sg_io_hdr_t io_header; +int remaining; +int offset; } SCSIRequest;struct SCSIDeviceState
@@ -84,6 +88,7 @@ struct SCSIDeviceStatevoid *opaque;int driver_status;uint8_t sensebuf[SCSI_SENSE_BUF_SIZE ]; +int max_chunk; };/* Global pool of SCSIRequest structures. */
@@ -101,6 +106,7 @@ static SCSIRequest *scsi_new_request(SCS r->buf =NULL ; r->buflen =0 ; } + r->offset =0 ; r->dev = s; r->tag = tag;memset (r->cmd,0 ,sizeof (r->cmd));
@@ -191,24 +197,90 @@ static void scsi_cancel_io(SCSIDevice *d } } +static void scsi_cmd_next (uint8_t *cmd,uint32_t inc) +{ +uint32_t addr; +switch (cmd[0 ] >>5 ) { +case 0 : + addr = cmd[3 ] | (cmd[2 ] <<8 ); + addr += inc; + cmd[2 ] = addr >>8 ; + cmd[3 ] = addr; +break ; +case 1 : +case 2 : +case 4 : +case 5 : + addr = cmd[5 ] | ((cmd[4 ] <<8 ) | ((cmd[3 ] <<16 ) | (cmd[2 ] <<24 ))); + addr += inc; + cmd[2 ] = addr >>24 ; + cmd[3 ] = addr >>16 ; + cmd[4 ] = addr >>8 ; + cmd[5 ] = addr; +break ; + } +} +static void scsi_set_length (uint8_t *cmd,uint32_t len) +{ +switch (cmd[0 ] >>5 ) { +case 0 : + cmd[4 ] = len; +break ; +case 1 : +case 2 : + cmd[7 ] = (len >>8 ); + cmd[8 ] = len; +break ; +case 4 : + cmd[10 ] = len >>24 ; + cmd[11 ] = len >>16 ; + cmd[12 ] = len >>8 ; + cmd[13 ] = len; +break ; +case 5 : + cmd[6 ] = len >>24 ; + cmd[7 ] = len >>16 ; + cmd[8 ] = len >>8 ; + cmd[9 ] = len; +break ; + } +} +static int execute_command (BlockDriverState *bdrv,SCSIRequest *r,int direction,BlockDriverCompletionFunc *complete) { +SCSIDeviceState *s = r->dev; + r->remaining =0 ; +retry: +if (s->max_chunk && r->buflen > s->max_chunk) { + r->remaining = r->buflen - s->max_chunk; +scsi_set_length (r->cmd, s->max_chunk / s->blocksize); + r->buflen = s->max_chunk; + } r->io_header.interface_id ='S' ; r->io_header.dxfer_direction = direction; - r->io_header.dxferp = r->buf; + r->io_header.dxferp = r->buf + r->offset; r->io_header.dxfer_len = r->buflen; r->io_header.cmdp = r->cmd; r->io_header.cmd_len = r->cmdlen; - r->io_header.mx_sb_len =sizeof (r->dev->sensebuf); - r->io_header.sbp = r->dev->sensebuf; + r->io_header.mx_sb_len =sizeof (s->sensebuf); + r->io_header.sbp = s->sensebuf; r->io_header.timeout =MAX_UINT ; r->io_header.usr_ptr = r; r->io_header.flags |=SG_FLAG_DIRECT_IO ;if (bdrv_pwrite (bdrv,-1 , &r->io_header,sizeof (r->io_header)) ==-1 ) { +if (errno ==12 ) { +if (!s->max_chunk) { + s->max_chunk =MAX_CHUNK ; + goto retry; + }else if (s->max_chunk > s->blocksize) { + s->max_chunk >>=1 ; + goto retry; + } + }BADF ("execute_command: write failed ! (%d)\n" , errno);return -1 ; }
@@ -246,7 +318,18 @@ static void scsi_read_complete(void * opscsi_command_complete (r, ret);return ; } - len = r->io_header.dxfer_len - r->io_header.resid; + r->offset += r->io_header.dxfer_len - r->io_header.resid; +if (r->remaining !=0 ) { +scsi_cmd_next (r->cmd, r->buflen / s->blocksize); +scsi_set_length (r->cmd, r->remaining / s->blocksize); + r->buflen = r->remaining; + ret =execute_command (s->bdrv, r,SG_DXFER_FROM_DEV , + scsi_read_complete); +if (ret ==-1 ) +scsi_command_complete (r, -EINVAL ); +return ; + } + len = r->offset;DPRINTF ("Data ready tag=0x%x len=%d\n" , r->tag, len); r->len =-1 ;
@@ -293,6 +376,7 @@ static void scsi_read_data(SCSIDevice *dstatic void scsi_write_complete (void * opaque,int ret) {SCSIRequest *r = (SCSIRequest *)opaque; +SCSIDeviceState *s = r->dev;DPRINTF ("scsi_write_complete() ret = %d\n" , ret);if (ret) {
@@ -300,6 +384,17 @@ static void scsi_write_complete(void * oscsi_command_complete (r, ret);return ; } + r->offset += r->io_header.dxfer_len - r->io_header.resid; +if (r->remaining !=0 ) { +scsi_cmd_next (r->cmd, r->buflen / s->blocksize); +scsi_set_length (r->cmd, r->remaining / s->blocksize); + r->buflen = r->remaining; + ret =execute_command (s->bdrv, r,SG_DXFER_TO_DEV , + scsi_write_complete); +if (ret ==-1 ) +scsi_command_complete (r, -EINVAL ); +return ; + }scsi_command_complete (r, ret); }
@@ -641,6 +736,7 @@ SCSIDevice *scsi_generic_init(BlockDrive s->lun = scsiid.lun; s->blocksize =get_blocksize (s->bdrv); s->driver_status =0 ; + s->max_chunk =0 ;memset (s->sensebuf,0 ,sizeof (s->sensebuf));/* removable media returns 0 if not present */ if (s->blocksize <=0 )
Quoted 10 lines · Laurent Vivier
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host.
Using the devices/dev/sg, it allows to send the SCSI commands from the virtual SCSI interface to the real device.It has been tested with a SATA disk and an ATA CD burner with ide-scsi module and wodim to burn a CD-RW.
v5 allows to read movies from a DVD. It has been tested with PowerDVD and XP with unprotected DVD.
Please update to current CVS, I committed v4 before I saw this one.
Thiemo