76daa3
From 26e2ce51252b84b36962ba786de9298415539720 Mon Sep 17 00:00:00 2001
76daa3
From: Jeffrey Cody <jcody@redhat.com>
76daa3
Date: Mon, 8 May 2017 22:11:13 +0200
76daa3
Subject: [PATCH 3/7] block/vxhs.c: Add support for a new block device type
76daa3
 called "vxhs"
76daa3
76daa3
RH-Author: Jeffrey Cody <jcody@redhat.com>
76daa3
Message-id: <3ad63ba3f75889d8c8dcbbaf53ebe5ab875a224c.1494281291.git.jcody@redhat.com>
76daa3
Patchwork-id: 75045
76daa3
O-Subject: [RHEV-7.4 qemu-kvm-rhev 1/4] block/vxhs.c: Add support for a new block device type called "vxhs"
76daa3
Bugzilla: 1265869
76daa3
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
76daa3
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
76daa3
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
76daa3
From: Ashish Mittal <ashmit602@gmail.com>
76daa3
76daa3
Source code for the qnio library that this code loads can be downloaded from:
76daa3
https://github.com/VeritasHyperScale/libqnio.git
76daa3
76daa3
Sample command line using JSON syntax:
76daa3
./x86_64-softmmu/qemu-system-x86_64 -name instance-00000008 -S -vnc 0.0.0.0:0
76daa3
-k en-us -vga cirrus -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5
76daa3
-msg timestamp=on
76daa3
'json:{"driver":"vxhs","vdisk-id":"c3e9095a-a5ee-4dce-afeb-2a59fb387410",
76daa3
"server":{"host":"172.172.17.4","port":"9999"}}'
76daa3
76daa3
Sample command line using URI syntax:
76daa3
qemu-img convert -f raw -O raw -n
76daa3
/var/lib/nova/instances/_base/0c5eacd5ebea5ed914b6a3e7b18f1ce734c386ad
76daa3
vxhs://192.168.0.1:9999/c6718f6b-0401-441d-a8c3-1f0064d75ee0
76daa3
76daa3
Sample command line using TLS credentials (run in secure mode):
76daa3
./qemu-io --object
76daa3
tls-creds-x509,id=tls0,dir=/etc/pki/qemu/vxhs,endpoint=client -c 'read
76daa3
-v 66000 2.5k' 'json:{"server.host": "127.0.0.1", "server.port": "9999",
76daa3
"vdisk-id": "/test.raw", "driver": "vxhs", "tls-creds":"tls0"}'
76daa3
76daa3
[Jeff: Modified trace-events with the correct string formatting]
76daa3
76daa3
Signed-off-by: Ashish Mittal <Ashish.Mittal@veritas.com>
76daa3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
76daa3
Reviewed-by: Jeff Cody <jcody@redhat.com>
76daa3
Signed-off-by: Jeff Cody <jcody@redhat.com>
76daa3
Message-id: 1491277689-24949-2-git-send-email-Ashish.Mittal@veritas.com
76daa3
(cherry picked from commit da92c3ff60b20392dd34902fb96304c4633b5fba)
76daa3
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
---
76daa3
 block/Makefile.objs  |   2 +
76daa3
 block/trace-events   |  17 ++
76daa3
 block/vxhs.c         | 575 +++++++++++++++++++++++++++++++++++++++++++++++++++
76daa3
 configure            |  39 ++++
76daa3
 qapi/block-core.json |  23 ++-
76daa3
 5 files changed, 654 insertions(+), 2 deletions(-)
76daa3
 create mode 100644 block/vxhs.c
76daa3
76daa3
diff --git a/block/Makefile.objs b/block/Makefile.objs
76daa3
index de96f8e..ea95530 100644
76daa3
--- a/block/Makefile.objs
76daa3
+++ b/block/Makefile.objs
76daa3
@@ -19,6 +19,7 @@ block-obj-$(CONFIG_LIBNFS) += nfs.o
76daa3
 block-obj-$(CONFIG_CURL) += curl.o
76daa3
 block-obj-$(CONFIG_RBD) += rbd.o
76daa3
 block-obj-$(CONFIG_GLUSTERFS) += gluster.o
76daa3
+block-obj-$(CONFIG_VXHS) += vxhs.o
76daa3
 block-obj-$(CONFIG_LIBSSH2) += ssh.o
76daa3
 block-obj-y += accounting.o dirty-bitmap.o
76daa3
 block-obj-y += write-threshold.o
76daa3
@@ -38,6 +39,7 @@ rbd.o-cflags       := $(RBD_CFLAGS)
76daa3
 rbd.o-libs         := $(RBD_LIBS)
76daa3
 gluster.o-cflags   := $(GLUSTERFS_CFLAGS)
