[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 05/14] ISCSI: Add SCSI passthrough via scsi-generic
From: |
Paolo Bonzini |
Subject: |
[Qemu-devel] [PATCH 05/14] ISCSI: Add SCSI passthrough via scsi-generic to libiscsi |
Date: |
Mon, 2 Jul 2012 11:41:18 +0200 |
From: Ronnie Sahlberg <address@hidden>
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>
Signed-off-by: Paolo Bonzini <address@hidden>
---
block/iscsi.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/scsi-generic.c | 13 ++---
2 files changed, 147 insertions(+), 8 deletions(-)
diff --git a/block/iscsi.c b/block/iscsi.c
index 22888a0..029739e 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -35,6 +35,10 @@
#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;
@@ -56,6 +60,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 {
@@ -515,6 +522,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)
{
@@ -926,6 +1063,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;
}
--
1.7.10.2
- [Qemu-devel] [PULL 00/14] SCSI updates for 2012-07-02, Paolo Bonzini, 2012/07/02
- [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Paolo Bonzini, 2012/07/02
- Re: [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Blue Swirl, 2012/07/03
- Re: [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Paolo Bonzini, 2012/07/04
- Re: [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Blue Swirl, 2012/07/05
- Re: [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Paolo Bonzini, 2012/07/06
- Re: [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Blue Swirl, 2012/07/07
- Re: [Qemu-devel] [PATCH 02/14] scsi: add a qdev property for the disk's WWN, Paolo Bonzini, 2012/07/07
[Qemu-devel] [PATCH 01/14] scsi: simplify handling of the VPD page length field, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 05/14] ISCSI: Add SCSI passthrough via scsi-generic to libiscsi,
Paolo Bonzini <=
[Qemu-devel] [PATCH 04/14] scsi-disk: implement READ DISC INFORMATION, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 03/14] atapi: implement READ DISC INFORMATION, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 07/14] megasas: Add header file, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 06/14] ISCSI: force use of sg for SMC and SSC devices, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 13/14] scsi: Add basic support for SCSI media changer commands., Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 10/14] scsi: Fix data length == SCSI_SENSE_BUF_SIZE, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 11/14] scsi: Fix LOAD_UNLOAD, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 12/14] scsi: Ensure command and transfer lengths are set for all SCSI devices, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 09/14] virtio-scsi: do not crash on adding buffers to the event queue, Paolo Bonzini, 2012/07/02
[Qemu-devel] [PATCH 14/14] scsi: Fix transfer length for READ POSITION commands., Paolo Bonzini, 2012/07/02