qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to


From: Paolo Bonzini
Subject: Re: [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi
Date: Sat, 26 May 2012 09:50:24 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20120430 Thunderbird/12.0.1

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 <address@hidden>
> ---
>  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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]