76daa3
 gluster.o-libs     := $(GLUSTERFS_LIBS)
76daa3
+vxhs.o-libs        := $(VXHS_LIBS)
76daa3
 ssh.o-cflags       := $(LIBSSH2_CFLAGS)
76daa3
 ssh.o-libs         := $(LIBSSH2_LIBS)
76daa3
 block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
76daa3
diff --git a/block/trace-events b/block/trace-events
76daa3
index 0bc5c0a..9a71c7f 100644
76daa3
--- a/block/trace-events
76daa3
+++ b/block/trace-events
76daa3
@@ -110,3 +110,20 @@ qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s
76daa3
 qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
76daa3
 qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
76daa3
 qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
76daa3
+
76daa3
+# block/vxhs.c
76daa3
+vxhs_iio_callback(int error) "ctx is NULL: error %d"
76daa3
+vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d"
76daa3
+vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d"
76daa3
+vxhs_aio_rw_invalid(int req) "Invalid I/O request iodir %d"
76daa3
+vxhs_aio_rw_ioerr(char *guid, int iodir, uint64_t size, uint64_t off, void *acb, int ret, int err) "IO ERROR (vDisk %s) FOR : Read/Write = %d size = %"PRIu64" offset = %"PRIu64" ACB = %p. Error = %d, errno = %d"
76daa3
+vxhs_get_vdisk_stat_err(char *guid, int ret, int err) "vDisk (%s) stat ioctl failed, ret = %d, errno = %d"
76daa3
+vxhs_get_vdisk_stat(char *vdisk_guid, uint64_t vdisk_size) "vDisk %s stat ioctl returned size %"PRIu64
76daa3
+vxhs_complete_aio(void *acb, uint64_t ret) "aio failed acb %p ret %"PRIu64
76daa3
+vxhs_parse_uri_filename(const char *filename) "URI passed via bdrv_parse_filename %s"
76daa3
+vxhs_open_vdiskid(const char *vdisk_id) "Opening vdisk-id %s"
76daa3
+vxhs_open_hostinfo(char *of_vsa_addr, int port) "Adding host %s:%d to BDRVVXHSState"
76daa3
+vxhs_open_iio_open(const char *host) "Failed to connect to storage agent on host %s"
76daa3
+vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d"
76daa3
+vxhs_close(char *vdisk_guid) "Closing vdisk %s"
76daa3
+vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s"
76daa3
diff --git a/block/vxhs.c b/block/vxhs.c
76daa3
new file mode 100644
76daa3
index 0000000..9ffe9d3
76daa3
--- /dev/null
76daa3
+++ b/block/vxhs.c
76daa3
@@ -0,0 +1,575 @@
76daa3
+/*
76daa3
+ * QEMU Block driver for Veritas HyperScale (VxHS)
76daa3
+ *
76daa3
+ * Copyright (c) 2017 Veritas Technologies LLC.
76daa3
+ *
76daa3
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
76daa3
+ * See the COPYING file in the top-level directory.
76daa3
+ *
76daa3
+ */
76daa3
+
76daa3
+#include "qemu/osdep.h"
76daa3
+#include <qnio/qnio_api.h>
76daa3
+#include <sys/param.h>
76daa3
+#include "block/block_int.h"
76daa3
+#include "qapi/qmp/qerror.h"
76daa3
+#include "qapi/qmp/qdict.h"
76daa3
+#include "qapi/qmp/qstring.h"
76daa3
+#include "trace.h"
76daa3
+#include "qemu/uri.h"
76daa3
+#include "qapi/error.h"
76daa3
+#include "qemu/uuid.h"
76daa3
+#include "crypto/tlscredsx509.h"
76daa3
+
76daa3
+#define VXHS_OPT_FILENAME           "filename"
76daa3
+#define VXHS_OPT_VDISK_ID           "vdisk-id"
76daa3
+#define VXHS_OPT_SERVER             "server"
76daa3
+#define VXHS_OPT_HOST               "host"
76daa3
+#define VXHS_OPT_PORT               "port"
76daa3
+
76daa3
+/* Only accessed under QEMU global mutex */
76daa3
+static uint32_t vxhs_ref;
76daa3
+
76daa3
+typedef enum {
76daa3
+    VDISK_AIO_READ,
76daa3
+    VDISK_AIO_WRITE,
76daa3
+} VDISKAIOCmd;
76daa3
+
76daa3
+/*
76daa3
+ * HyperScale AIO callbacks structure
76daa3
+ */
76daa3
+typedef struct VXHSAIOCB {
76daa3
+    BlockAIOCB common;
76daa3
+    int err;
76daa3
+} VXHSAIOCB;
76daa3
+
76daa3
+typedef struct VXHSvDiskHostsInfo {
76daa3
+    void *dev_handle; /* Device handle */
76daa3
+    char *host; /* Host name or IP */
76daa3
+    int port; /* Host's port number */
76daa3
+} VXHSvDiskHostsInfo;
76daa3
+
76daa3
+/*
76daa3
+ * Structure per vDisk maintained for state
76daa3
+ */
76daa3
+typedef struct BDRVVXHSState {
76daa3
+    VXHSvDiskHostsInfo vdisk_hostinfo; /* Per host info */
76daa3
+    char *vdisk_guid;
76daa3
+    char *tlscredsid; /* tlscredsid */
76daa3
+} BDRVVXHSState;
76daa3
+
76daa3
+static void vxhs_complete_aio_bh(void *opaque)
76daa3
+{
76daa3
+    VXHSAIOCB *acb = opaque;
76daa3
+    BlockCompletionFunc *cb = acb->common.cb;
76daa3
+    void *cb_opaque = acb->common.opaque;
76daa3
+    int ret = 0;
76daa3
+
76daa3
+    if (acb->err != 0) {
76daa3
+        trace_vxhs_complete_aio(acb, acb->err);
76daa3
+        ret = (-EIO);
76daa3
+    }
76daa3
+
76daa3
+    qemu_aio_unref(acb);
76daa3
+    cb(cb_opaque, ret);
76daa3
+}
76daa3
+
76daa3
+/*
76daa3
+ * Called from a libqnio thread
76daa3
+ */
76daa3
+static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error)
76daa3
+{
76daa3
+    VXHSAIOCB *acb = NULL;
76daa3
+
76daa3
+    switch (opcode) {
76daa3
+    case IRP_READ_REQUEST:
76daa3
+    case IRP_WRITE_REQUEST:
76daa3
+
76daa3
+        /*
76daa3
+         * ctx is VXHSAIOCB*
76daa3
+         * ctx is NULL if error is QNIOERROR_CHANNEL_HUP
76daa3
+         */
76daa3
+        if (ctx) {
76daa3
+            acb = ctx;
76daa3
+        } else {
76daa3
+            trace_vxhs_iio_callback(error);
76daa3
+            goto out;
76daa3
+        }
76daa3
+
76daa3
+        if (error) {
76daa3
+            if (!acb->err) {
76daa3
+                acb->err = error;
76daa3
+            }
76daa3
+            trace_vxhs_iio_callback(error);
76daa3
+        }
76daa3
+
76daa3
+        aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
76daa3
+                                vxhs_complete_aio_bh, acb);
76daa3
+        break;
76daa3
+
76daa3
+    default:
76daa3
+        if (error == QNIOERROR_HUP) {
76daa3
+            /*
76daa3
+             * Channel failed, spontaneous notification,
76daa3
+             * not in response to I/O
76daa3
+             */
76daa3
+            trace_vxhs_iio_callback_chnfail(error, errno);
76daa3
+        } else {
76daa3
+            trace_vxhs_iio_callback_unknwn(opcode, error);
76daa3
+        }
76daa3
+        break;
76daa3
+    }
76daa3
+out:
76daa3
+    return;
76daa3
+}
76daa3
+
76daa3
+static QemuOptsList runtime_opts = {
76daa3
+    .name = "vxhs",
76daa3
+    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
76daa3
+    .desc = {
76daa3
+        {
76daa3
+            .name = VXHS_OPT_FILENAME,
76daa3
+            .type = QEMU_OPT_STRING,
76daa3
+            .help = "URI to the Veritas HyperScale image",
76daa3
+        },
76daa3
+        {
76daa3
+            .name = VXHS_OPT_VDISK_ID,
76daa3
+            .type = QEMU_OPT_STRING,
76daa3
+            .help = "UUID of the VxHS vdisk",
76daa3
+        },
76daa3
+        {
76daa3
+            .name = "tls-creds",
76daa3
+            .type = QEMU_OPT_STRING,
76daa3
+            .help = "ID of the TLS/SSL credentials to use",
76daa3
+        },
76daa3
+        { /* end of list */ }
76daa3
+    },
76daa3
+};
76daa3
+
76daa3
+static QemuOptsList runtime_tcp_opts = {
76daa3
+    .name = "vxhs_tcp",
76daa3
+    .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
76daa3
+    .desc = {
76daa3
+        {
76daa3
+            .name = VXHS_OPT_HOST,
76daa3
+            .type = QEMU_OPT_STRING,
76daa3
+            .help = "host address (ipv4 addresses)",
76daa3
+        },
76daa3
+        {
76daa3
+            .name = VXHS_OPT_PORT,
76daa3
+            .type = QEMU_OPT_NUMBER,
76daa3
+            .help = "port number on which VxHSD is listening (default 9999)",
76daa3
+            .def_value_str = "9999"
76daa3
+        },
76daa3
+        { /* end of list */ }
76daa3
+    },
76daa3
+};
76daa3
+
76daa3
+/*
76daa3
+ * Parse incoming URI and populate *options with the host
76daa3
+ * and device information
76daa3
+ */
76daa3
+static int vxhs_parse_uri(const char *filename, QDict *options)
76daa3
+{
76daa3
+    URI *uri = NULL;
76daa3
+    char *port;
76daa3
+    int ret = 0;
76daa3
+
76daa3
+    trace_vxhs_parse_uri_filename(filename);
76daa3
+    uri = uri_parse(filename);
76daa3
+    if (!uri || !uri->server || !uri->path) {
76daa3
+        uri_free(uri);
76daa3
+        return -EINVAL;
76daa3
+    }
76daa3
+
76daa3
+    qdict_put(options, VXHS_OPT_SERVER".host", qstring_from_str(uri->server));
76daa3
+
76daa3
+    if (uri->port) {
76daa3
+        port = g_strdup_printf("%d", uri->port);
76daa3
+        qdict_put(options, VXHS_OPT_SERVER".port", qstring_from_str(port));
76daa3
+        g_free(port);
76daa3
+    }
76daa3
+
76daa3
+    qdict_put(options, "vdisk-id", qstring_from_str(uri->path));
76daa3
+
76daa3
+    trace_vxhs_parse_uri_hostinfo(uri->server, uri->port);
76daa3
+    uri_free(uri);
76daa3
+
76daa3
+    return ret;
76daa3
+}
76daa3
+
76daa3
+static void vxhs_parse_filename(const char *filename, QDict *options,
76daa3
+                                Error **errp)
76daa3
+{
76daa3
+    if (qdict_haskey(options, "vdisk-id") || qdict_haskey(options, "server")) {
76daa3
+        error_setg(errp, "vdisk-id/server and a file name may not be specified "
76daa3
+                         "at the same time");
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    if (strstr(filename, "://")) {
76daa3
+        int ret = vxhs_parse_uri(filename, options);
76daa3
+        if (ret < 0) {
76daa3
+            error_setg(errp, "Invalid URI. URI should be of the form "
76daa3
+                       "  vxhs://<host_ip>:<port>/<vdisk-id>");
76daa3
+        }
76daa3
+    }
76daa3
+}
76daa3
+
76daa3
+static int vxhs_init_and_ref(void)
76daa3
+{
76daa3
+    if (vxhs_ref++ == 0) {
76daa3
+        if (iio_init(QNIO_VERSION, vxhs_iio_callback)) {
76daa3
+            return -ENODEV;
76daa3
+        }
76daa3
+    }
76daa3
+    return 0;
76daa3
+}
76daa3
+
76daa3
+static void vxhs_unref(void)
76daa3
+{
76daa3
+    if (--vxhs_ref == 0) {
76daa3
+        iio_fini();
76daa3
+    }
76daa3
+}
76daa3
+
76daa3
+static void vxhs_get_tls_creds(const char *id, char **cacert,
76daa3
+                               char **key, char **cert, Error **errp)
76daa3
+{
76daa3
+    Object *obj;
76daa3
+    QCryptoTLSCreds *creds;
76daa3
+    QCryptoTLSCredsX509 *creds_x509;
76daa3
+
76daa3
+    obj = object_resolve_path_component(
76daa3
+        object_get_objects_root(), id);
76daa3
+
76daa3
+    if (!obj) {
76daa3
+        error_setg(errp, "No TLS credentials with id '%s'",
76daa3
+                   id);
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    creds_x509 = (QCryptoTLSCredsX509 *)
76daa3
+        object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS_X509);
76daa3
+
76daa3
+    if (!creds_x509) {
76daa3
+        error_setg(errp, "Object with id '%s' is not TLS credentials",
76daa3
+                   id);
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    creds = &creds_x509->parent_obj;
76daa3
+
76daa3
+    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
76daa3
+        error_setg(errp,
76daa3
+                   "Expecting TLS credentials with a client endpoint");
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    /*
76daa3
+     * Get the cacert, client_cert and client_key file names.
76daa3
+     */
76daa3
+    if (!creds->dir) {
76daa3
+        error_setg(errp, "TLS object missing 'dir' property value");
76daa3
+        return;
76daa3
+    }
76daa3
+
76daa3
+    *cacert = g_strdup_printf("%s/%s", creds->dir,
76daa3
+                              QCRYPTO_TLS_CREDS_X509_CA_CERT);
76daa3
+    *cert = g_strdup_printf("%s/%s", creds->dir,
76daa3
+                            QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
76daa3
+    *key = g_strdup_printf("%s/%s", creds->dir,
76daa3
+                           QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
76daa3
+}
76daa3
+
76daa3
+static int vxhs_open(BlockDriverState *bs, QDict *options,
76daa3
+                     int bdrv_flags, Error **errp)
76daa3
+{
76daa3
+    BDRVVXHSState *s = bs->opaque;
76daa3
+    void *dev_handlep;
76daa3
+    QDict *backing_options = NULL;
76daa3
+    QemuOpts *opts = NULL;
76daa3
+    QemuOpts *tcp_opts = NULL;
76daa3
+    char *of_vsa_addr = NULL;
76daa3
+    Error *local_err = NULL;
76daa3
+    const char *vdisk_id_opt;
76daa3
+    const char *server_host_opt;
76daa3
+    int ret = 0;
76daa3
+    char *cacert = NULL;
76daa3
+    char *client_key = NULL;
76daa3
+    char *client_cert = NULL;
76daa3
+
76daa3
+    ret = vxhs_init_and_ref();
76daa3
+    if (ret < 0) {
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    /* Create opts info from runtime_opts and runtime_tcp_opts list */
76daa3
+    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
76daa3
+    tcp_opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
76daa3
+
76daa3
+    qemu_opts_absorb_qdict(opts, options, &local_err);
76daa3
+    if (local_err) {
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    /* vdisk-id is the disk UUID */
76daa3
+    vdisk_id_opt = qemu_opt_get(opts, VXHS_OPT_VDISK_ID);
76daa3
+    if (!vdisk_id_opt) {
76daa3
+        error_setg(&local_err, QERR_MISSING_PARAMETER, VXHS_OPT_VDISK_ID);
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    /* vdisk-id may contain a leading '/' */
76daa3
+    if (strlen(vdisk_id_opt) > UUID_FMT_LEN + 1) {
76daa3
+        error_setg(&local_err, "vdisk-id cannot be more than %d characters",
76daa3
+                   UUID_FMT_LEN);
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    s->vdisk_guid = g_strdup(vdisk_id_opt);
76daa3
+    trace_vxhs_open_vdiskid(vdisk_id_opt);
76daa3
+
76daa3
+    /* get the 'server.' arguments */
76daa3
+    qdict_extract_subqdict(options, &backing_options, VXHS_OPT_SERVER".");
76daa3
+
76daa3
+    qemu_opts_absorb_qdict(tcp_opts, backing_options, &local_err);
76daa3
+    if (local_err != NULL) {
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    server_host_opt = qemu_opt_get(tcp_opts, VXHS_OPT_HOST);
76daa3
+    if (!server_host_opt) {
76daa3
+        error_setg(&local_err, QERR_MISSING_PARAMETER,
76daa3
+                   VXHS_OPT_SERVER"."VXHS_OPT_HOST);
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    if (strlen(server_host_opt) > MAXHOSTNAMELEN) {
76daa3
+        error_setg(&local_err, "server.host cannot be more than %d characters",
76daa3
+                   MAXHOSTNAMELEN);
76daa3
+        ret = -EINVAL;
76daa3
+        goto out;
76daa3
+    }
76daa3
+
76daa3
+    /* check if we got tls-creds via the --object argument */
76daa3
+    s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds"));
76daa3
+    if (s->tlscredsid) {
76daa3
+        vxhs_get_tls_creds(s->tlscredsid, &cacert, &client_key,
76daa3
+                           &client_cert, &local_err);
76daa3
+        if (local_err != NULL) {
76daa3
+            ret = -EINVAL;
76daa3
+            goto out;
76daa3
+        }
76daa3
+        trace_vxhs_get_creds(cacert, client_key, client_cert);
76daa3
+    }
76daa3
+
76daa3
+    s->vdisk_hostinfo.host = g_strdup(server_host_opt);
76daa3
+    s->vdisk_hostinfo.port = g_ascii_strtoll(qemu_opt_get(tcp_opts,
76daa3
+                                                          VXHS_OPT_PORT),
76daa3
+                                                          NULL, 0);
76daa3
+
76daa3
+    trace_vxhs_open_hostinfo(s->vdisk_hostinfo.host,
76daa3
+                             s->vdisk_hostinfo.port);
76daa3
+
76daa3
+    of_vsa_addr = g_strdup_printf("of://%s:%d",
76daa3
+                                  s->vdisk_hostinfo.host,
76daa3
+                                  s->vdisk_hostinfo.port);
76daa3
+
76daa3
+    /*
76daa3
+     * Open qnio channel to storage agent if not opened before
76daa3
+     */
76daa3
+    dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0,
76daa3
+                           cacert, client_key, client_cert);
76daa3
+    if (dev_handlep == NULL) {
76daa3
+        trace_vxhs_open_iio_open(of_vsa_addr);
76daa3
+        ret = -ENODEV;
76daa3
+        goto out;
76daa3
+    }
76daa3
+    s->vdisk_hostinfo.dev_handle = dev_handlep;
76daa3
+
76daa3
+out:
76daa3
+    g_free(of_vsa_addr);
76daa3
+    QDECREF(backing_options);
76daa3
+    qemu_opts_del(tcp_opts);
76daa3
+    qemu_opts_del(opts);
76daa3
+    g_free(cacert);
76daa3
+    g_free(client_key);
76daa3
+    g_free(client_cert);
76daa3
+
76daa3
+    if (ret < 0) {
76daa3
+        vxhs_unref();
76daa3
+        error_propagate(errp, local_err);
76daa3
+        g_free(s->vdisk_hostinfo.host);
76daa3
+        g_free(s->vdisk_guid);
76daa3
+        g_free(s->tlscredsid);
76daa3
+        s->vdisk_guid = NULL;
76daa3
+    }
76daa3
+
76daa3
+    return ret;
76daa3
+}
76daa3
+
76daa3
+static const AIOCBInfo vxhs_aiocb_info = {
76daa3
+    .aiocb_size = sizeof(VXHSAIOCB)
76daa3
+};
76daa3
+
76daa3
+/*
76daa3
+ * This allocates QEMU-VXHS callback for each IO
76daa3
+ * and is passed to QNIO. When QNIO completes the work,
76daa3
+ * it will be passed back through the callback.
76daa3
+ */
76daa3
+static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num,
76daa3
+                               QEMUIOVector *qiov, int nb_sectors,
76daa3
+                               BlockCompletionFunc *cb, void *opaque,
76daa3
+                               VDISKAIOCmd iodir)
76daa3
+{
76daa3
+    VXHSAIOCB *acb = NULL;
76daa3
+    BDRVVXHSState *s = bs->opaque;
76daa3
+    size_t size;
76daa3
+    uint64_t offset;
76daa3
+    int iio_flags = 0;
76daa3
+    int ret = 0;
76daa3
+    void *dev_handle = s->vdisk_hostinfo.dev_handle;
76daa3
+
76daa3
+    offset = sector_num * BDRV_SECTOR_SIZE;
76daa3
+    size = nb_sectors * BDRV_SECTOR_SIZE;
76daa3
+    acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque);
76daa3
+
76daa3
+    /*
76daa3
+     * Initialize VXHSAIOCB.
76daa3
+     */
76daa3
+    acb->err = 0;
76daa3
+
76daa3
+    iio_flags = IIO_FLAG_ASYNC;
76daa3
+
76daa3
+    switch (iodir) {
76daa3
+    case VDISK_AIO_WRITE:
76daa3
+            ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
76daa3
+                             offset, (uint64_t)size, iio_flags);
76daa3
+            break;
76daa3
+    case VDISK_AIO_READ:
76daa3
+            ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
76daa3
+                            offset, (uint64_t)size, iio_flags);
76daa3
+            break;
76daa3
+    default:
76daa3
+            trace_vxhs_aio_rw_invalid(iodir);
76daa3
+            goto errout;
76daa3
+    }
76daa3
+
76daa3
+    if (ret != 0) {
76daa3
+        trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset,
76daa3
+                                acb, ret, errno);
76daa3
+        goto errout;
76daa3
+    }
76daa3
+    return &acb->common;
76daa3
+
76daa3
+errout:
76daa3
+    qemu_aio_unref(acb);
76daa3
+    return NULL;
76daa3
+}
76daa3
+
76daa3
+static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs,
76daa3
+                                   int64_t sector_num, QEMUIOVector *qiov,
76daa3
+                                   int nb_sectors,
76daa3
+                                   BlockCompletionFunc *cb, void *opaque)
76daa3
+{
76daa3
+    return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb,
76daa3
+                       opaque, VDISK_AIO_READ);
76daa3
+}
76daa3
+
76daa3
+static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs,
76daa3
+                                   int64_t sector_num, QEMUIOVector *qiov,
76daa3
+                                   int nb_sectors,
76daa3
+                                   BlockCompletionFunc *cb, void *opaque)
76daa3
+{
76daa3
+    return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors,
76daa3
+                       cb, opaque, VDISK_AIO_WRITE);
76daa3
+}
76daa3
+
76daa3
+static void vxhs_close(BlockDriverState *bs)
76daa3
+{
76daa3
+    BDRVVXHSState *s = bs->opaque;
76daa3
+
76daa3
+    trace_vxhs_close(s->vdisk_guid);
76daa3
+
76daa3
+    g_free(s->vdisk_guid);
76daa3
+    s->vdisk_guid = NULL;
76daa3
+
76daa3
+    /*
76daa3
+     * Close vDisk device
76daa3
+     */
76daa3
+    if (s->vdisk_hostinfo.dev_handle) {
76daa3
+        iio_close(s->vdisk_hostinfo.dev_handle);
76daa3
+        s->vdisk_hostinfo.dev_handle = NULL;
76daa3
+    }
76daa3
+
76daa3
+    vxhs_unref();
76daa3
+
76daa3
+    /*
76daa3
+     * Free the dynamically allocated host string etc
76daa3
+     */
76daa3
+    g_free(s->vdisk_hostinfo.host);
76daa3
+    g_free(s->tlscredsid);
76daa3
+    s->tlscredsid = NULL;
76daa3
+    s->vdisk_hostinfo.host = NULL;
76daa3
+    s->vdisk_hostinfo.port = 0;
76daa3
+}
76daa3
+
76daa3
+static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s)
76daa3
+{
76daa3
+    int64_t vdisk_size = -1;
76daa3
+    int ret = 0;
76daa3
+    void *dev_handle = s->vdisk_hostinfo.dev_handle;
76daa3
+
76daa3
+    ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0);
76daa3
+    if (ret < 0) {
76daa3
+        trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno);
76daa3
+        return -EIO;
76daa3
+    }
76daa3
+
76daa3
+    trace_vxhs_get_vdisk_stat(s->vdisk_guid, vdisk_size);
76daa3
+    return vdisk_size;
76daa3
+}
76daa3
+
76daa3
+/*
76daa3
+ * Returns the size of vDisk in bytes. This is required
76daa3
+ * by QEMU block upper block layer so that it is visible
76daa3
+ * to guest.
76daa3
+ */
76daa3
+static int64_t vxhs_getlength(BlockDriverState *bs)
76daa3
+{
76daa3
+    BDRVVXHSState *s = bs->opaque;
76daa3
+    int64_t vdisk_size;
76daa3
+
76daa3
+    vdisk_size = vxhs_get_vdisk_stat(s);
76daa3
+    if (vdisk_size < 0) {
76daa3
+        return -EIO;
76daa3
+    }
76daa3
+
76daa3
+    return vdisk_size;
76daa3
+}
76daa3
+
76daa3
+static BlockDriver bdrv_vxhs = {
76daa3
+    .format_name                  = "vxhs",
76daa3
+    .protocol_name                = "vxhs",
76daa3
+    .instance_size                = sizeof(BDRVVXHSState),
76daa3
+    .bdrv_file_open               = vxhs_open,
76daa3
+    .bdrv_parse_filename          = vxhs_parse_filename,
76daa3
+    .bdrv_close                   = vxhs_close,
76daa3
+    .bdrv_getlength               = vxhs_getlength,
76daa3
+    .bdrv_aio_readv               = vxhs_aio_readv,
76daa3
+    .bdrv_aio_writev              = vxhs_aio_writev,
76daa3
+};
76daa3
+
76daa3
+static void bdrv_vxhs_init(void)
76daa3
+{
76daa3
+    bdrv_register(&bdrv_vxhs);
76daa3
+}
76daa3
+
76daa3
+block_init(bdrv_vxhs_init);
76daa3
diff --git a/configure b/configure
76daa3
index a1842a8..47b2475 100755
76daa3
--- a/configure
76daa3
+++ b/configure
76daa3
@@ -321,6 +321,7 @@ numa=""
76daa3
 tcmalloc="no"
