qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] libiscsi


From: Ronnie Sahlberg
Subject: [Qemu-devel] [PATCH] libiscsi
Date: Mon, 13 Dec 2010 19:05:50 +1100

This patch adds a new block driver : block.iscsi.c
This driver interfaces with the multiplatform posix library
for iscsi initiator/client access to iscsi devices hosted at
git://github.com/sahlberg/libiscsi.git

The patch adds the driver to interface with the iscsi library.
It also updated the configure script to
* by default, probe is libiscsi is available and if so, build
  qemu against libiscsi.
* --enable-libiscsi
  Force a build against libiscsi. If libiscsi is not available
  the build will fail.
* --disable-libiscsi
  Do not link against libiscsi, even if it is available.

When linked with libiscsi, qemu gains support to access iscsi resources
such as disks and cdrom directly, without having to make the devices visible
to the host.

You can specify devices using a iscsi url of the form :
iscsi://<host>[:<port]/<target-iqn-name>/<lun>

Example:
-drive file=iscsi://10.1.1.1:3260/iqn.ronnie.test/1

-cdrom iscsi://10.1.1.1:3260/iqn.ronnie.test/2

Signed-off-by: Ronnie Sahlberg <address@hidden>
---
 Makefile.objs |    2 +-
 block/iscsi.c |  528 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 configure     |   29 +++
 3 files changed, 558 insertions(+), 1 deletions(-)
 create mode 100644 block/iscsi.c

diff --git a/Makefile.objs b/Makefile.objs
index cebb945..81731c5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -22,7 +22,7 @@ block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o 
dmg.o bochs.o vpc.o vv
 block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
 block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
 block-nested-$(CONFIG_WIN32) += raw-win32.o
-block-nested-$(CONFIG_POSIX) += raw-posix.o
+block-nested-$(CONFIG_POSIX) += raw-posix.o iscsi.o
 block-nested-$(CONFIG_CURL) += curl.o
 
 block-obj-y +=  $(addprefix block/, $(block-nested-y))
