From da92c3ff60b20392dd34902fb96304c4633b5fba Mon Sep 17 00:00:00 2001 From: Ashish Mittal Date: Mon, 3 Apr 2017 20:48:08 -0700 Subject: [PATCH 01/12] block/vxhs.c: Add support for a new block device type called "vxhs" Source code for the qnio library that this code loads can be downloaded from: https://github.com/VeritasHyperScale/libqnio.git Sample command line using JSON syntax: ./x86_64-softmmu/qemu-system-x86_64 -name instance-00000008 -S -vnc 0.0.0.0:0 -k en-us -vga cirrus -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5 -msg timestamp=on 'json:{"driver":"vxhs","vdisk-id":"c3e9095a-a5ee-4dce-afeb-2a59fb387410", "server":{"host":"172.172.17.4","port":"9999"}}' Sample command line using URI syntax: qemu-img convert -f raw -O raw -n /var/lib/nova/instances/_base/0c5eacd5ebea5ed914b6a3e7b18f1ce734c386ad vxhs://192.168.0.1:9999/c6718f6b-0401-441d-a8c3-1f0064d75ee0 Sample command line using TLS credentials (run in secure mode): ./qemu-io --object tls-creds-x509,id=tls0,dir=/etc/pki/qemu/vxhs,endpoint=client -c 'read -v 66000 2.5k' 'json:{"server.host": "127.0.0.1", "server.port": "9999", "vdisk-id": "/test.raw", "driver": "vxhs", "tls-creds":"tls0"}' [Jeff: Modified trace-events with the correct string formatting] Signed-off-by: Ashish Mittal Reviewed-by: Stefan Hajnoczi Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody Message-id: 1491277689-24949-2-git-send-email-Ashish.Mittal@veritas.com --- block/Makefile.objs | 2 + block/trace-events | 17 ++ block/vxhs.c | 575 +++++++++++++++++++++++++++++++++++++++++++ configure | 39 +++ qapi/block-core.json | 23 +- 5 files changed, 654 insertions(+), 2 deletions(-) create mode 100644 block/vxhs.c diff --git a/block/Makefile.objs b/block/Makefile.objs index de96f8ee80..ea955302c8 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -19,6 +19,7 @@ block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o +block-obj-$(CONFIG_VXHS) += vxhs.o block-obj-$(CONFIG_LIBSSH2) += ssh.o block-obj-y += accounting.o dirty-bitmap.o block-obj-y += write-threshold.o @@ -38,6 +39,7 @@ rbd.o-cflags := $(RBD_CFLAGS) rbd.o-libs := $(RBD_LIBS) gluster.o-cflags := $(GLUSTERFS_CFLAGS) gluster.o-libs := $(GLUSTERFS_LIBS) +vxhs.o-libs := $(VXHS_LIBS) ssh.o-cflags := $(LIBSSH2_CFLAGS) ssh.o-libs := $(LIBSSH2_LIBS) block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o diff --git a/block/trace-events b/block/trace-events index 0bc5c0adf1..9a71c7fb04 100644 --- a/block/trace-events +++ b/block/trace-events @@ -110,3 +110,20 @@ qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s 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 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 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" + +# block/vxhs.c +vxhs_iio_callback(int error) "ctx is NULL: error %d" +vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d" +vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d" +vxhs_aio_rw_invalid(int req) "Invalid I/O request iodir %d" +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" +vxhs_get_vdisk_stat_err(char *guid, int ret, int err) "vDisk (%s) stat ioctl failed, ret = %d, errno = %d" +vxhs_get_vdisk_stat(char *vdisk_guid, uint64_t vdisk_size) "vDisk %s stat ioctl returned size %"PRIu64 +vxhs_complete_aio(void *acb, uint64_t ret) "aio failed acb %p ret %"PRIu64 +vxhs_parse_uri_filename(const char *filename) "URI passed via bdrv_parse_filename %s" +vxhs_open_vdiskid(const char *vdisk_id) "Opening vdisk-id %s" +vxhs_open_hostinfo(char *of_vsa_addr, int port) "Adding host %s:%d to BDRVVXHSState" +vxhs_open_iio_open(const char *host) "Failed to connect to storage agent on host %s" +vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d" +vxhs_close(char *vdisk_guid) "Closing vdisk %s" +vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s" diff --git a/block/vxhs.c b/block/vxhs.c new file mode 100644 index 0000000000..9ffe9d3814 --- /dev/null +++ b/block/vxhs.c @@ -0,0 +1,575 @@ +/* + * QEMU Block driver for Veritas HyperScale (VxHS) + * + * Copyright (c) 2017 Veritas Technologies LLC. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include +#include +#include "block/block_int.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "trace.h" +#include "qemu/uri.h" +#include "qapi/error.h" +#include "qemu/uuid.h" +#include "crypto/tlscredsx509.h" + +#define VXHS_OPT_FILENAME "filename" +#define VXHS_OPT_VDISK_ID "vdisk-id" +#define VXHS_OPT_SERVER "server" +#define VXHS_OPT_HOST "host" +#define VXHS_OPT_PORT "port" + +/* Only accessed under QEMU global mutex */ +static uint32_t vxhs_ref; + +typedef enum { + VDISK_AIO_READ, + VDISK_AIO_WRITE, +} VDISKAIOCmd; + +/* + * HyperScale AIO callbacks structure + */ +typedef struct VXHSAIOCB { + BlockAIOCB common; + int err; +} VXHSAIOCB; + +typedef struct VXHSvDiskHostsInfo { + void *dev_handle; /* Device handle */ + char *host; /* Host name or IP */ + int port; /* Host's port number */ +} VXHSvDiskHostsInfo; + +/* + * Structure per vDisk maintained for state + */ +typedef struct BDRVVXHSState { + VXHSvDiskHostsInfo vdisk_hostinfo; /* Per host info */ + char *vdisk_guid; + char *tlscredsid; /* tlscredsid */ +} BDRVVXHSState; + +static void vxhs_complete_aio_bh(void *opaque) +{ + VXHSAIOCB *acb = opaque; + BlockCompletionFunc *cb = acb->common.cb; + void *cb_opaque = acb->common.opaque; + int ret = 0; + + if (acb->err != 0) { + trace_vxhs_complete_aio(acb, acb->err); + ret = (-EIO); + } + + qemu_aio_unref(acb); + cb(cb_opaque, ret); +} + +/* + * Called from a libqnio thread + */ +static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error) +{ + VXHSAIOCB *acb = NULL; + + switch (opcode) { + case IRP_READ_REQUEST: + case IRP_WRITE_REQUEST: + + /* + * ctx is VXHSAIOCB* + * ctx is NULL if error is QNIOERROR_CHANNEL_HUP + */ + if (ctx) { + acb = ctx; + } else { + trace_vxhs_iio_callback(error); + goto out; + } + + if (error) { + if (!acb->err) { + acb->err = error; + } + trace_vxhs_iio_callback(error); + } + + aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), + vxhs_complete_aio_bh, acb); + break; + + default: + if (error == QNIOERROR_HUP) { + /* + * Channel failed, spontaneous notification, + * not in response to I/O + */ + trace_vxhs_iio_callback_chnfail(error, errno); + } else { + trace_vxhs_iio_callback_unknwn(opcode, error); + } + break; + } +out: + return; +} + +static QemuOptsList runtime_opts = { + .name = "vxhs", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = VXHS_OPT_FILENAME, + .type = QEMU_OPT_STRING, + .help = "URI to the Veritas HyperScale image", + }, + { + .name = VXHS_OPT_VDISK_ID, + .type = QEMU_OPT_STRING, + .help = "UUID of the VxHS vdisk", + }, + { + .name = "tls-creds", + .type = QEMU_OPT_STRING, + .help = "ID of the TLS/SSL credentials to use", + }, + { /* end of list */ } + }, +}; + +static QemuOptsList runtime_tcp_opts = { + .name = "vxhs_tcp", + .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head), + .desc = { + { + .name = VXHS_OPT_HOST, + .type = QEMU_OPT_STRING, + .help = "host address (ipv4 addresses)", + }, + { + .name = VXHS_OPT_PORT, + .type = QEMU_OPT_NUMBER, + .help = "port number on which VxHSD is listening (default 9999)", + .def_value_str = "9999" + }, + { /* end of list */ } + }, +}; + +/* + * Parse incoming URI and populate *options with the host + * and device information + */ +static int vxhs_parse_uri(const char *filename, QDict *options) +{ + URI *uri = NULL; + char *port; + int ret = 0; + + trace_vxhs_parse_uri_filename(filename); + uri = uri_parse(filename); + if (!uri || !uri->server || !uri->path) { + uri_free(uri); + return -EINVAL; + } + + qdict_put(options, VXHS_OPT_SERVER".host", qstring_from_str(uri->server)); + + if (uri->port) { + port = g_strdup_printf("%d", uri->port); + qdict_put(options, VXHS_OPT_SERVER".port", qstring_from_str(port)); + g_free(port); + } + + qdict_put(options, "vdisk-id", qstring_from_str(uri->path)); + + trace_vxhs_parse_uri_hostinfo(uri->server, uri->port); + uri_free(uri); + + return ret; +} + +static void vxhs_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + if (qdict_haskey(options, "vdisk-id") || qdict_haskey(options, "server")) { + error_setg(errp, "vdisk-id/server and a file name may not be specified " + "at the same time"); + return; + } + + if (strstr(filename, "://")) { + int ret = vxhs_parse_uri(filename, options); + if (ret < 0) { + error_setg(errp, "Invalid URI. URI should be of the form " + " vxhs://:/"); + } + } +} + +static int vxhs_init_and_ref(void) +{ + if (vxhs_ref++ == 0) { + if (iio_init(QNIO_VERSION, vxhs_iio_callback)) { + return -ENODEV; + } + } + return 0; +} + +static void vxhs_unref(void) +{ + if (--vxhs_ref == 0) { + iio_fini(); + } +} + +static void vxhs_get_tls_creds(const char *id, char **cacert, + char **key, char **cert, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + QCryptoTLSCredsX509 *creds_x509; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return; + } + + creds_x509 = (QCryptoTLSCredsX509 *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS_X509); + + if (!creds_x509) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return; + } + + creds = &creds_x509->parent_obj; + + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "Expecting TLS credentials with a client endpoint"); + return; + } + + /* + * Get the cacert, client_cert and client_key file names. + */ + if (!creds->dir) { + error_setg(errp, "TLS object missing 'dir' property value"); + return; + } + + *cacert = g_strdup_printf("%s/%s", creds->dir, + QCRYPTO_TLS_CREDS_X509_CA_CERT); + *cert = g_strdup_printf("%s/%s", creds->dir, + QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + *key = g_strdup_printf("%s/%s", creds->dir, + QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); +} + +static int vxhs_open(BlockDriverState *bs, QDict *options, + int bdrv_flags, Error **errp) +{ + BDRVVXHSState *s = bs->opaque; + void *dev_handlep; + QDict *backing_options = NULL; + QemuOpts *opts = NULL; + QemuOpts *tcp_opts = NULL; + char *of_vsa_addr = NULL; + Error *local_err = NULL; + const char *vdisk_id_opt; + const char *server_host_opt; + int ret = 0; + char *cacert = NULL; + char *client_key = NULL; + char *client_cert = NULL; + + ret = vxhs_init_and_ref(); + if (ret < 0) { + ret = -EINVAL; + goto out; + } + + /* Create opts info from runtime_opts and runtime_tcp_opts list */ + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + tcp_opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort); + + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + ret = -EINVAL; + goto out; + } + + /* vdisk-id is the disk UUID */ + vdisk_id_opt = qemu_opt_get(opts, VXHS_OPT_VDISK_ID); + if (!vdisk_id_opt) { + error_setg(&local_err, QERR_MISSING_PARAMETER, VXHS_OPT_VDISK_ID); + ret = -EINVAL; + goto out; + } + + /* vdisk-id may contain a leading '/' */ + if (strlen(vdisk_id_opt) > UUID_FMT_LEN + 1) { + error_setg(&local_err, "vdisk-id cannot be more than %d characters", + UUID_FMT_LEN); + ret = -EINVAL; + goto out; + } + + s->vdisk_guid = g_strdup(vdisk_id_opt); + trace_vxhs_open_vdiskid(vdisk_id_opt); + + /* get the 'server.' arguments */ + qdict_extract_subqdict(options, &backing_options, VXHS_OPT_SERVER"."); + + qemu_opts_absorb_qdict(tcp_opts, backing_options, &local_err); + if (local_err != NULL) { + ret = -EINVAL; + goto out; + } + + server_host_opt = qemu_opt_get(tcp_opts, VXHS_OPT_HOST); + if (!server_host_opt) { + error_setg(&local_err, QERR_MISSING_PARAMETER, + VXHS_OPT_SERVER"."VXHS_OPT_HOST); + ret = -EINVAL; + goto out; + } + + if (strlen(server_host_opt) > MAXHOSTNAMELEN) { + error_setg(&local_err, "server.host cannot be more than %d characters", + MAXHOSTNAMELEN); + ret = -EINVAL; + goto out; + } + + /* check if we got tls-creds via the --object argument */ + s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); + if (s->tlscredsid) { + vxhs_get_tls_creds(s->tlscredsid, &cacert, &client_key, + &client_cert, &local_err); + if (local_err != NULL) { + ret = -EINVAL; + goto out; + } + trace_vxhs_get_creds(cacert, client_key, client_cert); + } + + s->vdisk_hostinfo.host = g_strdup(server_host_opt); + s->vdisk_hostinfo.port = g_ascii_strtoll(qemu_opt_get(tcp_opts, + VXHS_OPT_PORT), + NULL, 0); + + trace_vxhs_open_hostinfo(s->vdisk_hostinfo.host, + s->vdisk_hostinfo.port); + + of_vsa_addr = g_strdup_printf("of://%s:%d", + s->vdisk_hostinfo.host, + s->vdisk_hostinfo.port); + + /* + * Open qnio channel to storage agent if not opened before + */ + dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0, + cacert, client_key, client_cert); + if (dev_handlep == NULL) { + trace_vxhs_open_iio_open(of_vsa_addr); + ret = -ENODEV; + goto out; + } + s->vdisk_hostinfo.dev_handle = dev_handlep; + +out: + g_free(of_vsa_addr); + QDECREF(backing_options); + qemu_opts_del(tcp_opts); + qemu_opts_del(opts); + g_free(cacert); + g_free(client_key); + g_free(client_cert); + + if (ret < 0) { + vxhs_unref(); + error_propagate(errp, local_err); + g_free(s->vdisk_hostinfo.host); + g_free(s->vdisk_guid); + g_free(s->tlscredsid); + s->vdisk_guid = NULL; + } + + return ret; +} + +static const AIOCBInfo vxhs_aiocb_info = { + .aiocb_size = sizeof(VXHSAIOCB) +}; + +/* + * This allocates QEMU-VXHS callback for each IO + * and is passed to QNIO. When QNIO completes the work, + * it will be passed back through the callback. + */ +static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque, + VDISKAIOCmd iodir) +{ + VXHSAIOCB *acb = NULL; + BDRVVXHSState *s = bs->opaque; + size_t size; + uint64_t offset; + int iio_flags = 0; + int ret = 0; + void *dev_handle = s->vdisk_hostinfo.dev_handle; + + offset = sector_num * BDRV_SECTOR_SIZE; + size = nb_sectors * BDRV_SECTOR_SIZE; + acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); + + /* + * Initialize VXHSAIOCB. + */ + acb->err = 0; + + iio_flags = IIO_FLAG_ASYNC; + + switch (iodir) { + case VDISK_AIO_WRITE: + ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, + offset, (uint64_t)size, iio_flags); + break; + case VDISK_AIO_READ: + ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, + offset, (uint64_t)size, iio_flags); + break; + default: + trace_vxhs_aio_rw_invalid(iodir); + goto errout; + } + + if (ret != 0) { + trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset, + acb, ret, errno); + goto errout; + } + return &acb->common; + +errout: + qemu_aio_unref(acb); + return NULL; +} + +static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb, + opaque, VDISK_AIO_READ); +} + +static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockCompletionFunc *cb, void *opaque) +{ + return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, + cb, opaque, VDISK_AIO_WRITE); +} + +static void vxhs_close(BlockDriverState *bs) +{ + BDRVVXHSState *s = bs->opaque; + + trace_vxhs_close(s->vdisk_guid); + + g_free(s->vdisk_guid); + s->vdisk_guid = NULL; + + /* + * Close vDisk device + */ + if (s->vdisk_hostinfo.dev_handle) { + iio_close(s->vdisk_hostinfo.dev_handle); + s->vdisk_hostinfo.dev_handle = NULL; + } + + vxhs_unref(); + + /* + * Free the dynamically allocated host string etc + */ + g_free(s->vdisk_hostinfo.host); + g_free(s->tlscredsid); + s->tlscredsid = NULL; + s->vdisk_hostinfo.host = NULL; + s->vdisk_hostinfo.port = 0; +} + +static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s) +{ + int64_t vdisk_size = -1; + int ret = 0; + void *dev_handle = s->vdisk_hostinfo.dev_handle; + + ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0); + if (ret < 0) { + trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno); + return -EIO; + } + + trace_vxhs_get_vdisk_stat(s->vdisk_guid, vdisk_size); + return vdisk_size; +} + +/* + * Returns the size of vDisk in bytes. This is required + * by QEMU block upper block layer so that it is visible + * to guest. + */ +static int64_t vxhs_getlength(BlockDriverState *bs) +{ + BDRVVXHSState *s = bs->opaque; + int64_t vdisk_size; + + vdisk_size = vxhs_get_vdisk_stat(s); + if (vdisk_size < 0) { + return -EIO; + } + + return vdisk_size; +} + +static BlockDriver bdrv_vxhs = { + .format_name = "vxhs", + .protocol_name = "vxhs", + .instance_size = sizeof(BDRVVXHSState), + .bdrv_file_open = vxhs_open, + .bdrv_parse_filename = vxhs_parse_filename, + .bdrv_close = vxhs_close, + .bdrv_getlength = vxhs_getlength, + .bdrv_aio_readv = vxhs_aio_readv, + .bdrv_aio_writev = vxhs_aio_writev, +}; + +static void bdrv_vxhs_init(void) +{ + bdrv_register(&bdrv_vxhs); +} + +block_init(bdrv_vxhs_init); diff --git a/configure b/configure index 6db3044fb0..5c2accc826 100755 --- a/configure +++ b/configure @@ -320,6 +320,7 @@ numa="" tcmalloc="no" jemalloc="no" replication="yes" +vxhs="" supported_cpu="no" supported_os="no" @@ -1183,6 +1184,10 @@ for opt do ;; --enable-replication) replication="yes" ;; + --disable-vxhs) vxhs="no" + ;; + --enable-vxhs) vxhs="yes" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1415,6 +1420,7 @@ disabled with --disable-FEATURE, default is enabled if available: xfsctl xfsctl support qom-cast-debug cast debugging support tools build qemu-io, qemu-nbd and qemu-image tools + vxhs Veritas HyperScale vDisk backend support NOTE: The object files are built at the place where configure is launched EOF @@ -4783,6 +4789,33 @@ if compile_prog "" "" ; then have_sysmacros=yes fi +########################################## +# Veritas HyperScale block driver VxHS +# Check if libvxhs is installed + +if test "$vxhs" != "no" ; then + cat > $TMPC < +#include + +void *vxhs_callback; + +int main(void) { + iio_init(QNIO_VERSION, vxhs_callback); + return 0; +} +EOF + vxhs_libs="-lvxhs -lssl" + if compile_prog "" "$vxhs_libs" ; then + vxhs=yes + else + if test "$vxhs" = "yes" ; then + feature_not_found "vxhs block device" "Install libvxhs See github" + fi + vxhs=no + fi +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -5149,6 +5182,7 @@ echo "tcmalloc support $tcmalloc" echo "jemalloc support $jemalloc" echo "avx2 optimization $avx2_opt" echo "replication support $replication" +echo "VxHS block device $vxhs" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -5788,6 +5822,11 @@ if test "$pthread_setname_np" = "yes" ; then echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak fi +if test "$vxhs" = "yes" ; then + echo "CONFIG_VXHS=y" >> $config_host_mak + echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak +fi + if test "$tcg_interpreter" = "yes"; then QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" elif test "$ARCH" = "sparc64" ; then diff --git a/qapi/block-core.json b/qapi/block-core.json index 033457ce86..87fb747ab6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2108,6 +2108,8 @@ # # Drivers that are supported in block device operations. # +# @vxhs: Since 2.10 +# # Since: 2.9 ## { 'enum': 'BlockdevDriver', @@ -2116,7 +2118,7 @@ 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', - 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } + 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ## # @BlockdevOptionsFile: @@ -2865,6 +2867,22 @@ 'base': 'BlockdevOptionsGenericFormat', 'data': { '*offset': 'int', '*size': 'int' } } +## +# @BlockdevOptionsVxHS: +# +# Driver specific block device options for VxHS +# +# @vdisk-id: UUID of VxHS volume +# @server: vxhs server IP, port +# @tls-creds: TLS credentials ID +# +# Since: 2.10 +## +{ 'struct': 'BlockdevOptionsVxHS', + 'data': { 'vdisk-id': 'str', + 'server': 'InetSocketAddressBase', + '*tls-creds': 'str' } } + ## # @BlockdevOptions: # @@ -2927,7 +2945,8 @@ 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', 'vpc': 'BlockdevOptionsGenericFormat', - 'vvfat': 'BlockdevOptionsVVFAT' + 'vvfat': 'BlockdevOptionsVVFAT', + 'vxhs': 'BlockdevOptionsVxHS' } } ## From ae0c0a3decabba6b710d8e6902d43495f7490de2 Mon Sep 17 00:00:00 2001 From: Ashish Mittal Date: Mon, 3 Apr 2017 20:48:09 -0700 Subject: [PATCH 02/12] block/vxhs.c: Add qemu-iotests for new block device type "vxhs" These changes use a vxhs test server that is a part of the following repository: https://github.com/VeritasHyperScale/libqnio.git Signed-off-by: Ashish Mittal Reviewed-by: Stefan Hajnoczi Reviewed-by: Jeff Cody Signed-off-by: Jeff Cody Message-id: 1491277689-24949-3-git-send-email-Ashish.Mittal@veritas.com --- tests/qemu-iotests/common | 6 ++++++ tests/qemu-iotests/common.config | 13 +++++++++++++ tests/qemu-iotests/common.filter | 1 + tests/qemu-iotests/common.rc | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 4d5650d7c8..9c6f9721e5 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -157,6 +157,7 @@ check options -ssh test ssh -nfs test nfs -luks test luks + -vxhs test vxhs -xdiff graphical mode diff -nocache use O_DIRECT on backing file -misalign misalign memory allocations @@ -260,6 +261,11 @@ testlist options xpand=false ;; + -vxhs) + IMGPROTO=vxhs + xpand=false + ;; + -ssh) IMGPROTO=ssh xpand=false diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index 55527aac87..c4b51b3509 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -105,6 +105,10 @@ if [ -z "$QEMU_NBD_PROG" ]; then export QEMU_NBD_PROG="`set_prog_path qemu-nbd`" fi +if [ -z "$QEMU_VXHS_PROG" ]; then + export QEMU_VXHS_PROG="`set_prog_path qnio_server`" +fi + _qemu_wrapper() { ( @@ -156,10 +160,19 @@ _qemu_nbd_wrapper() ) } +_qemu_vxhs_wrapper() +{ + ( + echo $BASHPID > "${TEST_DIR}/qemu-vxhs.pid" + exec "$QEMU_VXHS_PROG" $QEMU_VXHS_OPTIONS "$@" + ) +} + export QEMU=_qemu_wrapper export QEMU_IMG=_qemu_img_wrapper export QEMU_IO=_qemu_io_wrapper export QEMU_NBD=_qemu_nbd_wrapper +export QEMU_VXHS=_qemu_vxhs_wrapper QEMU_IMG_EXTRA_ARGS= if [ "$IMGOPTSSYNTAX" = "true" ]; then diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 104001358b..c9a2d5c595 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -122,6 +122,7 @@ _filter_img_info() -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \ + -e 's#json.*vdisk-id.*vxhs"}}#TEST_DIR/t.IMGFMT#' \ -e "/encrypted: yes/d" \ -e "/cluster_size: [0-9]\\+/d" \ -e "/table_size: [0-9]\\+/d" \ diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 7d4781d4ad..62529eed6e 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -85,6 +85,9 @@ else elif [ "$IMGPROTO" = "nfs" ]; then TEST_DIR="nfs://127.0.0.1/$TEST_DIR" TEST_IMG=$TEST_DIR/t.$IMGFMT + elif [ "$IMGPROTO" = "vxhs" ]; then + TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT" else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi @@ -171,6 +174,12 @@ _make_test_img() eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE >/dev/null &" sleep 1 # FIXME: qemu-nbd needs to be listening before we continue fi + + # Start QNIO server on image directory for vxhs protocol + if [ $IMGPROTO = "vxhs" ]; then + eval "$QEMU_VXHS -d $TEST_DIR > /dev/null &" + sleep 1 # Wait for server to come up. + fi } _rm_test_img() @@ -197,6 +206,16 @@ _cleanup_test_img() fi rm -f "$TEST_IMG_FILE" ;; + vxhs) + if [ -f "${TEST_DIR}/qemu-vxhs.pid" ]; then + local QEMU_VXHS_PID + read QEMU_VXHS_PID < "${TEST_DIR}/qemu-vxhs.pid" + kill ${QEMU_VXHS_PID} >/dev/null 2>&1 + rm -f "${TEST_DIR}/qemu-vxhs.pid" + fi + rm -f "$TEST_IMG_FILE" + ;; + file) _rm_test_img "$TEST_DIR/t.$IMGFMT" _rm_test_img "$TEST_DIR/t.$IMGFMT.orig" From a98f49f46abb2034277633405479a4ab790c381e Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Tue, 14 Feb 2017 09:51:42 -0500 Subject: [PATCH 03/12] qemu-iotests: exclude vxhs from image creation via protocol The protocol VXHS does not support image creation. Some tests expect to be able to create images through the protocol. Exclude VXHS from these tests. Signed-off-by: Jeff Cody --- tests/qemu-iotests/017 | 1 + tests/qemu-iotests/020 | 1 + tests/qemu-iotests/029 | 1 + tests/qemu-iotests/073 | 1 + tests/qemu-iotests/114 | 1 + tests/qemu-iotests/130 | 1 + tests/qemu-iotests/134 | 1 + tests/qemu-iotests/156 | 1 + tests/qemu-iotests/158 | 1 + 9 files changed, 9 insertions(+) diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017 index e3f9e75967..4f9302db42 100755 --- a/tests/qemu-iotests/017 +++ b/tests/qemu-iotests/017 @@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed _supported_proto generic +_unsupported_proto vxhs _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index 9c4a68c977..7a111100ec 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed _supported_proto generic +_unsupported_proto vxhs _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index e639ac0ddf..30bab24dc0 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -42,6 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting intenal snapshots _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux # Internal snapshots are (currently) impossible with refcount_bits=1 _unsupported_imgopts 'refcount_bits=1[^0-9]' diff --git a/tests/qemu-iotests/073 b/tests/qemu-iotests/073 index ad37a617b2..40f85b18b9 100755 --- a/tests/qemu-iotests/073 +++ b/tests/qemu-iotests/073 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux CLUSTER_SIZE=64k diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 index f110d4f65a..5b7dc5496c 100755 --- a/tests/qemu-iotests/114 +++ b/tests/qemu-iotests/114 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux diff --git a/tests/qemu-iotests/130 b/tests/qemu-iotests/130 index ecc8a5ba1b..f941fc94a3 100755 --- a/tests/qemu-iotests/130 +++ b/tests/qemu-iotests/130 @@ -42,6 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux qemu_comm_method="monitor" diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134 index af618b8817..acce946e75 100755 --- a/tests/qemu-iotests/134 +++ b/tests/qemu-iotests/134 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index cc95ff1f98..78deaffcc3 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -48,6 +48,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 qed _supported_proto generic +_unsupported_proto vxhs _supported_os Linux # Create source disk diff --git a/tests/qemu-iotests/158 b/tests/qemu-iotests/158 index a6cdd6d8cf..ef8d70f109 100755 --- a/tests/qemu-iotests/158 +++ b/tests/qemu-iotests/158 @@ -39,6 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic +_unsupported_proto vxhs _supported_os Linux From fe5241bfe3fb61ec3f589ceacd91c1469bfd400f Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:25 -0400 Subject: [PATCH 04/12] block: add bdrv_set_read_only() helper function We have a helper wrapper for checking for the BDS read_only flag, add a helper wrapper to set the read_only flag as well. Reviewed-by: Stefan Hajnoczi Signed-off-by: Jeff Cody Reviewed-by: John Snow Message-id: 9b18972d05f5fa2ac16c014f0af98d680553048d.1491597120.git.jcody@redhat.com --- block.c | 5 +++++ block/bochs.c | 2 +- block/cloop.c | 2 +- block/dmg.c | 2 +- block/rbd.c | 2 +- block/vvfat.c | 4 ++-- include/block/block.h | 1 + 7 files changed, 12 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index 7eda9a42b3..6f21145689 100644 --- a/block.c +++ b/block.c @@ -192,6 +192,11 @@ void path_combine(char *dest, int dest_size, } } +void bdrv_set_read_only(BlockDriverState *bs, bool read_only) +{ + bs->read_only = read_only; +} + void bdrv_get_full_backing_filename_from_filename(const char *backed, const char *backing, char *dest, size_t sz, diff --git a/block/bochs.c b/block/bochs.c index 516da56c3b..bdc28316e7 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -110,7 +110,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bs->read_only = true; /* no write support yet */ + bdrv_set_read_only(bs, true); /* no write support yet */ ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); if (ret < 0) { diff --git a/block/cloop.c b/block/cloop.c index a6c7b9dbe6..11f17c8489 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -72,7 +72,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bs->read_only = true; + bdrv_set_read_only(bs, true); /* read header */ ret = bdrv_pread(bs->file, 128, &s->block_size, 4); diff --git a/block/dmg.c b/block/dmg.c index a7d25fc47b..27ce4a62d1 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -420,7 +420,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, } block_module_load_one("dmg-bz2"); - bs->read_only = true; + bdrv_set_read_only(bs, true); s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; diff --git a/block/rbd.c b/block/rbd.c index 1ceeeb5a60..6ad2904d78 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -641,7 +641,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_open; } - bs->read_only = (s->snap != NULL); + bdrv_set_read_only(bs, (s->snap != NULL)); qemu_opts_del(opts); return 0; diff --git a/block/vvfat.c b/block/vvfat.c index af5153d27d..d4ce6d7092 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1157,7 +1157,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->current_cluster=0xffffffff; /* read only is the default for safety */ - bs->read_only = true; + bdrv_set_read_only(bs, true); s->qcow = NULL; s->qcow_filename = NULL; s->fat2 = NULL; @@ -1173,7 +1173,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, if (ret < 0) { goto fail; } - bs->read_only = false; + bdrv_set_read_only(bs, false); } bs->total_sectors = cyls * heads * secs; diff --git a/include/block/block.h b/include/block/block.h index 466de49b48..99d49f2d66 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -434,6 +434,7 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); bool bdrv_is_read_only(BlockDriverState *bs); +void bdrv_set_read_only(BlockDriverState *bs, bool read_only); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); int bdrv_media_changed(BlockDriverState *bs); From e2b8247a322cd92945785edf25f09e6b3e8285f9 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:26 -0400 Subject: [PATCH 05/12] block: do not set BDS read_only if copy_on_read enabled A few block drivers will set the BDS read_only flag from their .bdrv_open() function. This means the bs->read_only flag could be set after we enable copy_on_read, as the BDRV_O_COPY_ON_READ flag check occurs prior to the call to bdrv->bdrv_open(). This adds an error return to bdrv_set_read_only(), and an error will be return if we try to set the BDS to read_only while copy_on_read is enabled. This patch also changes the behavior of vvfat. Before, vvfat could override the drive 'readonly' flag with its own, internal 'rw' flag. For instance, this -drive parameter would result in a writable image: "-drive format=vvfat,dir=/tmp/vvfat,rw,if=virtio,readonly=on" This is not correct. Now, attempting to use the above -drive parameter will result in an error (i.e., 'rw' is incompatible with 'readonly=on'). Signed-off-by: Jeff Cody Reviewed-by: Stefan Hajnoczi Reviewed-by: John Snow Message-id: 0c5b4c1cc2c651471b131f21376dfd5ea24d2196.1491597120.git.jcody@redhat.com --- block.c | 10 +++++++++- block/bochs.c | 5 ++++- block/cloop.c | 5 ++++- block/dmg.c | 6 +++++- block/rbd.c | 11 ++++++++++- block/vvfat.c | 21 ++++++++++++++++----- include/block/block.h | 2 +- 7 files changed, 49 insertions(+), 11 deletions(-) diff --git a/block.c b/block.c index 6f21145689..67ae35a47a 100644 --- a/block.c +++ b/block.c @@ -192,9 +192,17 @@ void path_combine(char *dest, int dest_size, } } -void bdrv_set_read_only(BlockDriverState *bs, bool read_only) +int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) { + /* Do not set read_only if copy_on_read is enabled */ + if (bs->copy_on_read && read_only) { + error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled", + bdrv_get_device_or_node_name(bs)); + return -EINVAL; + } + bs->read_only = read_only; + return 0; } void bdrv_get_full_backing_filename_from_filename(const char *backed, diff --git a/block/bochs.c b/block/bochs.c index bdc28316e7..a759b6eff0 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -110,7 +110,10 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bdrv_set_read_only(bs, true); /* no write support yet */ + ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ + if (ret < 0) { + return ret; + } ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); if (ret < 0) { diff --git a/block/cloop.c b/block/cloop.c index 11f17c8489..d6597fcf78 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -72,7 +72,10 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - bdrv_set_read_only(bs, true); + ret = bdrv_set_read_only(bs, true, errp); + if (ret < 0) { + return ret; + } /* read header */ ret = bdrv_pread(bs->file, 128, &s->block_size, 4); diff --git a/block/dmg.c b/block/dmg.c index 27ce4a62d1..900ae5a678 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -419,8 +419,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } + ret = bdrv_set_read_only(bs, true, errp); + if (ret < 0) { + return ret; + } + block_module_load_one("dmg-bz2"); - bdrv_set_read_only(bs, true); s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; diff --git a/block/rbd.c b/block/rbd.c index 6ad2904d78..1c43171df1 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -635,13 +635,22 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_shutdown; } + /* rbd_open is always r/w */ r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); if (r < 0) { error_setg_errno(errp, -r, "error reading header from %s", s->name); goto failed_open; } - bdrv_set_read_only(bs, (s->snap != NULL)); + /* If we are using an rbd snapshot, we must be r/o, otherwise + * leave as-is */ + if (s->snap != NULL) { + r = bdrv_set_read_only(bs, true, &local_err); + if (r < 0) { + error_propagate(errp, local_err); + goto failed_open; + } + } qemu_opts_del(opts); return 0; diff --git a/block/vvfat.c b/block/vvfat.c index d4ce6d7092..b509d55642 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1156,8 +1156,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->current_cluster=0xffffffff; - /* read only is the default for safety */ - bdrv_set_read_only(bs, true); s->qcow = NULL; s->qcow_filename = NULL; s->fat2 = NULL; @@ -1169,11 +1167,24 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1); if (qemu_opt_get_bool(opts, "rw", false)) { - ret = enable_write_target(bs, errp); - if (ret < 0) { + if (!bdrv_is_read_only(bs)) { + ret = enable_write_target(bs, errp); + if (ret < 0) { + goto fail; + } + } else { + ret = -EPERM; + error_setg(errp, + "Unable to set VVFAT to 'rw' when drive is read-only"); + goto fail; + } + } else { + /* read only is the default for safety */ + ret = bdrv_set_read_only(bs, true, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); goto fail; } - bdrv_set_read_only(bs, false); } bs->total_sectors = cyls * heads * secs; diff --git a/include/block/block.h b/include/block/block.h index 99d49f2d66..1d7fd19b86 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -434,7 +434,7 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); bool bdrv_is_read_only(BlockDriverState *bs); -void bdrv_set_read_only(BlockDriverState *bs, bool read_only); +int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); int bdrv_media_changed(BlockDriverState *bs); From d6fcdf06d9fc7d72151cc2069dc5c1adf5c21707 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:27 -0400 Subject: [PATCH 06/12] block: honor BDRV_O_ALLOW_RDWR when clearing bs->read_only The BDRV_O_ALLOW_RDWR flag allows / prohibits the changing of the BDS 'read_only' state, but there are a few places where it is ignored. In the bdrv_set_read_only() helper, make sure to honor the flag. Signed-off-by: Jeff Cody Reviewed-by: Stefan Hajnoczi Reviewed-by: John Snow Message-id: be2e5fb2d285cbece2b6d06bed54a6f56520d251.1491597120.git.jcody@redhat.com --- block.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/block.c b/block.c index 67ae35a47a..4752feee4e 100644 --- a/block.c +++ b/block.c @@ -201,6 +201,13 @@ int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) return -EINVAL; } + /* Do not clear read_only if it is prohibited */ + if (!read_only && !(bs->open_flags & BDRV_O_ALLOW_RDWR)) { + error_setg(errp, "Node '%s' is read only", + bdrv_get_device_or_node_name(bs)); + return -EPERM; + } + bs->read_only = read_only; return 0; } From 93ed524e3de732f70b3b042e3c56bc4ed719498d Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:28 -0400 Subject: [PATCH 07/12] block: code movement Move bdrv_is_read_only() up with its friends. Reviewed-by: Stefan Hajnoczi Reviewed-by: John Snow Signed-off-by: Jeff Cody Message-id: 73b2399459760c32506f9407efb9dddb3a2789de.1491597120.git.jcody@redhat.com --- block.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/block.c b/block.c index 4752feee4e..123c98279b 100644 --- a/block.c +++ b/block.c @@ -192,6 +192,11 @@ void path_combine(char *dest, int dest_size, } } +bool bdrv_is_read_only(BlockDriverState *bs) +{ + return bs->read_only; +} + int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) { /* Do not set read_only if copy_on_read is enabled */ @@ -3375,11 +3380,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; } -bool bdrv_is_read_only(BlockDriverState *bs) -{ - return bs->read_only; -} - bool bdrv_is_sg(BlockDriverState *bs) { return bs->sg; From 45803a039670cbee914dcb54cf3952f9b2d64d1f Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:29 -0400 Subject: [PATCH 08/12] block: introduce bdrv_can_set_read_only() Introduce check function for setting read_only flags. Will return < 0 on error, with appropriate Error value set. Does not alter any flags. Signed-off-by: Jeff Cody Reviewed-by: Stefan Hajnoczi Reviewed-by: John Snow Message-id: e2bba34ac3bc76a0c42adc390413f358ae0566e8.1491597120.git.jcody@redhat.com --- block.c | 14 +++++++++++++- include/block/block.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index 123c98279b..1ac05c1394 100644 --- a/block.c +++ b/block.c @@ -197,7 +197,7 @@ bool bdrv_is_read_only(BlockDriverState *bs) return bs->read_only; } -int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) +int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) { /* Do not set read_only if copy_on_read is enabled */ if (bs->copy_on_read && read_only) { @@ -213,6 +213,18 @@ int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) return -EPERM; } + return 0; +} + +int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) +{ + int ret = 0; + + ret = bdrv_can_set_read_only(bs, read_only, errp); + if (ret < 0) { + return ret; + } + bs->read_only = read_only; return 0; } diff --git a/include/block/block.h b/include/block/block.h index 1d7fd19b86..144df0ddfb 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -434,6 +434,7 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); bool bdrv_is_read_only(BlockDriverState *bs); +int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); From 3d8ce171cb4a62b55727bc949e2e9427f77de6fb Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:30 -0400 Subject: [PATCH 09/12] block: use bdrv_can_set_read_only() during reopen Signed-off-by: Jeff Cody Reviewed-by: Stefan Hajnoczi Reviewed-by: John Snow Message-id: 00aed7ffdd7be4b9ed9ce1007d50028a72b34ebe.1491597120.git.jcody@redhat.com --- block.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index 1ac05c1394..5db266be21 100644 --- a/block.c +++ b/block.c @@ -2789,6 +2789,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, BlockDriver *drv; QemuOpts *opts; const char *value; + bool read_only; assert(reopen_state != NULL); assert(reopen_state->bs->drv != NULL); @@ -2817,12 +2818,13 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, qdict_put(reopen_state->options, "driver", qstring_from_str(value)); } - /* if we are to stay read-only, do not allow permission change - * to r/w */ - if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) && - reopen_state->flags & BDRV_O_RDWR) { - error_setg(errp, "Node '%s' is read only", - bdrv_get_device_or_node_name(reopen_state->bs)); + /* If we are to stay read-only, do not allow permission change + * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is + * not set, or if the BDS still has copy_on_read enabled */ + read_only = !(reopen_state->flags & BDRV_O_RDWR); + ret = bdrv_can_set_read_only(reopen_state->bs, read_only, &local_err); + if (local_err) { + error_propagate(errp, local_err); goto error; } From 80b61a27c69c67492ce87ddd63ee497b5bc00e7e Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:31 -0400 Subject: [PATCH 10/12] block/rbd - update variable names to more apt names Update 'clientname' to be 'user', which tracks better with both the QAPI and rados variable naming. Update 'name' to be 'image_name', as it indicates the rbd image. Naming it 'image' would have been ideal, but we are using that for the rados_image_t value returned by rbd_open(). Reviewed-by: Stefan Hajnoczi Signed-off-by: Jeff Cody Reviewed-by: John Snow Message-id: b7ec1fb2e1cf36f9b6911631447a5b0422590b7d.1491597120.git.jcody@redhat.com --- block/rbd.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 1c43171df1..35853c9ba4 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -94,7 +94,7 @@ typedef struct BDRVRBDState { rados_t cluster; rados_ioctx_t io_ctx; rbd_image_t image; - char *name; + char *image_name; char *snap; } BDRVRBDState; @@ -350,7 +350,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) int64_t bytes = 0; int64_t objsize; int obj_order = 0; - const char *pool, *name, *conf, *clientname, *keypairs; + const char *pool, *image_name, *conf, *user, *keypairs; const char *secretid; rados_t cluster; rados_ioctx_t io_ctx; @@ -393,11 +393,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) */ pool = qdict_get_try_str(options, "pool"); conf = qdict_get_try_str(options, "conf"); - clientname = qdict_get_try_str(options, "user"); - name = qdict_get_try_str(options, "image"); + user = qdict_get_try_str(options, "user"); + image_name = qdict_get_try_str(options, "image"); keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); - ret = rados_create(&cluster, clientname); + ret = rados_create(&cluster, user); if (ret < 0) { error_setg_errno(errp, -ret, "error initializing"); goto exit; @@ -434,7 +434,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) goto shutdown; } - ret = rbd_create(io_ctx, name, bytes, &obj_order); + ret = rbd_create(io_ctx, image_name, bytes, &obj_order); if (ret < 0) { error_setg_errno(errp, -ret, "error rbd create"); } @@ -540,7 +540,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVRBDState *s = bs->opaque; - const char *pool, *snap, *conf, *clientname, *name, *keypairs; + const char *pool, *snap, *conf, *user, *image_name, *keypairs; const char *secretid; QemuOpts *opts; Error *local_err = NULL; @@ -567,24 +567,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, pool = qemu_opt_get(opts, "pool"); conf = qemu_opt_get(opts, "conf"); snap = qemu_opt_get(opts, "snapshot"); - clientname = qemu_opt_get(opts, "user"); - name = qemu_opt_get(opts, "image"); + user = qemu_opt_get(opts, "user"); + image_name = qemu_opt_get(opts, "image"); keypairs = qemu_opt_get(opts, "=keyvalue-pairs"); - if (!pool || !name) { + if (!pool || !image_name) { error_setg(errp, "Parameters 'pool' and 'image' are required"); r = -EINVAL; goto failed_opts; } - r = rados_create(&s->cluster, clientname); + r = rados_create(&s->cluster, user); if (r < 0) { error_setg_errno(errp, -r, "error initializing"); goto failed_opts; } s->snap = g_strdup(snap); - s->name = g_strdup(name); + s->image_name = g_strdup(image_name); /* try default location when conf=NULL, but ignore failure */ r = rados_conf_read_file(s->cluster, conf); @@ -636,9 +636,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } /* rbd_open is always r/w */ - r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); + r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap); if (r < 0) { - error_setg_errno(errp, -r, "error reading header from %s", s->name); + error_setg_errno(errp, -r, "error reading header from %s", + s->image_name); goto failed_open; } @@ -660,7 +661,7 @@ failed_open: failed_shutdown: rados_shutdown(s->cluster); g_free(s->snap); - g_free(s->name); + g_free(s->image_name); failed_opts: qemu_opts_del(opts); g_free(mon_host); @@ -674,7 +675,7 @@ static void qemu_rbd_close(BlockDriverState *bs) rbd_close(s->image); rados_ioctx_destroy(s->io_ctx); g_free(s->snap); - g_free(s->name); + g_free(s->image_name); rados_shutdown(s->cluster); } From 56e7cf8df03ae142c7797435dc0ece80a42f4fd0 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Fri, 7 Apr 2017 16:55:32 -0400 Subject: [PATCH 11/12] block/rbd: Add support for reopen() This adds support for reopen in rbd, for changing between r/w and r/o. Note, that this is only a flag change, but we will block a change from r/o to r/w if we are using an RBD internal snapshot. Reviewed-by: Stefan Hajnoczi Signed-off-by: Jeff Cody Reviewed-by: John Snow Message-id: d4e87539167ec6527d44c97b164eabcccf96e4f3.1491597120.git.jcody@redhat.com --- block/rbd.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/block/rbd.c b/block/rbd.c index 35853c9ba4..6471f4fd2b 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -668,6 +668,26 @@ failed_opts: return r; } + +/* Since RBD is currently always opened R/W via the API, + * we just need to check if we are using a snapshot or not, in + * order to determine if we will allow it to be R/W */ +static int qemu_rbd_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + BDRVRBDState *s = state->bs->opaque; + int ret = 0; + + if (s->snap && state->flags & BDRV_O_RDWR) { + error_setg(errp, + "Cannot change node '%s' to r/w when using RBD snapshot", + bdrv_get_device_or_node_name(state->bs)); + ret = -EINVAL; + } + + return ret; +} + static void qemu_rbd_close(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; @@ -1074,6 +1094,7 @@ static BlockDriver bdrv_rbd = { .bdrv_parse_filename = qemu_rbd_parse_filename, .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, + .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, .bdrv_create = qemu_rbd_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_get_info = qemu_rbd_getinfo, From ecfa185400ade2abc9915efa924cbad1e15a21a4 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Tue, 18 Apr 2017 15:42:41 -0400 Subject: [PATCH 12/12] qemu-iotests: _cleanup_qemu must be called on exit For the tests that use the common.qemu functions for running a QEMU process, _cleanup_qemu must be called in the exit function. If it is not, if the qemu process aborts, then not all of the droppings are cleaned up (e.g. pidfile, fifos). This updates those tests that did not have a cleanup in qemu-iotests. (I swapped spaces for tabs in test 102 as well) Reported-by: Eric Blake Reviewed-by: Eric Blake Signed-off-by: Jeff Cody Message-id: d59c2f6ad6c1da8b9b3c7f357c94a7122ccfc55a.1492544096.git.jcody@redhat.com --- tests/qemu-iotests/028 | 1 + tests/qemu-iotests/094 | 11 ++++++++--- tests/qemu-iotests/102 | 5 +++-- tests/qemu-iotests/109 | 1 + tests/qemu-iotests/117 | 1 + tests/qemu-iotests/130 | 1 + tests/qemu-iotests/140 | 1 + tests/qemu-iotests/141 | 1 + tests/qemu-iotests/143 | 1 + tests/qemu-iotests/156 | 1 + 10 files changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 7783e57c71..97a8869251 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -32,6 +32,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f "${TEST_IMG}.copy" _cleanup_test_img } diff --git a/tests/qemu-iotests/094 b/tests/qemu-iotests/094 index 0ba0b0c361..9aa01e3627 100755 --- a/tests/qemu-iotests/094 +++ b/tests/qemu-iotests/094 @@ -27,7 +27,14 @@ echo "QA output created by $seq" here="$PWD" status=1 # failure is the default! -trap "exit \$status" 0 1 2 3 15 +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$TEST_DIR/source.$IMGFMT" +} + +trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks . ./common.rc @@ -73,8 +80,6 @@ _send_qemu_cmd $QEMU_HANDLE \ wait=1 _cleanup_qemu -_cleanup_test_img -rm -f "$TEST_DIR/source.$IMGFMT" # success, all done echo '*** done' diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 index 64b4af9441..87db1bb1bf 100755 --- a/tests/qemu-iotests/102 +++ b/tests/qemu-iotests/102 @@ -25,11 +25,12 @@ seq=$(basename $0) echo "QA output created by $seq" here=$PWD -status=1 # failure is the default! +status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_qemu + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index 927151a285..6161633a52 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f $TEST_IMG.src _cleanup_test_img } diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117 index e955d52de3..6c83461182 100755 --- a/tests/qemu-iotests/117 +++ b/tests/qemu-iotests/117 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/130 b/tests/qemu-iotests/130 index f941fc94a3..e7e43de6d6 100755 --- a/tests/qemu-iotests/130 +++ b/tests/qemu-iotests/130 @@ -31,6 +31,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/140 b/tests/qemu-iotests/140 index 49f9df4eb0..8c80a5a866 100755 --- a/tests/qemu-iotests/140 +++ b/tests/qemu-iotests/140 @@ -33,6 +33,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img rm -f "$TEST_DIR/nbd" } diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 27fb1cc92c..40a3405968 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu _cleanup_test_img rm -f "$TEST_DIR/{b,m,o}.$IMGFMT" } diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143 index ec4ef2221a..5ff1944507 100755 --- a/tests/qemu-iotests/143 +++ b/tests/qemu-iotests/143 @@ -29,6 +29,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f "$TEST_DIR/nbd" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index 78deaffcc3..d799b73e1e 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -37,6 +37,7 @@ status=1 # failure is the default! _cleanup() { + _cleanup_qemu rm -f "$TEST_IMG{,.target}{,.backing,.overlay}" } trap "_cleanup; exit \$status" 0 1 2 3 15