[Top][All Lists]
[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