Il 25/05/2012 13:59, Ronnie Sahlberg ha scritto: > Update iscsi to allow passthrough of SG_IO scsi commands when the iscsi > device is forced to be scsi-generic. > > Implement both bdrv_ioctl() and bdrv_aio_ioctl() in the iscsi backend, > emulate the SG_IO ioctl and pass the SCSI commands across to the > iscsi target. > > This allows end-to-end passthrough of SCSI all the way from the guest, > to qemu, via scsi-generic, then libiscsi all the way to the iscsi target. > > To activate this you need to specify that the iscsi lun should be treated > as a scsi-generic device. > > Example: > -device lsi -device scsi-generic,drive=MyISCSI \ > -drive file=iscsi://10.1.1.125/iqn.ronnie.test/1,if=none,id=MyISCSI > > Note, you can currently not boot a qemu guest from a scsi device. > > Note, > This only works when the host is linux, since the emulation relies on > definitions of SG_IO from the scsi-generic implementation in the > linux kernel. > It should be fairly easy to re-implement some structures similar enough > for non-linux hosts to do the same style of passthrough via a fake > scsi generic layer and libiscsi if need be. > > Signed-off-by: Ronnie Sahlberg <ronniesahlb...@gmail.com> > --- > block/iscsi.c | 186 > ++++++++++++++++++++++++++++++++++++++++++++++++++++- > hw/scsi-generic.c | 13 ++-- > 2 files changed, 189 insertions(+), 10 deletions(-) > > diff --git a/block/iscsi.c b/block/iscsi.c > index d710b86..0d40637 100644 > --- a/block/iscsi.c > +++ b/block/iscsi.c > @@ -34,10 +34,15 @@ > #include <iscsi/iscsi.h> > #include <iscsi/scsi-lowlevel.h> > > +#ifdef __linux__ > +#include <scsi/sg.h> > +#include <hw/scsi-defs.h> > +#endif > > typedef struct IscsiLun { > struct iscsi_context *iscsi; > int lun; > + enum scsi_inquiry_peripheral_device_type type; > int block_size; > unsigned long num_blocks; > int events; > @@ -54,6 +59,9 @@ typedef struct IscsiAIOCB { > int canceled; > size_t read_size; > size_t read_offset; > +#ifdef __linux__ > + sg_io_hdr_t *ioh; > +#endif > } IscsiAIOCB; > > struct IscsiTask { > @@ -509,6 +517,136 @@ iscsi_aio_discard(BlockDriverState *bs, > return &acb->common; > } > > +#ifdef __linux__ > +static void > +iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, > + void *command_data, void *opaque) > +{ > + IscsiAIOCB *acb = opaque; > + > + if (acb->canceled != 0) { > + qemu_aio_release(acb); > + scsi_free_scsi_task(acb->task); > + acb->task = NULL; > + return; > + } > + > + acb->status = 0; > + if (status < 0) { > + error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s", > + iscsi_get_error(iscsi)); > + acb->status = -EIO; > + } > + > + acb->ioh->driver_status = 0; > + acb->ioh->host_status = 0; > + acb->ioh->resid = 0; > + > +#define SG_ERR_DRIVER_SENSE 0x08 > + > + if (status == SCSI_STATUS_CHECK_CONDITION && acb->task->datain.size >= > 2) { > + int ss; > + > + acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE; > + > + acb->ioh->sb_len_wr = acb->task->datain.size - 2; > + ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ? > + acb->ioh->mx_sb_len : acb->ioh->sb_len_wr; > + memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss); > + } > + > + iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb); > + scsi_free_scsi_task(acb->task); > + acb->task = NULL; > +} > + > +static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, > + unsigned long int req, void *buf, > + BlockDriverCompletionFunc *cb, void *opaque) > +{ > + IscsiLun *iscsilun = bs->opaque; > + struct iscsi_context *iscsi = iscsilun->iscsi; > + struct iscsi_data data; > + IscsiAIOCB *acb; > + > + assert(req == SG_IO); > + > + acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque); > + > + acb->iscsilun = iscsilun; > + acb->canceled = 0; > + acb->buf = NULL; > + acb->ioh = buf; > + > + acb->task = malloc(sizeof(struct scsi_task)); > + if (acb->task == NULL) { > + error_report("iSCSI: Failed to allocate task for scsi command. %s", > + iscsi_get_error(iscsi)); > + qemu_aio_release(acb); > + return NULL; > + } > + memset(acb->task, 0, sizeof(struct scsi_task)); > + > + switch (acb->ioh->dxfer_direction) { > + case SG_DXFER_TO_DEV: > + acb->task->xfer_dir = SCSI_XFER_WRITE; > + break; > + case SG_DXFER_FROM_DEV: > + acb->task->xfer_dir = SCSI_XFER_READ; > + break; > + default: > + acb->task->xfer_dir = SCSI_XFER_NONE; > + break; > + } > + > + acb->task->cdb_size = acb->ioh->cmd_len; > + memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len); > + acb->task->expxferlen = acb->ioh->dxfer_len; > + > + if (acb->task->xfer_dir == SCSI_XFER_WRITE) { > + data.data = acb->ioh->dxferp; > + data.size = acb->ioh->dxfer_len; > + } > + if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, > + iscsi_aio_ioctl_cb, > + (acb->task->xfer_dir == SCSI_XFER_WRITE) ? > + &data : NULL, > + acb) != 0) { > + scsi_free_scsi_task(acb->task); > + qemu_aio_release(acb); > + return NULL; > + } > + > + /* tell libiscsi to read straight into the buffer we got from ioctl */ > + if (acb->task->xfer_dir == SCSI_XFER_READ) { > + scsi_task_add_data_in_buffer(acb->task, > + acb->ioh->dxfer_len, > + acb->ioh->dxferp); > + } > + > + iscsi_set_events(iscsilun); > + > + return &acb->common; > +} > + > +static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void > *buf) > +{ > + IscsiLun *iscsilun = bs->opaque; > + > + switch (req) { > + case SG_GET_VERSION_NUM: > + *(int *)buf = 30000; > + break; > + case SG_GET_SCSI_ID: > + ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type; > + break; > + default: > + return -1; > + } > + return 0; > +} > +#endif > + > static int64_t > iscsi_getlength(BlockDriverState *bs) > { > @@ -558,18 +696,33 @@ iscsi_readcapacity16_cb(struct iscsi_context *iscsi, > int status, > } > > static void > -iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, > +iscsi_inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, > void *opaque) > { > struct IscsiTask *itask = opaque; > - struct scsi_task *task; > + struct scsi_task *task = command_data; > + struct scsi_inquiry_standard *inq; > > if (status != 0) { > itask->status = 1; > itask->complete = 1; > + scsi_free_scsi_task(task); > return; > } > > + inq = scsi_datain_unmarshall(task); > + if (inq == NULL) { > + error_report("iSCSI: Failed to unmarshall inquiry data."); > + itask->status = 1; > + itask->complete = 1; > + scsi_free_scsi_task(task); > + return; > + } > + > + itask->iscsilun->type = inq->periperal_device_type; > + > + scsi_free_scsi_task(task); > + > task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun, > iscsi_readcapacity16_cb, opaque); > if (task == NULL) { > @@ -580,6 +733,30 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int > status, void *command_data, > } > } > > +static void > +iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, > + void *opaque) > +{ > + struct IscsiTask *itask = opaque; > + struct scsi_task *task; > + > + if (status != 0) { > + itask->status = 1; > + itask->complete = 1; > + return; > + } > + > + task = iscsi_inquiry_task(iscsi, itask->iscsilun->lun, > + 0, 0, 36, > + iscsi_inquiry_cb, opaque); > + if (task == NULL) { > + error_report("iSCSI: failed to send inquiry command."); > + itask->status = 1; > + itask->complete = 1; > + return; > + } > +} > + > static int parse_chap(struct iscsi_context *iscsi, const char *target) > { > QemuOptsList *list; > @@ -827,6 +1004,11 @@ static BlockDriver bdrv_iscsi = { > .bdrv_aio_flush = iscsi_aio_flush, > > .bdrv_aio_discard = iscsi_aio_discard, > + > +#ifdef __linux__ > + .bdrv_ioctl = iscsi_ioctl, > + .bdrv_aio_ioctl = iscsi_aio_ioctl, > +#endif > }; > > static void iscsi_block_init(void) > diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c > index d856d23..8d51060 100644 > --- a/hw/scsi-generic.c > +++ b/hw/scsi-generic.c > @@ -400,12 +400,6 @@ static int scsi_generic_initfn(SCSIDevice *s) > return -1; > } > > - /* check we are really using a /dev/sg* file */ > - if (!bdrv_is_sg(s->conf.bs)) { > - error_report("not /dev/sg*"); > - return -1; > - } > - > if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) { > error_report("Device doesn't support drive option werror"); > return -1; > @@ -416,8 +410,11 @@ static int scsi_generic_initfn(SCSIDevice *s) > } > > /* check we are using a driver managing SG_IO (version 3 and after */ > - if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || > - sg_version < 30000) { > + if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) { > + error_report("scsi generic interface not supported"); > + return -1; > + } > + if (sg_version < 30000) { > error_report("scsi generic interface too old"); > return -1; > }
Applied to scsi-next branch for 1.2. Paolo