76daa3
 jemalloc="no"
76daa3
 replication="yes"
76daa3
+vxhs=""
76daa3
 
76daa3
 supported_cpu="no"
76daa3
 supported_os="no"
76daa3
@@ -1188,6 +1189,10 @@ for opt do
76daa3
   ;;
76daa3
   --enable-live-block-migration) live_block_migration="yes"
76daa3
   ;;
76daa3
+  --disable-vxhs) vxhs="no"
76daa3
+  ;;
76daa3
+  --enable-vxhs) vxhs="yes"
76daa3
+  ;;
76daa3
   *)
76daa3
       echo "ERROR: unknown option $opt"
76daa3
       echo "Try '$0 --help' for more information"
76daa3
@@ -1433,6 +1438,7 @@ disabled with --disable-FEATURE, default is enabled if available:
76daa3
   xfsctl          xfsctl support
76daa3
   qom-cast-debug  cast debugging support
76daa3
   tools           build qemu-io, qemu-nbd and qemu-image tools
76daa3
+  vxhs            Veritas HyperScale vDisk backend support
76daa3
 
76daa3
 NOTE: The object files are built at the place where configure is launched
76daa3
 EOF
76daa3
@@ -4787,6 +4793,33 @@ if compile_prog "" "" ; then
76daa3
 fi