diff --git a/block/iscsi.c b/block/iscsi.c
new file mode 100644
index 0000000..fba5ee6
--- /dev/null
+++ b/block/iscsi.c
@@ -0,0 +1,528 @@
+/*
+ * QEMU Block driver for iSCSI images
+ *
+ * Copyright (c) 2010 Ronnie Sahlberg <address@hidden>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+#ifdef CONFIG_LIBISCSI
+
+#include <poll.h>
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "block_int.h"
+
+#include <iscsi/iscsi.h>
+#include <iscsi/scsi-lowlevel.h>
+
+
+typedef struct ISCSILUN {
+    struct iscsi_context *iscsi;
+    int lun;
+    int block_size;
+    unsigned long num_blocks;
+} ISCSILUN;
+
+typedef struct ISCSIAIOCB {
+    BlockDriverAIOCB common;
+    QEMUIOVector *qiov;
+    QEMUBH *bh;
+    ISCSILUN *iscsilun;
+    int canceled;
+    int status;
+    size_t read_size;
+} ISCSIAIOCB;
+
+struct iscsi_task {
+    ISCSILUN *iscsilun;
+    int status;
+    int complete;
+};
+
+static int
+iscsi_is_inserted(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    return iscsi_is_logged_in(iscsi);
+}
+
+
+static void
+iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    ISCSIAIOCB *acb = (ISCSIAIOCB *)blockacb;
+
+    acb->status = -EIO;
+    acb->common.cb(acb->common.opaque, acb->status);
+    acb->canceled = 1;
+}
+
+static AIOPool iscsi_aio_pool = {
+    .aiocb_size         = sizeof(ISCSIAIOCB),
+    .cancel             = iscsi_aio_cancel,
+};
+
+
+static void iscsi_process_read(void *arg);
+static void iscsi_process_write(void *arg);
+
+static void
+iscsi_set_events(ISCSILUN *iscsilun)
+{
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
+                           (iscsi_which_events(iscsi)&POLLOUT)
+                           ?iscsi_process_write:NULL,
+                           NULL, NULL, iscsilun);
+}
+
+static void
+iscsi_process_read(void *arg)
+{
+    ISCSILUN *iscsilun = arg;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    iscsi_service(iscsi, POLLIN);
+    iscsi_set_events(iscsilun);
+}
+
+static void
+iscsi_process_write(void *arg)
+{
+    ISCSILUN *iscsilun = arg;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    iscsi_service(iscsi, POLLOUT);
+    iscsi_set_events(iscsilun);
+}
+
+
+static int
+iscsi_schedule_bh(QEMUBHFunc *cb, ISCSIAIOCB *acb)
+{
+    acb->bh = qemu_bh_new(cb, acb);
+    if (!acb->bh) {
+       error_report("oom: could not create iscsi bh");
+       return -EIO;
+    }
+
+    qemu_bh_schedule(acb->bh);
+    return 0;
+}
+
+static void
+iscsi_readv_writev_bh_cb(void *p)
+{
+    ISCSIAIOCB *acb = p;
+
+    acb->common.cb(acb->common.opaque, acb->status);
+    qemu_aio_release(acb);
+}
+
+static void
+iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
+                    void *command_data, void *private_data)
+{
+    ISCSIAIOCB *acb = private_data;
+
+    if (acb->canceled != 0) {
+       qemu_aio_release(acb);
+       return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+       error_report("Failed to write10 data to iSCSI lun. %s",
+                    iscsi_get_error(iscsi));
+       acb->status = -EIO;
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
+                QEMUIOVector *qiov, int nb_sectors,
+                BlockDriverCompletionFunc *cb,
+                void *opaque)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    ISCSIAIOCB *acb;
+    size_t size;
+    unsigned char *buf;
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+    if (!acb) {
+       return NULL;
+    }
+
+    acb->iscsilun = iscsilun;
+    acb->qiov     = qiov;
+
+    acb->canceled   = 0;
+
+    /* XXX we should pass the iovec to write10 to avoid the extra copy */
+    /* this will allow us to get rid of 'buf' completely */
+    size = nb_sectors * BDRV_SECTOR_SIZE;
+    buf = qemu_malloc(size);
+    qemu_iovec_to_buffer(acb->qiov, buf);
+    if (iscsi_write10_async(iscsi, iscsilun->lun, buf, size,
+                           sector_num * BDRV_SECTOR_SIZE
+                           / iscsilun->block_size,
+                           0, 0, iscsilun->block_size,
+                           iscsi_aio_write10_cb, acb) != 0) {
+       error_report("iSCSI: Failed to send write10 command. %s",
+                    iscsi_get_error(iscsi));
+       qemu_free(buf);
+       qemu_aio_release(acb);
+       return NULL;
+    }
+    qemu_free(buf);
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+static void
+iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
+                   void *command_data, void *private_data)
+{
+    ISCSIAIOCB *acb = private_data;
+    struct scsi_task *scsi = command_data;
+
+    if (acb->canceled != 0) {
+       qemu_aio_release(acb);
+       return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+       error_report("Failed to read10 data from iSCSI lun. %s",
+                    iscsi_get_error(iscsi));
+       acb->status = -EIO;
+    } else {
+       qemu_iovec_from_buffer(acb->qiov, scsi->datain.data, acb->read_size);
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
+               QEMUIOVector *qiov, int nb_sectors,
+               BlockDriverCompletionFunc *cb,
+               void *opaque)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    ISCSIAIOCB *acb;
+    size_t qemu_read_size, lun_read_size;
+
+    qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
+    lun_read_size  = (qemu_read_size + iscsilun->block_size - 1)
+      / iscsilun->block_size * iscsilun->block_size;
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+    if (!acb) {
+       return NULL;
+    }
+
+    acb->iscsilun = iscsilun;
+    acb->qiov     = qiov;
+
+    acb->canceled   = 0;
+    acb->read_size  = qemu_read_size;
+
+    if (iscsi_read10_async(iscsi, iscsilun->lun,
+                          sector_num * BDRV_SECTOR_SIZE
+                          / iscsilun->block_size,
+                          lun_read_size, iscsilun->block_size,
+                          iscsi_aio_read10_cb, acb) != 0) {
+       error_report("iSCSI: Failed to send read10 command. %s",
+                    iscsi_get_error(iscsi));
+       qemu_aio_release(acb);
+       return NULL;
+    }
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+
+static void synccache10_cb(struct iscsi_context *iscsi, int status,
+                          void *command_data, void *private_data)
+{
+    struct iscsi_task *task = private_data;
+
+    task->status   = status;
+    task->complete = 1;
+}
+
+static int
+iscsi_flush(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    struct iscsi_task task;
+
+    task.iscsilun = iscsilun;
+    task.status = 0;
+    task.complete = 0;
+
+    if (iscsi_synchronizecache10_async(iscsi, iscsilun->lun,
+                                      0, 0, 0, 0,
+                                      synccache10_cb,
+                                      &task) != 0) {
+       error_report("iSCSI: Failed to send SYNCHRONIZECACHE10. %s",
+                    iscsi_get_error(iscsi));
+        return -1;
+    }
+
+    async_context_push();
+    while (!task.complete) {
+       iscsi_set_events(iscsilun);
+       qemu_aio_wait();
+    }
+    async_context_pop();
+    if (task.status != 0) {
+       error_report("iSCSI: Failed to send SYNCHRONIZECACHE10: %s",
+                    iscsi_get_error(iscsi));
+       return -EIO;
+    }
+
+    return 0;
+}
+
+static int64_t
+iscsi_getlength(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    int64_t len;
+
+    len  = iscsilun->num_blocks;
+    len *= iscsilun->block_size;
+
+    return len;
+}
+
+static void
+iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
+                       void *command_data, void *private_data)
+{
+    struct iscsi_task *task = private_data;
+    struct scsi_readcapacity10 *rc10;
+    struct scsi_task *scsi = command_data;
+
+    if (status != 0) {
+       error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
+                    iscsi_get_error(iscsi));
+       task->status   = 1;
+       task->complete = 1;
+       return;
+    }
+
+    rc10 = scsi_datain_unmarshall(scsi);
+    if (rc10 == NULL) {
+       error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
+       task->status   = 1;
+       task->complete = 1;
+       return;
+    }
+
+    task->iscsilun->block_size = rc10->block_size;
+    task->iscsilun->num_blocks = rc10->lba;
+
+    task->status   = 0;
+    task->complete = 1;
+}
+
+
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+                void *private_data)
+{
+    struct iscsi_task *task = private_data;
+
+    if (status != 0) {
+       task->status   = 1;
+       task->complete = 1;
+       return;
+    }
+
+    if (iscsi_readcapacity10_async(iscsi, task->iscsilun->lun, 0, 0,
+                                  iscsi_readcapacity10_cb, private_data)
+       != 0) {
+       error_report("iSCSI: failed to send readcapacity command.");
+       task->status   = 1;
+       task->complete = 1;
+    }
+}
+
+/*
+ * We support iscsi url's on the form
+ * iscsi://<host>[:<port>]/<targetname>/<lun>
+ */
+static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = NULL;
+    struct iscsi_task task;
+    char *ptr, *host, *target, *url;
+    char *tmp;
+    int ret, lun;
+
+    bzero(iscsilun, sizeof(ISCSILUN));
+
+    url = qemu_strdup(filename);
+
+    if (strncmp(url, "iscsi://", 8)) {
+       error_report("iSCSI: url does not start with 'iscsi://'");
+       ret = -EINVAL;
+       goto failed;
+    }
+
+    host = url + 8;
+
+    ptr = index(host, '/');
+    if (ptr == NULL) {
+       error_report("iSCSI: host is not '/' terminated.");
+       ret = -EINVAL;
+       goto failed;
+    }
+
+    *ptr++ = 0;
+    target = ptr;
+
+    ptr = index(target, '/');
+    if (ptr == NULL) {
+       error_report("iSCSI: host/target is not '/' terminated.");
+       ret = -EINVAL;
+       goto failed;
+    }
+
+    *ptr++ = 0;
+
+    lun = strtol(ptr, &tmp, 10);
+    if (*tmp) {
+       error_report("iSCSI: Invalid LUN specified.");
+       ret = -EINVAL;
+       goto failed;
+    }
+
+    /* Should really append the KVM name after the ':' here */
+    iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
+    if (iscsi == NULL) {
+       error_report("iSCSI: Failed to create iSCSI context.");
+       ret = -ENOMEM;
+       goto failed;
+    }
+
+    if (iscsi_set_targetname(iscsi, target)) {
+       error_report("iSCSI: Failed to set target name.");
+       ret = -ENOMEM;
+       goto failed;
+    }
+
+    if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+       error_report("iSCSI: Failed to set session type to normal.");
+       ret = -ENOMEM;
+       goto failed;
+    }
+
+    iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+
+    task.iscsilun = iscsilun;
+    task.status = 0;
+    task.complete = 0;
+
+    iscsilun->iscsi = iscsi;
+    iscsilun->lun   = lun;
+
+    if (iscsi_full_connect_async(iscsi, host, lun, iscsi_connect_cb, &task)
+       != 0) {
+       error_report("iSCSI: Failed to start async connect.");
+       ret = -ENOMEM;
+       goto failed;
+    }
+    async_context_push();
+    while (!task.complete) {
+       iscsi_set_events(iscsilun);
+       qemu_aio_wait();
+    }
+    async_context_pop();
+    if (task.status != 0) {
+       error_report("iSCSI: Failed to connect to LUN : %s",
+                    iscsi_get_error(iscsi));
+       ret = -EINVAL;
+       goto failed;
+    }
+
+    return 0;
+
+failed:
+    qemu_free(url);
+    if (iscsi != NULL) {
+       iscsi_destroy_context(iscsi);
+    }
+    bzero(iscsilun, sizeof(ISCSILUN));
+    return ret;
+}
+
+static void iscsi_close(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
+    iscsi_destroy_context(iscsi);
+    bzero(iscsilun, sizeof(ISCSILUN));
+}
+
+static BlockDriver bdrv_iscsi = {
+    .format_name     = "iscsi",
+    .protocol_name   = "iscsi",
+
+    .instance_size   = sizeof(ISCSILUN),
+    .bdrv_file_open  = iscsi_open,
+    .bdrv_close      = iscsi_close,
+    .bdrv_flush      = iscsi_flush,
+
+    .bdrv_getlength  = iscsi_getlength,
+
+    .bdrv_aio_readv  = iscsi_aio_readv,
+    .bdrv_aio_writev = iscsi_aio_writev,
+
+    .bdrv_is_inserted = iscsi_is_inserted,
+};
+
+static void iscsi_block_init(void)
+{
+    bdrv_register(&bdrv_iscsi);
+}
+
+block_init(iscsi_block_init);
+
+#endif
diff --git a/configure b/configure
index 2917874..93864ca 100755
--- a/configure
+++ b/configure
@@ -332,6 +332,7 @@ zero_malloc=""
 trace_backend="nop"
 trace_file="trace"
 spice=""
