All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi
Date: Sat, 26 May 2012 09:50:24 +0200	[thread overview]
Message-ID: <4FC08B40.7020302@redhat.com> (raw)
In-Reply-To: <1337947141-22950-2-git-send-email-ronniesahlberg@gmail.com>

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 <ronniesahlberg@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

      reply	other threads:[~2012-05-26  7:50 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-05-25 11:59 [Qemu-devel] [PATCH 0/1] ISCSI: Add support for iscsi passthroughi to iscsi devices Ronnie Sahlberg
2012-05-25 11:59 ` [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi Ronnie Sahlberg
2012-05-26  7:50   ` Paolo Bonzini [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4FC08B40.7020302@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=ronniesahlberg@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.