76daa3
 
76daa3
 ##########################################
76daa3
+# Veritas HyperScale block driver VxHS
76daa3
+# Check if libvxhs is installed
76daa3
+
76daa3
+if test "$vxhs" != "no" ; then
76daa3
+  cat > $TMPC <
76daa3
+#include <stdint.h>
76daa3
+#include <qnio/qnio_api.h>
76daa3
+
76daa3
+void *vxhs_callback;
76daa3
+
76daa3
+int main(void) {
76daa3
+    iio_init(QNIO_VERSION, vxhs_callback);
76daa3
+    return 0;
76daa3
+}
76daa3
+EOF
76daa3
+  vxhs_libs="-lvxhs -lssl"
76daa3
+  if compile_prog "" "$vxhs_libs" ; then
76daa3
+    vxhs=yes
76daa3
+  else
76daa3
+    if test "$vxhs" = "yes" ; then
76daa3
+      feature_not_found "vxhs block device" "Install libvxhs See github"
76daa3
+    fi
76daa3
+    vxhs=no
76daa3
+  fi
76daa3
+fi
76daa3
+
76daa3
+##########################################
76daa3
 # End of CC checks
76daa3
 # After here, no more $cc or $ld runs
76daa3
 
76daa3
@@ -5153,6 +5186,7 @@ echo "tcmalloc support  $tcmalloc"
76daa3
 echo "jemalloc support  $jemalloc"