+libiscsi=""
 
 # OS specific
 if check_define __linux__ ; then
@@ -648,6 +649,10 @@ for opt do
   ;;
   --enable-spice) spice="yes"
   ;;
+  --disable-libiscsi) libiscsi="no"
+  ;;
+  --enable-libiscsi) libiscsi="yes"
+  ;;
   --enable-profiler) profiler="yes"
   ;;
   --enable-cocoa)
@@ -934,6 +939,8 @@ echo "  --trace-file=NAME        Full PATH,NAME of file to 
store traces"
 echo "                           Default:trace-<pid>"
 echo "  --disable-spice          disable spice"
 echo "  --enable-spice           enable spice"
+echo "  --disable-libiscsi       disable iscsi support"
+echo "  --enable-libiscsi        enable iscsi support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is 
launched"
 exit 1
@@ -2123,6 +2130,23 @@ EOF
   fi
 fi
 
+# libiscsi probe
+if test "$libiscsi" != "no" ; then
+  cat > $TMPC << EOF
+#include <iscsi/iscsi.h>
+int main(void) { iscsi_create_context(""); return 0; }
+EOF
+  if compile_prog "-Werror" "-liscsi" ; then
+    libiscsi="yes"
+    LIBS="$LIBS -liscsi"
+  else
+    if test "$libiscsi" = "yes" ; then
+      feature_not_found "libiscsi"
+    fi
+    libiscsi="no"
+  fi
+fi
+
 ##########################################
 
 ##########################################
@@ -2354,6 +2378,7 @@ echo "vhost-net support $vhost_net"
 echo "Trace backend     $trace_backend"
 echo "Trace output file $trace_file-<pid>"
 echo "spice support     $spice"
+echo "libiscsi support  $libiscsi"
 
 if test $sdl_too_old = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2617,6 +2642,10 @@ if test "$spice" = "yes" ; then
   echo "CONFIG_SPICE=y" >> $config_host_mak
 fi
 
+if test "$libiscsi" = "yes" ; then
+  echo "CONFIG_LIBISCSI=y" >> $config_host_mak
+fi
+
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
   echo "CONFIG_BSD=y" >> $config_host_mak
-- 
1.7.3.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]