diff --git a/block/curl.c b/block/curl.c index 4e77c93b46..6dccf00256 100644 --- a/block/curl.c +++ b/block/curl.c @@ -324,17 +324,11 @@ curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len, CURLAIOCB *acb) static void curl_multi_check_completion(BDRVCURLState *s) { int msgs_in_queue; + CURLMsg *msg; /* Try to find done transfers, so we can free the easy * handle again. */ - for (;;) { - CURLMsg *msg; - msg = curl_multi_info_read(s->multi, &msgs_in_queue); - - /* Quit when there are no more completions */ - if (!msg) - break; - + while ((msg = curl_multi_info_read(s->multi, &msgs_in_queue))) { if (msg->msg == CURLMSG_DONE) { int i; CURLState *state = NULL; @@ -397,7 +391,6 @@ static void curl_multi_check_completion(BDRVCURLState *s) } curl_clean_state(state); - break; } } } diff --git a/block/mirror.c b/block/mirror.c index bc982cb99a..fa1d975eb9 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -99,6 +99,7 @@ typedef struct MirrorBlockJob { typedef struct MirrorBDSOpaque { MirrorBlockJob *job; + BdrvDirtyBitmap *dirty_bitmap; bool stop; bool is_commit; } MirrorBDSOpaque; @@ -1675,9 +1676,11 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, abort(); } - if (!copy_to_target && s->job && s->job->dirty_bitmap) { - qatomic_set(&s->job->actively_synced, false); - bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + if (!copy_to_target) { + if (s->job) { + qatomic_set(&s->job->actively_synced, false); + } + bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, bytes); } if (ret < 0) { @@ -1904,13 +1907,35 @@ static BlockJob *mirror_start_job( bdrv_drained_begin(bs); ret = bdrv_append(mirror_top_bs, bs, errp); - bdrv_drained_end(bs); - if (ret < 0) { + bdrv_drained_end(bs); + bdrv_unref(mirror_top_bs); + return NULL; + } + + bs_opaque->dirty_bitmap = bdrv_create_dirty_bitmap(mirror_top_bs, + granularity, + NULL, errp); + if (!bs_opaque->dirty_bitmap) { + bdrv_drained_end(bs); bdrv_unref(mirror_top_bs); return NULL; } + /* + * The mirror job doesn't use the block layer's dirty tracking because it + * needs to be able to switch seemlessly between background copy mode (which + * does need dirty tracking) and write blocking mode (which doesn't) and + * doing that would require draining the node. Instead, mirror_top_bs takes + * care of updating the dirty bitmap as appropriate. + * + * Note that write blocking mode only becomes effective after mirror_run() + * sets mirror_top_opaque->job (see should_copy_to_target()). Until then, + * we're still in background copy mode irrespective of @copy_mode. + */ + bdrv_disable_dirty_bitmap(bs_opaque->dirty_bitmap); + bdrv_drained_end(bs); + /* Make sure that the source is not resized while the job is running */ s = block_job_create(job_id, driver, NULL, mirror_top_bs, BLK_PERM_CONSISTENT_READ, @@ -2005,24 +2030,13 @@ static BlockJob *mirror_start_job( s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); + s->dirty_bitmap = bs_opaque->dirty_bitmap; s->unmap = unmap; if (auto_complete) { s->should_complete = true; } bdrv_graph_rdunlock_main_loop(); - s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, - NULL, errp); - if (!s->dirty_bitmap) { - goto fail; - } - - /* - * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active - * mode. - */ - bdrv_disable_dirty_bitmap(s->dirty_bitmap); - bdrv_graph_wrlock_drained(); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | @@ -2102,9 +2116,6 @@ fail: g_free(s->replaces); blk_unref(s->target); bs_opaque->job = NULL; - if (s->dirty_bitmap) { - bdrv_release_dirty_bitmap(s->dirty_bitmap); - } job_early_fail(&s->common.job); } @@ -2118,6 +2129,7 @@ fail: bdrv_graph_wrunlock(); bdrv_drained_end(bs); + bdrv_release_dirty_bitmap(bs_opaque->dirty_bitmap); bdrv_unref(mirror_top_bs); return NULL; diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 3391cee4d2..1fd28d59eb 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -422,7 +422,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) /* Then try adding all block devices. If one fails, close all and * exit. */ - block_list = qmp_query_block(NULL); + block_list = qmp_query_block(true, true, NULL); for (info = block_list; info; info = info->next) { if (!info->value->inserted) { @@ -741,7 +741,7 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) /* Print BlockBackend information */ if (!nodes) { - block_list = qmp_query_block(NULL); + block_list = qmp_query_block(false, false, NULL); } else { block_list = NULL; } diff --git a/block/nfs.c b/block/nfs.c index 1d3a34a30c..b78f4f86e8 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -249,14 +249,15 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, } /* - * Safe to call: nfs_service(), which called us, is only run from the FD - * handlers, never from the request coroutine. The request coroutine in - * turn will yield unconditionally. - * No need to release the lock, even if we directly enter the coroutine, as - * the lock is never re-taken after yielding. (Note: If we do enter the - * coroutine, @task will probably be dangling once aio_co_wake() returns.) + * Using aio_co_wake() here could re-enter the coroutine directly, while we + * still hold the mutex. The current request will not attempt to re-take + * the mutex, so that is fine; but if the same coroutine then goes on to + * submit another request, that new request will try to re-take the mutex, + * resulting in a deadlock. + * To prevent that, only schedule the coroutine so it will be entered later, + * with the mutex released. */ - aio_co_wake(task->co); + aio_co_schedule(qemu_coroutine_get_aio_context(task->co), task->co); } static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset, @@ -716,8 +717,8 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, if (task->ret < 0) { error_report("NFS Error: %s", nfs_get_error(nfs)); } - /* Safe to call, see nfs_co_generic_cb() */ - aio_co_wake(task->co); + /* Must not use aio_co_wake(), see nfs_co_generic_cb() */ + aio_co_schedule(qemu_coroutine_get_aio_context(task->co), task->co); } static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs) diff --git a/block/qapi.c b/block/qapi.c index 27e0ac6a32..eabfbfc258 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -456,7 +456,7 @@ fail: /* @p_info will be set only on success. */ static void GRAPH_RDLOCK -bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, Error **errp) +bdrv_query_info(BlockBackend *blk, bool flat, BlockInfo **p_info, Error **errp) { BlockInfo *info = g_malloc0(sizeof(*info)); BlockDriverState *bs = blk_bs(blk); @@ -488,7 +488,7 @@ bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, Error **errp) } if (bs && bs->drv) { - info->inserted = bdrv_block_device_info(blk, bs, false, errp); + info->inserted = bdrv_block_device_info(blk, bs, flat, errp); if (info->inserted == NULL) { goto err; } @@ -698,7 +698,7 @@ bdrv_query_bds_stats(BlockDriverState *bs, bool blk_level) return s; } -BlockInfoList *qmp_query_block(Error **errp) +BlockInfoList *qmp_query_block(bool has_flat, bool flat, Error **errp) { BlockInfoList *head = NULL, **p_next = &head; BlockBackend *blk; @@ -714,7 +714,7 @@ BlockInfoList *qmp_query_block(Error **errp) } info = g_malloc0(sizeof(*info)); - bdrv_query_info(blk, &info->value, &local_err); + bdrv_query_info(blk, flat, &info->value, &local_err); if (local_err) { error_propagate(errp, local_err); g_free(info); diff --git a/block/qcow2.c b/block/qcow2.c index e29810d86a..81fd299b4c 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3991,6 +3991,8 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, BlockDriverState *bs = NULL; BlockDriverState *data_bs = NULL; const char *val; + bool keep_data_file = false; + BlockdevCreateOptionsQcow2 *qcow2_opts; int ret; /* Only the keyval visitor supports the dotted syntax needed for @@ -4022,6 +4024,22 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3"); } + val = qdict_get_try_str(qdict, BLOCK_OPT_KEEP_DATA_FILE); + if (val) { + if (!strcmp(val, "on")) { + keep_data_file = true; + } else if (!strcmp(val, "off")) { + keep_data_file = false; + } else { + error_setg(errp, + "Invalid value '%s' for '%s': Must be 'on' or 'off'", + val, BLOCK_OPT_KEEP_DATA_FILE); + ret = -EINVAL; + goto finish; + } + qdict_del(qdict, BLOCK_OPT_KEEP_DATA_FILE); + } + /* Change legacy command line options into QMP ones */ static const QDictRenames opt_renames[] = { { BLOCK_OPT_BACKING_FILE, "backing-file" }, @@ -4058,9 +4076,11 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, /* Create and open an external data file (protocol layer) */ val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE); if (val) { - ret = bdrv_co_create_file(val, opts, false, errp); - if (ret < 0) { - goto finish; + if (!keep_data_file) { + ret = bdrv_co_create_file(val, opts, false, errp); + if (ret < 0) { + goto finish; + } } data_bs = bdrv_co_open(val, NULL, NULL, @@ -4073,6 +4093,11 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, qdict_del(qdict, BLOCK_OPT_DATA_FILE); qdict_put_str(qdict, "data-file", data_bs->node_name); + } else if (keep_data_file) { + error_setg(errp, "Must not use '%s=on' without '%s'", + BLOCK_OPT_KEEP_DATA_FILE, BLOCK_OPT_DATA_FILE); + ret = -EINVAL; + goto finish; } /* Set 'driver' and 'node' options */ @@ -4093,9 +4118,39 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, goto finish; } + qcow2_opts = &create_options->u.qcow2; + + if (!qcow2_opts->has_preallocation) { + qcow2_opts->preallocation = PREALLOC_MODE_OFF; + } + + if (keep_data_file && + qcow2_opts->preallocation != PREALLOC_MODE_OFF && + qcow2_opts->preallocation != PREALLOC_MODE_METADATA) + { + error_setg(errp, "Preallocating more than only metadata would " + "overwrite the external data file's content and is " + "therefore incompatible with '%s=on'", + BLOCK_OPT_KEEP_DATA_FILE); + ret = -EINVAL; + goto finish; + } + + if (keep_data_file && + qcow2_opts->preallocation == PREALLOC_MODE_OFF && + !qcow2_opts->data_file_raw) + { + error_setg(errp, "'%s=on' requires '%s=metadata' or '%s=on', or the " + "file contents will not be visible", + BLOCK_OPT_KEEP_DATA_FILE, + BLOCK_OPT_PREALLOC, + BLOCK_OPT_DATA_FILE_RAW); + ret = -EINVAL; + goto finish; + } + /* Silently round up size */ - create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size, - BDRV_SECTOR_SIZE); + qcow2_opts->size = ROUND_UP(qcow2_opts->size, BDRV_SECTOR_SIZE); /* Create the qcow2 image (format layer) */ ret = qcow2_co_create(create_options, errp); @@ -4103,7 +4158,9 @@ finish: if (ret < 0) { bdrv_graph_co_rdlock(); bdrv_co_delete_file_noerr(bs); - bdrv_co_delete_file_noerr(data_bs); + if (!keep_data_file) { + bdrv_co_delete_file_noerr(data_bs); + } bdrv_graph_co_rdunlock(); } else { ret = 0; @@ -6202,6 +6259,12 @@ static QemuOptsList qcow2_create_opts = { .help = "Compression method used for image cluster " \ "compression", \ .def_value_str = "zlib" \ + }, \ + { \ + .name = BLOCK_OPT_KEEP_DATA_FILE, \ + .type = QEMU_OPT_BOOL, \ + .help = "Assume the external data file already exists and " \ + "do not overwrite it" \ }, QCOW_COMMON_OPTIONS, { /* end of list */ } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 66fdce9a90..5329ff1fdb 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -295,19 +295,15 @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm, /* Start the next pending I/O request for a ThrottleGroupMember. Return whether * any request was actually pending. * + * This assumes that tg->lock is held. + * * @tgm: the current ThrottleGroupMember * @direction: the ThrottleDirection */ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm, ThrottleDirection direction) { - bool ret; - - qemu_co_mutex_lock(&tgm->throttled_reqs_lock); - ret = qemu_co_queue_next(&tgm->throttled_reqs[direction]); - qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); - - return ret; + return qemu_co_queue_next(&tgm->throttled_reqs[direction]); } /* Look for the next pending I/O request and schedule it. @@ -378,12 +374,8 @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm /* Wait if there's a timer set or queued requests of this type */ if (must_wait || tgm->pending_reqs[direction]) { tgm->pending_reqs[direction]++; - qemu_mutex_unlock(&tg->lock); - qemu_co_mutex_lock(&tgm->throttled_reqs_lock); qemu_co_queue_wait(&tgm->throttled_reqs[direction], - &tgm->throttled_reqs_lock); - qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); - qemu_mutex_lock(&tg->lock); + &tg->lock); tgm->pending_reqs[direction]--; } @@ -410,15 +402,15 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) ThrottleDirection direction = data->direction; bool empty_queue; + qemu_mutex_lock(&tg->lock); empty_queue = !throttle_group_co_restart_queue(tgm, direction); /* If the request queue was empty then we have to take care of * scheduling the next one */ if (empty_queue) { - qemu_mutex_lock(&tg->lock); schedule_next_request(tgm, direction); - qemu_mutex_unlock(&tg->lock); } + qemu_mutex_unlock(&tg->lock); g_free(data); @@ -569,7 +561,6 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, read_timer_cb, write_timer_cb, tgm); - qemu_co_mutex_init(&tgm->throttled_reqs_lock); } /* Unregister a ThrottleGroupMember from its group, removing it from the list, diff --git a/block/vmdk.c b/block/vmdk.c index 89e89cd10e..cd8b4ec7c8 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1951,10 +1951,10 @@ vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, marker = (VmdkGrainMarker *)cluster_buf; compressed_data = marker->data; data_len = le32_to_cpu(marker->size); - } - if (!data_len || data_len > buf_bytes) { - ret = -EINVAL; - goto out; + if (!data_len || data_len > buf_bytes - sizeof(VmdkGrainMarker)) { + ret = -EINVAL; + goto out; + } } ret = uncompress(uncomp_buf, &buf_len, compressed_data, data_len); if (ret != Z_OK) { diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 9324af903d..147c08155f 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -56,6 +56,7 @@ #define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" #define BLOCK_OPT_COMPRESSION_TYPE "compression_type" #define BLOCK_OPT_EXTL2 "extended_l2" +#define BLOCK_OPT_KEEP_DATA_FILE "keep_data_file" #define BLOCK_PROBE_BUF_SIZE 512 diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index 2355e8d9de..7dfc81f7b5 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -35,8 +35,7 @@ typedef struct ThrottleGroupMember { AioContext *aio_context; - /* throttled_reqs_lock protects the CoQueues for throttled requests. */ - CoMutex throttled_reqs_lock; + /* Protected by ThrottleGroup.lock */ CoQueue throttled_reqs[THROTTLE_MAX]; /* Nonzero if the I/O limits are currently being ignored; generally diff --git a/monitor/monitor.c b/monitor/monitor.c index 6dc5a7016d..00b93ed612 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -330,14 +330,33 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) { MonitorQAPIEventConf *evconf; MonitorQAPIEventState *evstate; + bool throttled; assert(event < QAPI_EVENT__MAX); evconf = &monitor_qapi_event_conf[event]; trace_monitor_protocol_event_queue(event, qdict, evconf->rate); + throttled = evconf->rate; + + /* + * Rate limit BLOCK_IO_ERROR only for action != "stop". + * + * If the VM is stopped after an I/O error, this is important information + * for the management tool to keep track of the state of QEMU and we can't + * merge any events. At the same time, stopping the VM means that the guest + * can't send additional requests and the number of events is already + * limited, so we can do without rate limiting. + */ + if (event == QAPI_EVENT_BLOCK_IO_ERROR) { + QDict *data = qobject_to(QDict, qdict_get(qdict, "data")); + const char *action = qdict_get_str(data, "action"); + if (!strcmp(action, "stop")) { + throttled = false; + } + } QEMU_LOCK_GUARD(&monitor_lock); - if (!evconf->rate) { + if (!throttled) { /* Unthrottled event */ monitor_qapi_event_emit(event, qdict); } else { diff --git a/qapi/block-core.json b/qapi/block-core.json index b82af74256..da0b36a375 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -855,6 +855,10 @@ # # Get a list of `BlockInfo` for all virtual block devices. # +# @flat: Omit nested data about the backing image, i.e. `BlockInfo` +# member 'inserted.image.backing-image' will be absent. +# Default is false. (Since 11.0) +# # Returns: a list describing each virtual block device. Filter nodes # that were created implicitly are skipped over. # @@ -945,6 +949,7 @@ # } ## { 'command': 'query-block', 'returns': ['BlockInfo'], + 'data': { '*flat': 'bool' }, 'allow-preconfig': true } ## @@ -5789,7 +5794,7 @@ # .. note:: If action is "stop", a `STOP` event will eventually follow # the `BLOCK_IO_ERROR` event. # -# .. note:: This event is rate-limited. +# .. note:: This event is rate-limited, except if action is "stop". # # Since: 0.13 # diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index d0dd333117..e0463815c6 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -66,6 +66,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -92,6 +93,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -118,6 +120,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -144,6 +147,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -170,6 +174,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -196,6 +201,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -222,6 +228,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -248,6 +255,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -288,6 +296,7 @@ Supported qcow2 options: encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits= - Width of a reference count entry in bits @@ -376,6 +385,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -402,6 +412,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -428,6 +439,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -454,6 +466,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -480,6 +493,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -506,6 +520,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -532,6 +547,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -558,6 +574,7 @@ Supported options: encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables extent_size_hint= - Extent size hint for the image file, 0 to disable + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates nocow= - Turn off copy-on-write (valid only on btrfs) preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) @@ -598,6 +615,7 @@ Supported qcow2 options: encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) extended_l2= - Extended L2 tables + keep_data_file= - Assume the external data file already exists and do not overwrite it lazy_refcounts= - Postpone refcount updates preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) refcount_bits= - Width of a reference count entry in bits diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244 index bb9cc6512f..2a98ad58f5 100755 --- a/tests/qemu-iotests/244 +++ b/tests/qemu-iotests/244 @@ -384,6 +384,77 @@ $QEMU_IMG compare --image-opts \ "driver=raw,file.filename=$TEST_IMG.data" \ "file.filename=$TEST_IMG,backing.file.filename=$TEST_IMG.base" +echo +echo '=== keep_data_file tests ===' + +echo +echo '--- Creating test data file ---' + +# Easiest way to create the raw data file without having to create and +# access it manually +_make_test_img -o "data_file=$TEST_IMG.data,data_file_raw=on" 1M +# Values chosen by a fair random.org evaluation +$QEMU_IO -c 'write -P 3 0 512k' -c 'write -P 96 512k 512k' "$TEST_IMG" | + _filter_qemu_io + +echo +echo '--- Testing stand-alone option ---' + +# Cannot work, needs data file +_make_test_img -o "keep_data_file=on" 1M + +# Invalid option value +_make_test_img -o "keep_data_file=true" 1M + +# Should be the same as omitting +_make_test_img -o "keep_data_file=off" 1M + +# No preallocation is OK when also specifying data_file_raw; otherwise, none of +# the data file will be mapped, i.e. its contents will stay hidden, so +# requesting its contents to be kept (but hidden) doesn't make much sense. +# +# Metadata preallocation is OK: It will not overwrite the data file's contents, +# but ensure the contents are mapped and visible. +# +# Any data preallocation (like falloc) is not OK, as this would overwrite the +# data file's contents despite keep_data_file requesting they should not be +# overwritten. +# +# Note that all of these cases use the data file created above: This verifies +# that when passing keep_data_file=on, the data file is always kept as-is (and +# e.g. not deleted on error). +for prealloc in off metadata falloc full; do + # Without metadata preallocation, the data_file_raw flag is required so that + # the data file's contents are visible. + for data_file_raw in off on; do + echo + echo "--- Testing prealloc=$prealloc data_file_raw=$data_file_raw ---" + + # Remove previously existing qcow2 (metadata) file + _cleanup_test_img + + opts="data_file=$TEST_IMG.data,keep_data_file=on" + opts+=",preallocation=$prealloc" + opts+=",data_file_raw=$data_file_raw" + + _make_test_img -o "$opts" 1M + if [ -f "$TEST_IMG" ]; then + $QEMU_IO -c 'read -P 3 0 512k' -c 'read -P 96 512k 512k' "$TEST_IMG" | + _filter_qemu_io + fi + done +done + +echo +echo '--- Testing non-existent data file ---' + +# Maybe a matter of taste whether this should fail or create the file, but +# failing is simpler (= will always skip create) and seems safer (users may +# expect the file to exist, and the error will warn them when it does not). +_make_test_img \ + -o "data_file=$TEST_IMG.doesnotexist,keep_data_file=on,data_file_raw=on" \ + 1M + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out index f46cfe93f1..c62f5aec25 100644 --- a/tests/qemu-iotests/244.out +++ b/tests/qemu-iotests/244.out @@ -197,4 +197,65 @@ wrote 1048576/1048576 bytes at offset 0 Comparing qcow2 image and raw data file: Images are identical. + +=== keep_data_file tests === + +--- Creating test data file --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +--- Testing stand-alone option --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 keep_data_file=on +qemu-img: TEST_DIR/t.IMGFMT: Must not use 'keep_data_file=on' without 'data_file' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 keep_data_file=true +qemu-img: TEST_DIR/t.IMGFMT: Invalid value 'true' for 'keep_data_file': Must be 'on' or 'off' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 keep_data_file=off + +--- Testing prealloc=off data_file_raw=off --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=off keep_data_file=on preallocation=off +qemu-img: TEST_DIR/t.IMGFMT: 'keep_data_file=on' requires 'preallocation=metadata' or 'data_file_raw=on', or the file contents will not be visible + +--- Testing prealloc=off data_file_raw=on --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on keep_data_file=on preallocation=off +read 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +--- Testing prealloc=metadata data_file_raw=off --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=off keep_data_file=on preallocation=metadata +read 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +--- Testing prealloc=metadata data_file_raw=on --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on keep_data_file=on preallocation=metadata +read 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +--- Testing prealloc=falloc data_file_raw=off --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=off keep_data_file=on preallocation=falloc +qemu-img: TEST_DIR/t.IMGFMT: Preallocating more than only metadata would overwrite the external data file's content and is therefore incompatible with 'keep_data_file=on' + +--- Testing prealloc=falloc data_file_raw=on --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on keep_data_file=on preallocation=falloc +qemu-img: TEST_DIR/t.IMGFMT: Preallocating more than only metadata would overwrite the external data file's content and is therefore incompatible with 'keep_data_file=on' + +--- Testing prealloc=full data_file_raw=off --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=off keep_data_file=on preallocation=full +qemu-img: TEST_DIR/t.IMGFMT: Preallocating more than only metadata would overwrite the external data file's content and is therefore incompatible with 'keep_data_file=on' + +--- Testing prealloc=full data_file_raw=on --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on keep_data_file=on preallocation=full +qemu-img: TEST_DIR/t.IMGFMT: Preallocating more than only metadata would overwrite the external data file's content and is therefore incompatible with 'keep_data_file=on' + +--- Testing non-existent data file --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 data_file=TEST_DIR/t.IMGFMT.doesnotexist data_file_raw=on keep_data_file=on +qemu-img: TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT.doesnotexist': No such file or directory *** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 26e6b45b04..ce4213cb97 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -182,7 +182,7 @@ _do_filter_img_create() -e 's/^\(fmt\)/0-\1/' \ -e 's/^\(size\)/1-\1/' \ -e 's/^\(backing\)/2-\1/' \ - -e 's/^\(data_file\)/3-\1/' \ + -e 's/^\(\(keep_\)\?data_file\)/3-\1/' \ -e 's/^\(encryption\)/4-\1/' \ -e 's/^\(preallocation\)/8-\1/' \ | LC_ALL=C sort \ diff --git a/ui/cocoa.m b/ui/cocoa.m index 23b7a736d7..5b21fe3aea 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1849,7 +1849,7 @@ static void addRemovableDevicesMenuItems(void) BlockInfoList *currentDevice, *pointerToFree; NSString *deviceName; - currentDevice = qmp_query_block(NULL); + currentDevice = qmp_query_block(false, false, NULL); pointerToFree = currentDevice; menu = [[[NSApp mainMenu] itemWithTitle:@"Machine"] submenu];