From f51062061e6adf64a879177f35ff5c6babb63e7e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 30 Jan 2014 12:52:47 +0100 Subject: [PATCH 01/18] qemu-iotest: Make 077 raw-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qemu-io command sequences make the assumption that an unaligned request on the format layer will be unaligned on the blkdebug layer as well. This doesn't necessarily hold true for drivers other than raw. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi Reviewed-by: BenoĆ®t Canet --- tests/qemu-iotests/077 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077 index bbf7b5145a..4dd1bdde20 100755 --- a/tests/qemu-iotests/077 +++ b/tests/qemu-iotests/077 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw _supported_proto generic _supported_os Linux From 6542aa9c75bcef5a549b8ac1ce34d0ec6782a3c2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 3 Feb 2014 10:26:13 +0100 Subject: [PATCH 02/18] block: add native support for NFS This patch adds native support for accessing images on NFS shares without the requirement to actually mount the entire NFS share on the host. NFS Images can simply be specified by an url of the form: nfs:////[?param=value[¶m2=value2[&...]]] For example: qemu-img create -f qcow2 nfs://10.0.0.1/qemu-images/test.qcow2 You need LibNFS from Ronnie Sahlberg available at: git://github.com/sahlberg/libnfs.git for this to work. During configure it is automatically probed for libnfs and support is enabled on-the-fly. You can forbid or enforce libnfs support with --disable-libnfs or --enable-libnfs respectively. Due to NFS restrictions you might need to execute your binaries as root, allow them to open priviledged ports (<1024) or specify insecure option on the NFS server. For additional information on ROOT vs. non-ROOT operation and URL format + parameters see: https://raw.github.com/sahlberg/libnfs/master/README Supported by qemu are the uid, gid and tcp-syncnt URL parameters. LibNFS currently support NFS version 3 only. Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- MAINTAINERS | 5 + block/Makefile.objs | 1 + block/nfs.c | 439 ++++++++++++++++++++++++++++++++++++++++++++ configure | 26 +++ qapi-schema.json | 1 + 5 files changed, 472 insertions(+) create mode 100644 block/nfs.c diff --git a/MAINTAINERS b/MAINTAINERS index adc59735a9..026ea4f021 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -937,6 +937,11 @@ M: Peter Lieven S: Supported F: block/iscsi.c +NFS +M: Peter Lieven +S: Maintained +F: block/nfs.c + SSH M: Richard W.M. Jones S: Supported diff --git a/block/Makefile.objs b/block/Makefile.objs index 4e8c91ec34..e254a2180a 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -12,6 +12,7 @@ block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o ifeq ($(CONFIG_POSIX),y) block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o +block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o diff --git a/block/nfs.c b/block/nfs.c new file mode 100644 index 0000000000..ef731f04e3 --- /dev/null +++ b/block/nfs.c @@ -0,0 +1,439 @@ +/* + * QEMU Block driver for native access to files on NFS shares + * + * Copyright (c) 2014 Peter Lieven + * + * 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" + +#include +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "block/block_int.h" +#include "trace.h" +#include "qemu/iov.h" +#include "qemu/uri.h" +#include "sysemu/sysemu.h" +#include + +typedef struct NFSClient { + struct nfs_context *context; + struct nfsfh *fh; + int events; + bool has_zero_init; +} NFSClient; + +typedef struct NFSRPC { + int ret; + int complete; + QEMUIOVector *iov; + struct stat *st; + Coroutine *co; + QEMUBH *bh; +} NFSRPC; + +static void nfs_process_read(void *arg); +static void nfs_process_write(void *arg); + +static void nfs_set_events(NFSClient *client) +{ + int ev = nfs_which_events(client->context); + if (ev != client->events) { + qemu_aio_set_fd_handler(nfs_get_fd(client->context), + (ev & POLLIN) ? nfs_process_read : NULL, + (ev & POLLOUT) ? nfs_process_write : NULL, + client); + + } + client->events = ev; +} + +static void nfs_process_read(void *arg) +{ + NFSClient *client = arg; + nfs_service(client->context, POLLIN); + nfs_set_events(client); +} + +static void nfs_process_write(void *arg) +{ + NFSClient *client = arg; + nfs_service(client->context, POLLOUT); + nfs_set_events(client); +} + +static void nfs_co_init_task(NFSClient *client, NFSRPC *task) +{ + *task = (NFSRPC) { + .co = qemu_coroutine_self(), + }; +} + +static void nfs_co_generic_bh_cb(void *opaque) +{ + NFSRPC *task = opaque; + qemu_bh_delete(task->bh); + qemu_coroutine_enter(task->co, NULL); +} + +static void +nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, + void *private_data) +{ + NFSRPC *task = private_data; + task->complete = 1; + task->ret = ret; + if (task->ret > 0 && task->iov) { + if (task->ret <= task->iov->size) { + qemu_iovec_from_buf(task->iov, 0, data, task->ret); + } else { + task->ret = -EIO; + } + } + if (task->ret == 0 && task->st) { + memcpy(task->st, data, sizeof(struct stat)); + } + if (task->co) { + task->bh = qemu_bh_new(nfs_co_generic_bh_cb, task); + qemu_bh_schedule(task->bh); + } +} + +static int coroutine_fn nfs_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + + nfs_co_init_task(client, &task); + task.iov = iov; + + if (nfs_pread_async(client->context, client->fh, + sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, + nfs_co_generic_cb, &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + if (task.ret < 0) { + return task.ret; + } + + /* zero pad short reads */ + if (task.ret < iov->size) { + qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); + } + + return 0; +} + +static int coroutine_fn nfs_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + char *buf = NULL; + + nfs_co_init_task(client, &task); + + buf = g_malloc(nb_sectors * BDRV_SECTOR_SIZE); + qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); + + if (nfs_pwrite_async(client->context, client->fh, + sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, + buf, nfs_co_generic_cb, &task) != 0) { + g_free(buf); + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + g_free(buf); + + if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { + return task.ret < 0 ? task.ret : -EIO; + } + + return 0; +} + +static int coroutine_fn nfs_co_flush(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + + nfs_co_init_task(client, &task); + + if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, + &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + return task.ret; +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "nfs", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "URL to the NFS file", + }, + { /* end of list */ } + }, +}; + +static void nfs_client_close(NFSClient *client) +{ + if (client->context) { + if (client->fh) { + nfs_close(client->context, client->fh); + } + qemu_aio_set_fd_handler(nfs_get_fd(client->context), NULL, NULL, NULL); + nfs_destroy_context(client->context); + } + memset(client, 0, sizeof(NFSClient)); +} + +static void nfs_file_close(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + nfs_client_close(client); +} + +static int64_t nfs_client_open(NFSClient *client, const char *filename, + int flags, Error **errp) +{ + int ret = -EINVAL, i; + struct stat st; + URI *uri; + QueryParams *qp = NULL; + char *file = NULL, *strp = NULL; + + uri = uri_parse(filename); + if (!uri) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + strp = strrchr(uri->path, '/'); + if (strp == NULL) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + file = g_strdup(strp); + *strp = 0; + + client->context = nfs_init_context(); + if (client->context == NULL) { + error_setg(errp, "Failed to init NFS context"); + goto fail; + } + + qp = query_params_parse(uri->query); + for (i = 0; i < qp->n; i++) { + if (!qp->p[i].value) { + error_setg(errp, "Value for NFS parameter expected: %s", + qp->p[i].name); + goto fail; + } + if (!strncmp(qp->p[i].name, "uid", 3)) { + nfs_set_uid(client->context, atoi(qp->p[i].value)); + } else if (!strncmp(qp->p[i].name, "gid", 3)) { + nfs_set_gid(client->context, atoi(qp->p[i].value)); + } else if (!strncmp(qp->p[i].name, "tcp-syncnt", 10)) { + nfs_set_tcp_syncnt(client->context, atoi(qp->p[i].value)); + } else { + error_setg(errp, "Unknown NFS parameter name: %s", + qp->p[i].name); + goto fail; + } + } + + ret = nfs_mount(client->context, uri->server, uri->path); + if (ret < 0) { + error_setg(errp, "Failed to mount nfs share: %s", + nfs_get_error(client->context)); + goto fail; + } + + if (flags & O_CREAT) { + ret = nfs_creat(client->context, file, 0600, &client->fh); + if (ret < 0) { + error_setg(errp, "Failed to create file: %s", + nfs_get_error(client->context)); + goto fail; + } + } else { + ret = nfs_open(client->context, file, flags, &client->fh); + if (ret < 0) { + error_setg(errp, "Failed to open file : %s", + nfs_get_error(client->context)); + goto fail; + } + } + + ret = nfs_fstat(client->context, client->fh, &st); + if (ret < 0) { + error_setg(errp, "Failed to fstat file: %s", + nfs_get_error(client->context)); + goto fail; + } + + ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); + client->has_zero_init = S_ISREG(st.st_mode); + goto out; +fail: + nfs_client_close(client); +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + g_free(file); + return ret; +} + +static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { + NFSClient *client = bs->opaque; + int64_t ret; + QemuOpts *opts; + Error *local_err = NULL; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return -EINVAL; + } + ret = nfs_client_open(client, qemu_opt_get(opts, "filename"), + (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, + errp); + if (ret < 0) { + return ret; + } + bs->total_sectors = ret; + return 0; +} + +static int nfs_file_create(const char *url, QEMUOptionParameter *options, + Error **errp) +{ + int ret = 0; + int64_t total_size = 0; + NFSClient *client = g_malloc0(sizeof(NFSClient)); + + /* Read out options */ + while (options && options->name) { + if (!strcmp(options->name, "size")) { + total_size = options->value.n; + } + options++; + } + + ret = nfs_client_open(client, url, O_CREAT, errp); + if (ret < 0) { + goto out; + } + ret = nfs_ftruncate(client->context, client->fh, total_size); + nfs_client_close(client); +out: + g_free(client); + return ret; +} + +static int nfs_has_zero_init(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + return client->has_zero_init; +} + +static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + NFSRPC task = {0}; + struct stat st; + + task.st = &st; + if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, + &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_aio_wait(); + } + + return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize); +} + +static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) +{ + NFSClient *client = bs->opaque; + return nfs_ftruncate(client->context, client->fh, offset); +} + +static BlockDriver bdrv_nfs = { + .format_name = "nfs", + .protocol_name = "nfs", + + .instance_size = sizeof(NFSClient), + .bdrv_needs_filename = true, + .bdrv_has_zero_init = nfs_has_zero_init, + .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, + .bdrv_truncate = nfs_file_truncate, + + .bdrv_file_open = nfs_file_open, + .bdrv_close = nfs_file_close, + .bdrv_create = nfs_file_create, + + .bdrv_co_readv = nfs_co_readv, + .bdrv_co_writev = nfs_co_writev, + .bdrv_co_flush_to_disk = nfs_co_flush, +}; + +static void nfs_block_init(void) +{ + bdrv_register(&bdrv_nfs); +} + +block_init(nfs_block_init); diff --git a/configure b/configure index 236764a3bd..5b20ce6139 100755 --- a/configure +++ b/configure @@ -251,6 +251,7 @@ vss_win32_sdk="" win_sdk="no" want_tools="yes" libiscsi="" +libnfs="" coroutine="" coroutine_pool="" seccomp="" @@ -840,6 +841,10 @@ for opt do ;; --enable-libiscsi) libiscsi="yes" ;; + --disable-libnfs) libnfs="no" + ;; + --enable-libnfs) libnfs="yes" + ;; --enable-profiler) profiler="yes" ;; --disable-cocoa) cocoa="no" @@ -1229,6 +1234,8 @@ Advanced options (experts only): --enable-rbd enable building the rados block device (rbd) --disable-libiscsi disable iscsi support --enable-libiscsi enable iscsi support + --disable-libnfs disable nfs support + --enable-libnfs enable nfs support --disable-smartcard-nss disable smartcard nss support --enable-smartcard-nss enable smartcard nss support --disable-libusb disable libusb (for usb passthrough) @@ -3600,6 +3607,20 @@ elif test "$debug" = "no" ; then CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" fi +########################################## +# Do we have libnfs +if test "$libnfs" != "no" ; then + if $pkg_config --atleast-version=1.9.2 libnfs; then + libnfs="yes" + libnfs_libs=$($pkg_config --libs libnfs) + LIBS="$LIBS $libnfs_libs" + else + if test "$libnfs" = "yes" ; then + feature_not_found "libnfs" + fi + libnfs="no" + fi +fi # Disable zero malloc errors for official releases unless explicitly told to # enable/disable @@ -3829,6 +3850,7 @@ echo "libiscsi support $libiscsi (1.4.0)" else echo "libiscsi support $libiscsi" fi +echo "libnfs support $libnfs" echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" echo "seccomp support $seccomp" @@ -4165,6 +4187,10 @@ if test "$libiscsi" = "yes" ; then fi fi +if test "$libnfs" = "yes" ; then + echo "CONFIG_LIBNFS=y" >> $config_host_mak +fi + if test "$seccomp" = "yes"; then echo "CONFIG_SECCOMP=y" >> $config_host_mak fi diff --git a/qapi-schema.json b/qapi-schema.json index 05ced9d572..7cfb5e5d1e 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4371,6 +4371,7 @@ # TODO gluster: Wait for structured options # TODO iscsi: Wait for structured options # TODO nbd: Should take InetSocketAddress for 'host'? +# TODO nfs: Wait for structured options # TODO rbd: Wait for structured options # TODO sheepdog: Wait for structured options # TODO ssh: Should take InetSocketAddress for 'host'? From 1f7bf7d0687f5a58613358f5e7cfd8f29a3a21e2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 3 Feb 2014 10:26:14 +0100 Subject: [PATCH 03/18] qemu-iotests: change _supported_proto to file for various tests all these tests do anything of the following and thus fail with any protocol other than file: - the tests use rm, cp or mv shell commands which only work on file - the tests use qcow2.py - the images construct new filenames (e.g. backing file names) and the logic is broken for anything else than file Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- tests/qemu-iotests/013 | 2 +- tests/qemu-iotests/014 | 2 +- tests/qemu-iotests/018 | 2 +- tests/qemu-iotests/019 | 2 +- tests/qemu-iotests/023 | 2 +- tests/qemu-iotests/024 | 2 +- tests/qemu-iotests/026 | 2 +- tests/qemu-iotests/028 | 2 +- tests/qemu-iotests/031 | 2 +- tests/qemu-iotests/034 | 2 +- tests/qemu-iotests/036 | 2 +- tests/qemu-iotests/037 | 2 +- tests/qemu-iotests/038 | 2 +- tests/qemu-iotests/039 | 2 +- tests/qemu-iotests/043 | 2 +- tests/qemu-iotests/046 | 2 +- tests/qemu-iotests/052 | 2 +- tests/qemu-iotests/054 | 2 +- tests/qemu-iotests/059 | 2 +- tests/qemu-iotests/060 | 2 +- tests/qemu-iotests/061 | 2 +- tests/qemu-iotests/063 | 2 +- tests/qemu-iotests/069 | 2 +- tests/qemu-iotests/071 | 2 +- tests/qemu-iotests/072 | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/qemu-iotests/013 b/tests/qemu-iotests/013 index 389f4b8156..ea3cab91d6 100755 --- a/tests/qemu-iotests/013 +++ b/tests/qemu-iotests/013 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # much of this could be generic for any format supporting compression. _supported_fmt qcow qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux TEST_OFFSETS="0 4294967296" diff --git a/tests/qemu-iotests/014 b/tests/qemu-iotests/014 index 0edeb4b6f5..b23c2db9b6 100755 --- a/tests/qemu-iotests/014 +++ b/tests/qemu-iotests/014 @@ -43,7 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # much of this could be generic for any format supporting snapshots _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux TEST_OFFSETS="0 4294967296" diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018 index 6f7f0545d0..d8a7d435ab 100755 --- a/tests/qemu-iotests/018 +++ b/tests/qemu-iotests/018 @@ -41,7 +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 +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index b43e70f3cb..e67445c754 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -45,7 +45,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/023 b/tests/qemu-iotests/023 index 090ed23dec..9ad06b990e 100755 --- a/tests/qemu-iotests/023 +++ b/tests/qemu-iotests/023 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # much of this could be generic for any format supporting compression. _supported_fmt qcow qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux TEST_OFFSETS="0 4294967296" diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index be974f02a2..9bf99e198a 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -43,7 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Currently only qcow2 and qed support rebasing _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=65536 diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index c9c5f83936..df2884ba51 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Currently only qcow2 supports rebasing _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" "none" diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 93a9fa6e83..a99e4fa2bd 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -45,7 +45,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files except vmdk and qcow which do not support # smaller backing files. _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux # Choose a size that is not necessarily a cluster size multiple for image diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index c9070b0513..1d920ea87a 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qcow2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=65536 diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 index 7349789583..69c7858586 100755 --- a/tests/qemu-iotests/034 +++ b/tests/qemu-iotests/034 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index e049a645e7..03b6aa9de7 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qcow2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux # Only qcow2v3 and later supports feature bits diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037 index e444349e6d..9171d8c8a6 100755 --- a/tests/qemu-iotests/037 +++ b/tests/qemu-iotests/037 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/038 b/tests/qemu-iotests/038 index 7bb7906e7f..cfaf00a783 100755 --- a/tests/qemu-iotests/038 +++ b/tests/qemu-iotests/038 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=2M diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 6abf47267f..9b355c0977 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 index d7f12319b3..b316b97c0c 100755 --- a/tests/qemu-iotests/043 +++ b/tests/qemu-iotests/043 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 index 3f17ceb1b9..2d44bbb187 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=64k diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052 index 4d4e411339..6bdae92780 100755 --- a/tests/qemu-iotests/052 +++ b/tests/qemu-iotests/052 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt generic -_supported_proto generic +_supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054 index 5a0d1b16c2..c8b7082b4e 100755 --- a/tests/qemu-iotests/054 +++ b/tests/qemu-iotests/054 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux echo diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 2d604d3a91..ca5aa16ff7 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests vmdk-specific low-level functionality _supported_fmt vmdk -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index bbb19090a1..af8ed9f39a 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qocw2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux rt_offset=65536 # 0x10000 (XXX: just an assumption) diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index e42f9bd5e8..d3a6b388b5 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qocw2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux echo diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index 77503a2984..a47493a076 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.pattern _supported_fmt qcow qcow2 vmdk qed raw -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 index 50347d91d2..e661598c4a 100755 --- a/tests/qemu-iotests/069 +++ b/tests/qemu-iotests/069 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt cow qed qcow qcow2 vmdk -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index dbc07c6c4f..3924e51f51 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux function do_run_qemu() diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072 index a3876c2161..58faa8b5a7 100755 --- a/tests/qemu-iotests/072 +++ b/tests/qemu-iotests/072 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow cow -_supported_proto generic +_supported_proto file _supported_os Linux IMG_SIZE=64M From ecd792fd2178570b8bcf399a86af5a6d9504437b Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 3 Feb 2014 10:26:15 +0100 Subject: [PATCH 04/18] qemu-iotests: blacklist test 020 for NFS protocol reopening is currently not supported. Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- tests/qemu-iotests/020 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index 73a0429481..2f258dc6e9 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -49,6 +49,11 @@ _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ "subformat=twoGbMaxExtentSparse" +# NFS does not support bdrv_reopen_prepare thus qemu-img commit fails. +if [ "$IMGPROTO" = "nfs" ]; then + _notrun "image protocol $IMGPROTO does not support bdrv_commit" +fi + TEST_OFFSETS="0 4294967296" _make_test_img 6G From a19737f1bdcca1de3d0c1d7c87935d89d5ccc1e5 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 3 Feb 2014 10:26:16 +0100 Subject: [PATCH 05/18] qemu-iotests: enable test 016 and 025 to work with NFS protocol Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- tests/qemu-iotests/016 | 2 +- tests/qemu-iotests/025 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/016 b/tests/qemu-iotests/016 index b87a32bc27..7ea9e94b5d 100755 --- a/tests/qemu-iotests/016 +++ b/tests/qemu-iotests/016 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt raw -_supported_proto file sheepdog +_supported_proto file sheepdog nfs _supported_os Linux diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 index 9426c93bd0..a5f45b454c 100755 --- a/tests/qemu-iotests/025 +++ b/tests/qemu-iotests/025 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.pattern _supported_fmt raw qcow2 qed -_supported_proto file sheepdog rbd +_supported_proto file sheepdog rbd nfs _supported_os Linux echo "=== Creating image" From 170632dbc9f75217861dd8bf2e6da3c269a1ba18 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 3 Feb 2014 10:26:17 +0100 Subject: [PATCH 06/18] qemu-iotests: enable support for NFS protocol Signed-off-by: Peter Lieven Signed-off-by: Kevin Wolf --- tests/qemu-iotests/common | 22 +++++++++++++++++++--- tests/qemu-iotests/common.rc | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 8b4e22c856..5795358924 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -144,10 +144,12 @@ check options -vpc test vpc -vhdx test vhdx -vmdk test vmdk + -file test file (default) -rbd test rbd -sheepdog test sheepdog -nbd test nbd -ssh test ssh + -nfs test nfs -xdiff graphical mode diff -nocache use O_DIRECT on backing file -misalign misalign memory allocations @@ -211,22 +213,36 @@ testlist options xpand=false ;; + -file) + IMGPROTO=file + xpand=false + ;; + -rbd) IMGPROTO=rbd xpand=false ;; + -sheepdog) IMGPROTO=sheepdog xpand=false ;; + -nbd) IMGPROTO=nbd xpand=false ;; + -ssh) IMGPROTO=ssh xpand=false ;; + + -nfs) + IMGPROTO=nfs + xpand=false + ;; + -nocache) CACHEMODE="none" CACHEMODE_IS_DEFAULT=false @@ -238,10 +254,10 @@ testlist options xpand=false ;; - -valgrind) - valgrind=true + -valgrind) + valgrind=true xpand=false - ;; + ;; -g) # -g group ... pick from group file group=true diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 0f68156400..71e9a7462d 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -61,6 +61,9 @@ elif [ "$IMGPROTO" = "nbd" ]; then elif [ "$IMGPROTO" = "ssh" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" +elif [ "$IMGPROTO" = "nfs" ]; then + TEST_DIR="nfs://127.0.0.1/$TEST_DIR" + TEST_IMG=$TEST_DIR/t.$IMGFMT else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi From 765003db029ed4660a09807958276e251de84fac Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 3 Feb 2014 14:49:42 +0100 Subject: [PATCH 07/18] block: Fail gracefully with missing filename This fixes a regression introduced in commit 2a05cbe42 ('block: Allow block devices without files'): $ qemu-system-x86_64 -drive driver=file qemu-system-x86_64: block.c:892: bdrv_open_common: Assertion `!drv->bdrv_needs_filename || filename != ((void *)0)' failed. Now the respective check must be performed not only in bdrv_file_open(), but also in bdrv_open(). Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- block.c | 11 ++++++----- tests/qemu-iotests/051 | 12 ++++++++++++ tests/qemu-iotests/051.out | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/block.c b/block.c index cb21a5fa61..ac0ccac76d 100644 --- a/block.c +++ b/block.c @@ -832,6 +832,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, filename = qdict_get_try_str(options, "filename"); } + if (drv->bdrv_needs_filename && !filename) { + error_setg(errp, "The '%s' block driver requires a file name", + drv->format_name); + return -EINVAL; + } + trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); node_name = qdict_get_try_str(options, "node-name"); @@ -1031,11 +1037,6 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, goto fail; } qdict_del(options, "filename"); - } else if (drv->bdrv_needs_filename && !filename) { - error_setg(errp, "The '%s' block driver requires a file name", - drv->format_name); - ret = -EINVAL; - goto fail; } if (!drv->bdrv_file_open) { diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index b23d91b6f0..46345fb155 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -171,6 +171,18 @@ echo run_qemu -drive file="$TEST_IMG",file.driver=file run_qemu -drive file="$TEST_IMG",file.driver=qcow2 +echo +echo === Leaving out required options === +echo + +run_qemu -drive driver=file +run_qemu -drive driver=nbd +run_qemu -drive driver=raw +run_qemu -drive file.driver=file +run_qemu -drive file.driver=nbd +run_qemu -drive file.driver=raw +run_qemu -drive foo=bar + echo echo === Parsing protocol from file name === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index d0c5173626..30e2dbd6d7 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -225,6 +225,30 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename' +=== Leaving out required options === + +Testing: -drive driver=file +QEMU_PROG: -drive driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name + +Testing: -drive driver=nbd +QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument + +Testing: -drive driver=raw +QEMU_PROG: -drive driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level + +Testing: -drive file.driver=file +QEMU_PROG: -drive file.driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name + +Testing: -drive file.driver=nbd +QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument + +Testing: -drive file.driver=raw +QEMU_PROG: -drive file.driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level + +Testing: -drive foo=bar +QEMU_PROG: -drive foo=bar: could not open disk image ide0-hd0: Must specify either driver or file + + === Parsing protocol from file name === Testing: -hda foo:bar From 5d259fc7da83249a4f78fe32de2bc2874a997a9f Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Wed, 29 Jan 2014 13:03:35 +0100 Subject: [PATCH 08/18] block/iscsi: always fill bs->bl.opt_transfer_length the opt_transfer_length has nothing to do with logical block provisioning stuff so always copy it from the block limits VPD page. Reported-By: Benoit Canet Signed-off-by: Peter Lieven Reviewed-by: Benoit Canet Signed-off-by: Kevin Wolf --- block/iscsi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/block/iscsi.c b/block/iscsi.c index 6f4af72a75..8d0f9667c5 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1330,10 +1330,9 @@ static int iscsi_refresh_limits(BlockDriverState *bs) } bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); - - bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len, - iscsilun); } + bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len, + iscsilun); return 0; } From 16f0587e0a5da5b1ad76cb7c3739491bc042201c Mon Sep 17 00:00:00 2001 From: Hu Tao Date: Sun, 26 Jan 2014 11:12:37 +0800 Subject: [PATCH 09/18] qcow2: remove n_start and n_end of qcow2_alloc_cluster_offset() n_start can be actually calculated from offset. The number of sectors to be allocated(n_end - n_start) can be passed in in num. By removing n_start and n_end, we can save two parameters. The side effect is there is a bug in qcow2.c:preallocate() that passes incorrect n_start to qcow2_alloc_cluster_offset() is fixed. The bug can be triggerred by a larger cluster size than the default value(65536), for example: ./qemu-img create -f qcow2 \ -o 'cluster_size=131072,preallocation=metadata' file.img 4G Signed-off-by: Hu Tao Reviewed-by: Max Reitz Reviewed-by: Benoit Canet Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 14 ++++++-------- block/qcow2.c | 13 +++++++------ block/qcow2.h | 2 +- trace-events | 2 +- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 853408438a..c57f39dd2b 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1182,7 +1182,7 @@ fail: * Return 0 on success and -errno in error cases */ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, - int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m) + int *num, uint64_t *host_offset, QCowL2Meta **m) { BDRVQcowState *s = bs->opaque; uint64_t start, remaining; @@ -1190,15 +1190,13 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, uint64_t cur_bytes; int ret; - trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, - n_start, n_end); + trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *num); - assert(n_start * BDRV_SECTOR_SIZE == offset_into_cluster(s, offset)); - offset = start_of_cluster(s, offset); + assert((offset & ~BDRV_SECTOR_MASK) == 0); again: - start = offset + (n_start << BDRV_SECTOR_BITS); - remaining = (n_end - n_start) << BDRV_SECTOR_BITS; + start = offset; + remaining = *num << BDRV_SECTOR_BITS; cluster_offset = 0; *host_offset = 0; cur_bytes = 0; @@ -1284,7 +1282,7 @@ again: } } - *num = (n_end - n_start) - (remaining >> BDRV_SECTOR_BITS); + *num -= remaining >> BDRV_SECTOR_BITS; assert(*num > 0); assert(*host_offset != 0); diff --git a/block/qcow2.c b/block/qcow2.c index 99a1ad13e6..a274d5f9f9 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1000,7 +1000,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, { BDRVQcowState *s = bs->opaque; int index_in_cluster; - int n_end; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ uint64_t cluster_offset; @@ -1024,14 +1023,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, trace_qcow2_writev_start_part(qemu_coroutine_self()); index_in_cluster = sector_num & (s->cluster_sectors - 1); - n_end = index_in_cluster + remaining_sectors; + cur_nr_sectors = remaining_sectors; if (s->crypt_method && - n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) { - n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; + cur_nr_sectors > + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) { + cur_nr_sectors = + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster; } ret = qcow2_alloc_cluster_offset(bs, sector_num << 9, - index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta); + &cur_nr_sectors, &cluster_offset, &l2meta); if (ret < 0) { goto fail; } @@ -1408,7 +1409,7 @@ static int preallocate(BlockDriverState *bs) while (nb_sectors) { num = MIN(nb_sectors, INT_MAX >> 9); - ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, + ret = qcow2_alloc_cluster_offset(bs, offset, &num, &host_offset, &meta); if (ret < 0) { return ret; diff --git a/block/qcow2.h b/block/qcow2.h index b5b7d13630..0b0eac899c 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -468,7 +468,7 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset); int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, - int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m); + int *num, uint64_t *host_offset, QCowL2Meta **m); uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size); diff --git a/trace-events b/trace-events index 1b668d1ac2..ab11f9721d 100644 --- a/trace-events +++ b/trace-events @@ -495,7 +495,7 @@ qcow2_writev_done_part(void *co, int cur_nr_sectors) "co %p cur_nr_sectors %d" qcow2_writev_data(void *co, uint64_t offset) "co %p offset %" PRIx64 # block/qcow2-cluster.c -qcow2_alloc_clusters_offset(void *co, uint64_t offset, int n_start, int n_end) "co %p offet %" PRIx64 " n_start %d n_end %d" +qcow2_alloc_clusters_offset(void *co, uint64_t offset, int num) "co %p offet %" PRIx64 " num %d" qcow2_handle_copied(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offet %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64 qcow2_handle_alloc(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offet %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64 qcow2_do_alloc_clusters_offset(void *co, uint64_t guest_offset, uint64_t host_offset, int nb_clusters) "co %p guest_offet %" PRIx64 " host_offset %" PRIx64 " nb_clusters %d" From 33304ec9fa484e765c6249673e09e1b7d49c5b85 Mon Sep 17 00:00:00 2001 From: Hu Tao Date: Sun, 26 Jan 2014 11:12:38 +0800 Subject: [PATCH 10/18] qcow2: fix offset overflow in qcow2_alloc_clusters_at() When cluster size is big enough it can lead to an offset overflow in qcow2_alloc_clusters_at(). This patch fixes it. The allocation is stopped each time at L2 table boundary (see handle_alloc()), so the possible maximum bytes could be 2^(cluster_bits - 3 + cluster_bits) cluster_bits - 3 is used to compute the number of entry by L2 and the additional cluster_bits is to take into account each clusters referenced by the L2 entries. so int is safe for cluster_bits<=17, unsafe otherwise. Signed-off-by: Hu Tao Reviewed-by: Max Reitz Reviewed-by: Benoit Canet Signed-off-by: Kevin Wolf --- block/qcow2-refcount.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index c974abe795..8712d8bd54 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -676,7 +676,13 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, BDRVQcowState *s = bs->opaque; uint64_t cluster_index; uint64_t old_free_cluster_index; - int i, refcount, ret; + uint64_t i; + int refcount, ret; + + assert(nb_clusters >= 0); + if (nb_clusters == 0) { + return 0; + } /* Check how many clusters there are free */ cluster_index = offset >> s->cluster_bits; From 7c2bbf4aa66ca5a9fc2ca147e0e6cb6f407a3aa2 Mon Sep 17 00:00:00 2001 From: Hu Tao Date: Sun, 26 Jan 2014 11:12:39 +0800 Subject: [PATCH 11/18] qcow2: check for NULL l2meta In the case of a metadata preallocation with a large cluster size, qcow2_alloc_cluster_offset() can allocate nothing and returns a NULL l2meta. This patch checks for it and link2 l2 with only valid l2meta. Replace 9 and 512 with BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE respectively while at the function. Signed-off-by: Hu Tao Reviewed-by: Max Reitz Reviewed-by: Benoit Canet Signed-off-by: Kevin Wolf --- block/qcow2.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index a274d5f9f9..0b4335ca5b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1404,34 +1404,34 @@ static int preallocate(BlockDriverState *bs) int ret; QCowL2Meta *meta; - nb_sectors = bdrv_getlength(bs) >> 9; + nb_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; offset = 0; while (nb_sectors) { - num = MIN(nb_sectors, INT_MAX >> 9); + num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS); ret = qcow2_alloc_cluster_offset(bs, offset, &num, &host_offset, &meta); if (ret < 0) { return ret; } - ret = qcow2_alloc_cluster_link_l2(bs, meta); - if (ret < 0) { - qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters, - QCOW2_DISCARD_NEVER); - return ret; - } - - /* There are no dependent requests, but we need to remove our request - * from the list of in-flight requests */ if (meta != NULL) { + ret = qcow2_alloc_cluster_link_l2(bs, meta); + if (ret < 0) { + qcow2_free_any_clusters(bs, meta->alloc_offset, + meta->nb_clusters, QCOW2_DISCARD_NEVER); + return ret; + } + + /* There are no dependent requests, but we need to remove our + * request from the list of in-flight requests */ QLIST_REMOVE(meta, next_in_flight); } /* TODO Preallocate data if requested */ nb_sectors -= num; - offset += num << 9; + offset += num << BDRV_SECTOR_BITS; } /* @@ -1440,9 +1440,10 @@ static int preallocate(BlockDriverState *bs) * EOF). Extend the image to the last allocated sector. */ if (host_offset != 0) { - uint8_t buf[512]; - memset(buf, 0, 512); - ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1); + uint8_t buf[BDRV_SECTOR_SIZE]; + memset(buf, 0, BDRV_SECTOR_SIZE); + ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1, + buf, 1); if (ret < 0) { return ret; } From 693a50ade339e3ef9b042fd73a3b81405101ba3d Mon Sep 17 00:00:00 2001 From: Hu Tao Date: Sun, 26 Jan 2014 11:12:40 +0800 Subject: [PATCH 12/18] qemu-iotests: add test for qcow2 preallocation with different cluster sizes Reviewed-by: Max Reitz Signed-off-by: Hu Tao Signed-off-by: Kevin Wolf --- tests/qemu-iotests/079 | 63 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/079.out | 32 +++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 96 insertions(+) create mode 100755 tests/qemu-iotests/079 create mode 100644 tests/qemu-iotests/079.out diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 new file mode 100755 index 0000000000..2142bbb377 --- /dev/null +++ b/tests/qemu-iotests/079 @@ -0,0 +1,63 @@ +#!/bin/bash +# +# Test qcow2 preallocation with different cluster_sizes +# +# Copyright (C) 2014 Fujitsu. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=hutao@cn.fujitsu.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function test_qemu_img() +{ + echo qemu-img "$@" | _filter_testdir + $QEMU_IMG "$@" 2>&1 | _filter_testdir + echo +} + +echo "=== Check option preallocation and cluster_size ===" +echo +cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304" + +for s in $cluster_sizes; do + test_qemu_img create -f $IMGFMT -o preallocation=metadata,cluster_size=$s "$TEST_IMG" 4G +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/079.out b/tests/qemu-iotests/079.out new file mode 100644 index 0000000000..ef4b8c9117 --- /dev/null +++ b/tests/qemu-iotests/079.out @@ -0,0 +1,32 @@ +QA output created by 079 +=== Check option preallocation and cluster_size === + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=16384 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=16384 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=32768 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=32768 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=65536 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=131072 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=131072 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=262144 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=262144 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=524288 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=524288 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=1048576 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=1048576 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=2097152 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=2097152 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=4194304 TEST_DIR/t.qcow2 4G +qemu-img: TEST_DIR/t.qcow2: Cluster size must be a power of two between 512 and 2048k +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=4194304 preallocation='metadata' lazy_refcounts=off + +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 03c762fb4f..d8be74a17e 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -82,3 +82,4 @@ 073 rw auto 074 rw auto 077 rw auto +079 rw auto From ad6aef43d36fe65701ff84193576d7f3dcb82dc5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 4 Feb 2014 11:54:13 +0100 Subject: [PATCH 13/18] raw: Fix BlockLimits passthrough raw copies over the BlockLimits of bs->file during bdrv_open(). However, since commit d34682cd it is immediately overwritten during bdrv_refresh_limits(). This caused all fields except for opt_transfer_length and opt_mem_alignment (which happen to be correctly inherited in generic code) to be zeroed. Move the BlockLimit assignment to a .bdrv_refresh_limits() callback to make it work again for all fields. Reported-by: Laszlo Ersek Signed-off-by: Kevin Wolf Reviewed-by: Laszlo Ersek --- block/raw_bsd.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 978ae7a102..af8706dc97 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -90,6 +90,12 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return bdrv_get_info(bs->file, bdi); } +static int raw_refresh_limits(BlockDriverState *bs) +{ + bs->bl = bs->file->bl; + return 0; +} + static int raw_truncate(BlockDriverState *bs, int64_t offset) { return bdrv_truncate(bs->file, offset); @@ -150,7 +156,6 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { bs->sg = bs->file->sg; - bs->bl = bs->file->bl; return 0; } @@ -182,6 +187,7 @@ static BlockDriver bdrv_raw = { .bdrv_getlength = &raw_getlength, .has_variable_length = true, .bdrv_get_info = &raw_get_info, + .bdrv_refresh_limits = &raw_refresh_limits, .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, From 99c4a85ce65863e6ba6668164d47c0d7c645e3aa Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Feb 2014 15:29:00 +0100 Subject: [PATCH 14/18] block: Fix memory leaks in bdrv_co_do_pwritev() The error path for a failure in one of the two bdrv_aligned_preadv() calls leaked head_buf or tail_buf, respectively. This fixes the memory leak. Reported-by: Laszlo Ersek Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Laszlo Ersek --- block.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index ac0ccac76d..c1d1f74b88 100644 --- a/block.c +++ b/block.c @@ -3279,9 +3279,9 @@ fail: if (use_local_qiov) { qemu_iovec_destroy(&local_qiov); - qemu_vfree(head_buf); - qemu_vfree(tail_buf); } + qemu_vfree(head_buf); + qemu_vfree(tail_buf); return ret; } From af91f9a73c3a67eebbf4120cae62b82db8eaae19 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Feb 2014 15:35:56 +0100 Subject: [PATCH 15/18] block: bdrv_aligned_pwritev: Assert overlap range This adds assertions that the request that we actually end up passing to the block driver (which includes RMW data and has therefore potentially been rounded to alignment boundaries) is fully covered by the overlap_{offset,size} fields of the associated BdrvTrackedRequest. Suggested-by: Laszlo Ersek Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Laszlo Ersek --- block.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block.c b/block.c index c1d1f74b88..a027823ea4 100644 --- a/block.c +++ b/block.c @@ -3134,6 +3134,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, waited = wait_serialising_requests(req); assert(!waited || !req->serialising); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); From 5f5bcd80f8c365bca1480dc39141952fa7f88c71 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 7 Feb 2014 16:00:09 +0100 Subject: [PATCH 16/18] block: Don't call ROUND_UP with negative values The behaviour of the ROUND_UP macro with negative numbers isn't obvious. It happens to do the right thing in this please, but better avoid it. Suggested-by: Laszlo Ersek Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Laszlo Ersek --- block.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index a027823ea4..b0c5025977 100644 --- a/block.c +++ b/block.c @@ -2915,8 +2915,8 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, } total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE); - max_nb_sectors = MAX(0, ROUND_UP(total_sectors - sector_num, - align >> BDRV_SECTOR_BITS)); + max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num), + align >> BDRV_SECTOR_BITS); if (max_nb_sectors > 0) { ret = drv->bdrv_co_readv(bs, sector_num, MIN(nb_sectors, max_nb_sectors), qiov); From eaf944a43835399f12808aebd0d0a1db6249ed07 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Sat, 8 Feb 2014 09:53:22 +0100 Subject: [PATCH 17/18] blkdebug: Don't leak bs->file on failure Reported-by: Laszlo Ersek Signed-off-by: Kevin Wolf Reviewed-by: Laszlo Ersek --- block/blkdebug.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 56c4cd084f..8eb0db0723 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -396,14 +396,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, if (error_is_set(&local_err)) { error_propagate(errp, local_err); ret = -EINVAL; - goto fail; + goto out; } /* Read rules from config file or command line options */ config = qemu_opt_get(opts, "config"); ret = read_config(s, config, options, errp); if (ret) { - goto fail; + goto out; } /* Set initial state */ @@ -414,7 +414,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, flags, true, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); - goto fail; + goto out; } /* Set request alignment */ @@ -424,11 +424,15 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, } else { error_setg(errp, "Invalid alignment"); ret = -EINVAL; - goto fail; + goto fail_unref; } ret = 0; -fail: + goto out; + +fail_unref: + bdrv_unref(bs->file); +out: qemu_opts_del(opts); return ret; } From e96126ffa53d36ec75a1ee900a6b7e7c82d9bb9c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Sat, 8 Feb 2014 10:42:18 +0100 Subject: [PATCH 18/18] block: Fix 32 bit truncation in mark_request_serialising() On 32 bit hosts, size_t is too small for align as the bitmask ~(align - 1) will zero out the higher 32 bits of the offset. While at it, change the local overlap_bytes variable to unsigned to match the field in BdrvTrackedRequest. Signed-off-by: Kevin Wolf Reviewed-by: Laszlo Ersek --- block.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block.c b/block.c index b0c5025977..636aa117c3 100644 --- a/block.c +++ b/block.c @@ -2240,11 +2240,11 @@ static void tracked_request_begin(BdrvTrackedRequest *req, QLIST_INSERT_HEAD(&bs->tracked_requests, req, list); } -static void mark_request_serialising(BdrvTrackedRequest *req, size_t align) +static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) { int64_t overlap_offset = req->offset & ~(align - 1); - int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - - overlap_offset; + unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) + - overlap_offset; if (!req->serialising) { req->bs->serialising_in_flight++;