76daa3
 echo "avx2 optimization $avx2_opt"
76daa3
 echo "replication support $replication"
76daa3
+echo "VxHS block device $vxhs"
76daa3
 
76daa3
 if test "$sdl_too_old" = "yes"; then
76daa3
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
76daa3
@@ -5796,6 +5830,11 @@ if test "$pthread_setname_np" = "yes" ; then
76daa3
   echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak
76daa3
 fi
76daa3
 
76daa3
+if test "$vxhs" = "yes" ; then
76daa3
+  echo "CONFIG_VXHS=y" >> $config_host_mak
76daa3
+  echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak
76daa3
+fi
76daa3
+
76daa3
 if test "$tcg_interpreter" = "yes"; then
76daa3
   QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
76daa3
 elif test "$ARCH" = "sparc64" ; then
76daa3
diff --git a/qapi/block-core.json b/qapi/block-core.json
76daa3
index 68ad316..e9fcffb 100644
76daa3
--- a/qapi/block-core.json
76daa3
+++ b/qapi/block-core.json
76daa3
@@ -2108,6 +2108,8 @@
76daa3
 #
76daa3
 # Drivers that are supported in block device operations.
76daa3
 #
76daa3
+# @vxhs: Since 2.10
76daa3
+#
76daa3
 # Since: 2.9
