Real SCSI device passthrough
cover Real SCSI device passthrough
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/3] SG support (Synchronous I/O)
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host. it uses synchronous SCSI I/O.
[PATCH 2/3] SG support (Asynchronous Read/Write)
This patch modifies scsi_read_data() and scsi_write_data() to use asynchronous I/O.
[PATCH 3/3] SG support (Asynchronous Send Command)
This patch modifies scsi_send_command() to use asynchronous I/O .
Laurent
PATCH 1/3 SG support (Synchronous I/O)
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 command from the virtual SCSI interface to the real device.
This patch uses synchronous SCSI I/O.
block-raw.c has been modified to disable the "lseek()" when offset is less than 0 in raw_pread() and raw_pwrite() to be able to use them on /dev/sg devices.
--- Makefile | 1 block-raw.c | 19 + block.c | 17 + block.h | 2 block_int.h | 4 hw/esp.c | 20 + hw/lsi53c895a.c | 22 +- hw/scsi-disk.c | 62 +++-- hw/scsi-disk.h | 29 +- hw/scsi-generic.c | 577 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb-msd.c | 16 - 11 files changed, 704 insertions(+), 65 deletions(-)
block-raw.c +17 -2
@@ -52,6 +52,7 @@#include <sys/ioctl.h> #include <linux/cdrom.h> #include <linux/fd.h> +#include <linux/major.h> #endif #ifdef __FreeBSD__#include <sys/disk.h>
@@ -148,7 +149,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
@@ -201,7 +202,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) [%"
@@ -672,6 +673,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 );
@@ -851,6 +854,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)
@@ -873,6 +882,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 = {
@@ -899,6 +912,8 @@ 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, };#else /* _WIN32 */
block.c +16 -1
@@ -303,7 +303,6 @@ int bdrv_file_open(BlockDriverState **pb {BlockDriverState *bs;int ret; - bs =bdrv_new ("" );if (!bs)return -ENOMEM ;
@@ -780,6 +779,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)
@@ -1352,3 +1356,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
@@ -117,6 +117,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);
@@ -146,6 +147,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/scsi-disk.c +38 -24
166 lines not on this page · raw message
hw/esp.c +11 -9
@@ -120,7 +120,7 @@ static int get_cmd(ESPState *s, uint8_tif (s->current_dev) {/* Started a new command before the old one finished. Cancel it. */ -scsi_cancel_io (s->current_dev,0 ); + s->current_dev->cancel_io (s->current_dev,0 ); s->async_len =0 ; }
@@ -143,7 +143,7 @@ static void do_cmd(ESPState *s, uint8_tDPRINTF ("do_cmd: busid 0x%x\n" , buf[0 ]); lun = buf[0 ] &7 ; - datalen =scsi_send_command (s->current_dev,0 , &buf[1 ], lun); + datalen = s->current_dev->send_command (s->current_dev,0 , &buf[1 ], lun); s->ti_size = datalen;if (datalen !=0 ) { s->rregs[4 ] =STAT_IN |STAT_TC ;
@@ -151,10 +151,10 @@ static void do_cmd(ESPState *s, uint8_t s->dma_counter =0 ;if (datalen >0 ) { s->rregs[4 ] |=STAT_DI ; -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 ); }else { s->rregs[4 ] |=STAT_DO ; -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); } } s->rregs[5 ] =INTR_BS |INTR_FC ;
@@ -253,9 +253,9 @@ static void esp_do_dma(ESPState *s)if (s->async_len ==0 ) {if (to_device) {// ti_size is negative -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); }else { -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 );/* If there is still data to be read from the device then complete the DMA operation immeriately. Otherwise defer until the scsi layer has completed. */
@@ -290,7 +290,7 @@ static void esp_command_complete(void *o }else {DPRINTF ("transfer %d/%d\n" , s->dma_left, s->ti_size); s->async_len = arg; - s->async_buf =scsi_get_buf (s->current_dev,0 ); + s->async_buf = s->current_dev->get_buf (s->current_dev,0 );if (s->dma_left) {esp_do_dma (s); }else if (s->dma_counter !=0 && s->ti_size <=0 ) {
@@ -569,11 +569,13 @@ void esp_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + s->scsi_dev[id]->destroy (s->scsi_dev[id]); }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 +14 -8
@@ -465,7 +465,8 @@ static void lsi_do_dma(LSIState *s, int s->dbc -= count;if (s->dma_buf ==NULL ) { - s->dma_buf =scsi_get_buf (s->current_dev, s->current_tag); + s->dma_buf = s->current_dev->get_buf (s->current_dev, + s->current_tag); }/* ??? Set SFBR to first data byte. */
@@ -479,10 +480,10 @@ static void lsi_do_dma(LSIState *s, int s->dma_buf =NULL ;if (out) {/* Write the data. */ -scsi_write_data (s->current_dev, s->current_tag); + s->current_dev->write_data (s->current_dev, s->current_tag); }else {/* Request any remaining data. */ -scsi_read_data (s->current_dev, s->current_tag); + s->current_dev->read_data (s->current_dev, s->current_tag); } }else { s->dma_buf += count;
@@ -631,13 +632,14 @@ static void lsi_do_command(LSIState *s) s->dbc =16 ;cpu_physical_memory_read (s->dnad, buf, s->dbc); s->sfbr = buf[0 ]; - n =scsi_send_command (s->current_dev, s->current_tag, buf, s->current_lun); + n = s->current_dev->send_command (s->current_dev, s->current_tag, buf, + s->current_lun);if (n >0 ) {lsi_set_phase (s,PHASE_DI ); -scsi_read_data (s->current_dev, s->current_tag); + s->current_dev->read_data (s->current_dev, s->current_tag); }else if (n <0 ) {lsi_set_phase (s,PHASE_DO ); -scsi_write_data (s->current_dev, s->current_tag); + s->current_dev->write_data (s->current_dev, s->current_tag); }if (n && s->current_dma_len ==0 ) {/* Command did not complete immediately so disconnect. */
@@ -1268,6 +1270,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] */
@@ -1822,10 +1826,12 @@ void lsi_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + 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/usb-msd.c +8 -8
@@ -149,9 +149,9 @@ static void usb_msd_copy_data(MSDState * s->data_len -= len;if (s->scsi_len ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } }
@@ -204,7 +204,7 @@ static void usb_msd_command_complete(voireturn ; } s->scsi_len = arg; - s->scsi_buf =scsi_get_buf (s->scsi_dev, tag); + s->scsi_buf = s->scsi_dev->get_buf (s->scsi_dev, tag);if (p) {usb_msd_copy_data (s);if (s->usb_len ==0 ) {
@@ -342,7 +342,7 @@ static int usb_msd_handle_control(USBDevstatic void usb_msd_cancel_io (USBPacket *p,void *opaque) {MSDState *s = opaque; -scsi_cancel_io (s->scsi_dev, s->tag); + s->scsi_dev->cancel_io (s->scsi_dev, s->tag); s->packet =NULL ; s->scsi_len =0 ; }
@@ -390,14 +390,14 @@ static int usb_msd_handle_data(USBDeviceDPRINTF ("Command tag 0x%x flags %08x len %d data %d\n" , s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue =0 ; -scsi_send_command (s->scsi_dev, s->tag, cbw.cmd,0 ); + s->scsi_dev->send_command (s->scsi_dev, s->tag, cbw.cmd,0 );/* ??? Should check that USB and SCSI data transfer directions match. */ if (s->residue ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } ret = len;
@@ -508,7 +508,7 @@ static void usb_msd_handle_destroy(USBDe {MSDState *s = (MSDState *)dev; -scsi_disk_destroy (s->scsi_dev); + s->scsi_dev->destroy (s->scsi_dev);bdrv_delete (s->bs);qemu_free (s); }
hw/scsi-generic.c new file +577 -0
577 lines not on this page · raw message
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
hw/scsi-disk.h +16 -13
@@ -7,24 +7,27 @@ enum scsi_reason {SCSI_REASON_DATA /* Transfer complete, more data required. */ }; +typedef struct SCSIDeviceState SCSIDeviceState ;typedef struct SCSIDevice SCSIDevice ;typedef void (*scsi_completionfn )(void *opaque,int reason,uint32_t tag,uint32_t arg); -SCSIDevice *scsi_disk_init (BlockDriverState *bdrv, -int tcq, -scsi_completionfn completion, -void *opaque); -void scsi_disk_destroy (SCSIDevice *s); +struct SCSIDevice +{ +SCSIDeviceState *state; +void (*destroy)(SCSIDevice *s); +int32_t (*send_command)(SCSIDevice *s,uint32_t tag,uint8_t *buf, +int lun); +void (*read_data)(SCSIDevice *s,uint32_t tag); +int (*write_data)(SCSIDevice *s,uint32_t tag); +void (*cancel_io)(SCSIDevice *s,uint32_t tag); +uint8_t *(*get_buf)(SCSIDevice *s,uint32_t tag); +}; -int32_t scsi_send_command (SCSIDevice *s,uint32_t tag,uint8_t *buf,int lun); -/* SCSI data transfers are asynchrnonous. However, unlike the block IO -layer the completion routine may be called directly by -scsi_{read,write}_data. */ -void scsi_read_data (SCSIDevice *s,uint32_t tag); -int scsi_write_data (SCSIDevice *s,uint32_t tag); -void scsi_cancel_io (SCSIDevice *s,uint32_t tag); -uint8_t *scsi_get_buf (SCSIDevice *s,uint32_t tag); +SCSIDevice *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);
PATCH 2/3 SG support (Asynchronous Read/Write)
This patch modifies scsi_read_data() and scsi_write_data() to use asynchronous I/O whith SCSI Generic.
block-raw.s has been modified to define the number of bytes to transfer instead of the number of blocks: if nb_sectors is less than zero the nb_sectors is a number of bytes.
--- block-raw.c | 5 + hw/lsi53c895a.c | 2 hw/scsi-generic.c | 198 ++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 133 insertions(+), 72 deletions(-)
block-raw.c +4 -1
@@ -385,7 +385,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;
hw/scsi-generic.c +127 -71
341 lines not on this page · raw message
hw/lsi53c895a.c +2 -0
@@ -1225,6 +1225,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 */
PATCH 3/3 SG support (Asynchronous Send Command)
This patch modifies scsi_send_command() to use asynchronous I/O with SCSI Generic.
This allows to use command with data transfers that are not immediate. Old behavior can generate softlockup in the guest kernel.
hw/lsi53c895a.c has been modified to manage this new behavior.
--- hw/lsi53c895a.c | 25 ++++++++++++++++++------- hw/scsi-generic.c | 3 +-- 2 files changed, 19 insertions(+), 9 deletions(-)
hw/lsi53c895a.c +18 -7
@@ -187,6 +187,7 @@ typedef struct {/* The tag is a combination of the device ID and the SCSI tag. */ uint32_t current_tag;uint32_t current_dma_len; +int command_complete;uint8_t *dma_buf;lsi_queue *queue;int queue_len;
@@ -597,6 +598,7 @@ static void lsi_command_complete(void *oif (reason ==SCSI_REASON_DONE ) {DPRINTF ("Command complete sense=%d\n" , (int )arg); s->sense = arg; + s->command_complete =2 ;if (s->waiting && s->dbc !=0 ) {/* Raise phase mismatch for short transfers. */ lsi_bad_phase (s, out,PHASE_ST );
@@ -613,6 +615,7 @@ static void lsi_command_complete(void *o }DPRINTF ("Data ready tag=0x%x len=%d\n" , tag, arg); s->current_dma_len = arg; + s->command_complete =1 ;if (!s->waiting)return ;if (s->waiting ==1 || s->dbc ==0 ) {
@@ -632,6 +635,7 @@ static void lsi_do_command(LSIState *s) s->dbc =16 ;cpu_physical_memory_read (s->dnad, buf, s->dbc); s->sfbr = buf[0 ]; + s->command_complete =0 ; n = s->current_dev->send_command (s->current_dev, s->current_tag, buf, s->current_lun);if (n >0 ) {
@@ -641,13 +645,20 @@ static void lsi_do_command(LSIState *s)lsi_set_phase (s,PHASE_DO ); s->current_dev->write_data (s->current_dev, s->current_tag); } -if (n && s->current_dma_len ==0 ) { -/* Command did not complete immediately so disconnect. */ -lsi_add_msg_byte (s,2 );/* SAVE DATA POINTER */ -lsi_add_msg_byte (s,4 );/* DISCONNECT */ -lsi_set_phase (s,PHASE_MI ); - s->msg_action =1 ; -lsi_queue_command (s); + +if (!s->command_complete) { +if (n) { +/* Command did not complete immediately so disconnect. */ +lsi_add_msg_byte (s,2 );/* SAVE DATA POINTER */ +lsi_add_msg_byte (s,4 );/* DISCONNECT */ +/* wait data */ +lsi_set_phase (s,PHASE_MI ); + s->msg_action =1 ; +lsi_queue_command (s); + }else { +/* wait command complete */ +lsi_set_phase (s,PHASE_DI ); + } } }
hw/scsi-generic.c +1 -2
@@ -496,12 +496,11 @@ static int32_t scsi_send_command(SCSIDevfree (r->buf); r->buflen =0 ; r->buf =NULL ; - ret =execute_command (s->bdrv, r,SG_DXFER_NONE ,NULL ); + ret =execute_command (s->bdrv, r,SG_DXFER_NONE , scsi_command_complete);if (ret ==-1 ) {scsi_command_complete (r, -EINVAL );return 0 ; } -scsi_command_complete (r,scsi_get_sense (r));return 0 ; }