From patchwork Fri May 25 11:59:01 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: ronnie sahlberg X-Patchwork-Id: 161325 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 4F068B6EF1 for ; Fri, 25 May 2012 22:11:25 +1000 (EST) Received: from localhost ([::1]:44538 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SXtMZ-0000Mv-3f for incoming@patchwork.ozlabs.org; Fri, 25 May 2012 08:11:23 -0400 Received: from eggs.gnu.org ([208.118.235.92]:44197) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SXtMK-000068-Nq for qemu-devel@nongnu.org; Fri, 25 May 2012 08:11:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SXtME-0001hF-Ma for qemu-devel@nongnu.org; Fri, 25 May 2012 08:11:08 -0400 Received: from mail-pb0-f45.google.com ([209.85.160.45]:56978) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SXtME-0001ap-Ac for qemu-devel@nongnu.org; Fri, 25 May 2012 08:11:02 -0400 Received: by mail-pb0-f45.google.com with SMTP id ro12so1883260pbb.4 for ; Fri, 25 May 2012 05:11:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=aCw9RaowrIu78l/eGK+yxx92jmfm3WFkqDcTDxAwAWA=; b=MNBQL+gxpwiUM03MMQ5K6FjTgoVwixIfKjqCg3ZeJN4nSOlz29+QcCsEJK8x6E21NQ +RlH1N1hKqX/y1G6+yjnLQXcH5dIMQ+TvHZ2u3Z7l/9K9UCoG0awjzb80tR9gQK0cx0k f571tuIJHL8RPM3Q4TDq6AV70xBgdYd/elwEzXzT1pmoqiB4JkjSByTKtkEbzCGDKwMP Z4FabOntceBhR9lrqz97H/G22McjwmSaCgU+EwxyjSYxVNBvYrk6DDsLeo3uqHjfUoqY wHbNfu5BIpAt2tv6DifXOe4NXyBtuQFsI5sAbtQyUUi5ZTddXvCVu9pcF69cujZyPacI PxCA== Received: by 10.68.225.135 with SMTP id rk7mr32006685pbc.38.1337947861415; Fri, 25 May 2012 05:11:01 -0700 (PDT) Received: from ronniesahlberg@gmail.com (CPE-138-130-106-226.lns3.cht.bigpond.net.au. [138.130.106.226]) by mx.google.com with ESMTPS id wd10sm8868958pbc.70.2012.05.25.05.10.57 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 25 May 2012 05:11:00 -0700 (PDT) Received: by ronniesahlberg@gmail.com (sSMTP sendmail emulation); Fri, 25 May 2012 21:59:33 +1000 From: Ronnie Sahlberg To: pbonzini@redhat.com, qemu-devel@nongnu.org Date: Fri, 25 May 2012 21:59:01 +1000 Message-Id: <1337947141-22950-2-git-send-email-ronniesahlberg@gmail.com> X-Mailer: git-send-email 1.7.3.1 In-Reply-To: <1337947141-22950-1-git-send-email-ronniesahlberg@gmail.com> References: <1337947141-22950-1-git-send-email-ronniesahlberg@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.160.45 Cc: Ronnie Sahlberg Subject: [Qemu-devel] [PATCH] SCSI: Add SCSI passthrough via scsi-generic to libiscsi X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 #include +#ifdef __linux__ +#include +#include +#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; }