76daa3
 ##
76daa3
 { 'enum': 'BlockdevDriver',
76daa3
@@ -2116,7 +2118,7 @@
76daa3
             'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
76daa3
             'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
76daa3
             'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
76daa3
-            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
76daa3
+            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
76daa3
 
76daa3
 ##
76daa3
 # @BlockdevOptionsFile:
76daa3
@@ -2866,6 +2868,22 @@
76daa3
   'data': { '*offset': 'int', '*size': 'int' } }
76daa3
 
76daa3
 ##
76daa3
+# @BlockdevOptionsVxHS:
76daa3
+#
76daa3
+# Driver specific block device options for VxHS
76daa3
+#
76daa3
+# @vdisk-id:    UUID of VxHS volume
76daa3
+# @server:      vxhs server IP, port
76daa3
+# @tls-creds:   TLS credentials ID
76daa3
+#
76daa3
+# Since: 2.10
76daa3
+##
76daa3
+{ 'struct': 'BlockdevOptionsVxHS',
76daa3
+  'data': { 'vdisk-id': 'str',
76daa3
+            'server': 'InetSocketAddressBase',
76daa3
+            '*tls-creds': 'str' } }
76daa3
+
76daa3
+##
76daa3
 # @BlockdevOptions:
76daa3
 #
76daa3
 # Options for creating a block device.  Many options are available for all
76daa3
@@ -2927,7 +2945,8 @@
76daa3
       'vhdx':       'BlockdevOptionsGenericFormat',
76daa3
       'vmdk':       'BlockdevOptionsGenericCOWFormat',
76daa3
       'vpc':        'BlockdevOptionsGenericFormat',
76daa3
-      'vvfat':      'BlockdevOptionsVVFAT'
76daa3
+      'vvfat':      'BlockdevOptionsVVFAT',
76daa3
+      'vxhs':       'BlockdevOptionsVxHS'
76daa3
   } }
76daa3
 
76daa3
 ##
76daa3
-- 
76daa3
1.8.3.1
76daa3