* [Qemu-devel] [PATCH 0/1] ISCSI: Add support for iscsi passthroughi to iscsi devices
@ 2012-05-25 11:59 Ronnie Sahlberg
2012-05-25 11:59 ` [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi Ronnie Sahlberg
0 siblings, 1 reply; 3+ messages in thread
From: Ronnie Sahlberg @ 2012-05-25 11:59 UTC (permalink / raw)
To: pbonzini, qemu-devel
Paolo,
Thanks for your review!
I have implemented your suggested changes and resubmit the patch.
I didnt know about -device scsi-generic arguments but using it
allowed to get rid of the bs->iscsi field altogether as you suggested and the patch is now a low cleaner and doesnt touch so many other areas anymore.
The commit message contains an example fragment from how force the passthrough mode for iscsi on the commandline.
I will update the manpage and documentation to illustrate how to use passthrough with iscsi in a later patch during the weekend or next week.
Thanks
ronnie sahlberg
^ permalink raw reply [flat|nested] 3+ messages in thread
* [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi
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 ` Ronnie Sahlberg
2012-05-26 7:50 ` Paolo Bonzini
0 siblings, 1 reply; 3+ messages in thread
From: Ronnie Sahlberg @ 2012-05-25 11:59 UTC (permalink / raw)
To: pbonzini, qemu-devel; +Cc: Ronnie Sahlberg
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;
}
--
1.7.3.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi
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
0 siblings, 0 replies; 3+ messages in thread
From: Paolo Bonzini @ 2012-05-26 7:50 UTC (permalink / raw)
To: Ronnie Sahlberg; +Cc: qemu-devel
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
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2012-05-26 7:50 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).