#include "qapi/qobject-output-visitor.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qstring.h"
#include "qemu/cutils.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
+#include "qemu/sockets.h"
#include "qemu/units.h"
#include "qom/object_interfaces.h"
#include "sysemu/block-backend.h"
#include "block/qapi.h"
#include "crypto/init.h"
#include "trace/control.h"
+#include "qemu/throttle.h"
+#include "block/throttle-groups.h"
#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
"\n" QEMU_COPYRIGHT "\n"
static void dump_json_image_check(ImageCheck *check, bool quiet)
{
- QString *str;
+ GString *str;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
visit_type_ImageCheck(v, NULL, &check, &error_abort);
visit_complete(v, &obj);
- str = qobject_to_json_pretty(obj);
+ str = qobject_to_json_pretty(obj, true);
assert(str != NULL);
- qprintf(quiet, "%s\n", qstring_get_str(str));
+ qprintf(quiet, "%s\n", str->str);
qobject_unref(obj);
visit_free(v);
- qobject_unref(str);
+ g_string_free(str, true);
}
static void dump_human_image_check(ImageCheck *check, bool quiet)
CommonBlockJobCBInfo cbi;
bool image_opts = false;
AioContext *aio_context;
+ int64_t rate_limit = 0;
fmt = NULL;
cache = BDRV_DEFAULT_CACHE;
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":f:ht:b:dpq",
+ c = getopt_long(argc, argv, ":f:ht:b:dpqr:",
long_options, NULL);
if (c == -1) {
break;
case 'q':
quiet = true;
break;
+ case 'r':
+ rate_limit = cvtnum("rate limit", optarg);
+ if (rate_limit < 0) {
+ return 1;
+ }
+ break;
case OPTION_OBJECT: {
QemuOpts *opts;
opts = qemu_opts_parse_noisily(&qemu_object_opts,
/* This is different from QMP, which by default uses the deepest file in
* the backing chain (i.e., the very base); however, the traditional
* behavior of qemu-img commit is using the immediate backing file. */
- base_bs = backing_bs(bs);
+ base_bs = bdrv_backing_chain_next(bs);
if (!base_bs) {
error_setg(&local_err, "Image does not have a backing file");
goto done;
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0,
+ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit,
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
&cbi, false, &local_err);
aio_context_release(aio_context);
* 'pnum' is set to the number of sectors (including and immediately following
* the first one) that are known to be in the same allocated/unallocated state.
* The function will try to align the end offset to alignment boundaries so
- * that the request will at least end aligned and consequtive requests will
+ * that the request will at least end aligned and consecutive requests will
* also start at an aligned offset.
*/
static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum,
*pnum = 0;
return 0;
}
- is_zero = buffer_is_zero(buf, 512);
+ is_zero = buffer_is_zero(buf, BDRV_SECTOR_SIZE);
for(i = 1; i < n; i++) {
- buf += 512;
- if (is_zero != buffer_is_zero(buf, 512)) {
+ buf += BDRV_SECTOR_SIZE;
+ if (is_zero != buffer_is_zero(buf, BDRV_SECTOR_SIZE)) {
break;
}
}
Error **errp)
{
BlockDirtyBitmapMergeSource *merge_src;
- BlockDirtyBitmapMergeSourceList *list;
+ BlockDirtyBitmapMergeSourceList *list = NULL;
merge_src = g_new0(BlockDirtyBitmapMergeSource, 1);
merge_src->type = QTYPE_QDICT;
merge_src->u.external.node = g_strdup(src_node);
merge_src->u.external.name = g_strdup(src_name);
- list = g_new0(BlockDirtyBitmapMergeSourceList, 1);
- list->value = merge_src;
+ QAPI_LIST_PREPEND(list, merge_src);
qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp);
qapi_free_BlockDirtyBitmapMergeSourceList(list);
}
};
#define MAX_COROUTINES 16
+#define CONVERT_THROTTLE_GROUP "img_convert"
typedef struct ImgConvertState {
BlockBackend **src;
int64_t *src_sectors;
+ int *src_alignment;
int src_num;
int64_t total_sectors;
int64_t allocated_sectors;
if (s->sector_next_status <= sector_num) {
uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE;
int64_t count;
+ int tail;
+ BlockDriverState *src_bs = blk_bs(s->src[src_cur]);
+ BlockDriverState *base;
+
+ if (s->target_has_backing) {
+ base = bdrv_cow_bs(bdrv_skip_filters(src_bs));
+ } else {
+ base = NULL;
+ }
do {
count = n * BDRV_SECTOR_SIZE;
- if (s->target_has_backing) {
- ret = bdrv_block_status(blk_bs(s->src[src_cur]), offset,
- count, &count, NULL, NULL);
- } else {
- ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
- offset, count, &count, NULL,
- NULL);
- }
+ ret = bdrv_block_status_above(src_bs, base, offset, count, &count,
+ NULL, NULL);
if (ret < 0) {
if (s->salvage) {
n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE);
+ /*
+ * Avoid that s->sector_next_status becomes unaligned to the source
+ * request alignment and/or cluster size to avoid unnecessary read
+ * cycles.
+ */
+ tail = (sector_num - src_cur_offset + n) % s->src_alignment[src_cur];
+ if (n > tail) {
+ n -= tail;
+ }
+
if (ret & BDRV_BLOCK_ZERO) {
s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO;
} else if (ret & BDRV_BLOCK_DATA) {
#define MAX_BUF_SECTORS 32768
+static void set_rate_limit(BlockBackend *blk, int64_t rate_limit)
+{
+ ThrottleConfig cfg;
+
+ throttle_config_init(&cfg);
+ cfg.buckets[THROTTLE_BPS_WRITE].avg = rate_limit;
+
+ blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP);
+ blk_set_io_limits(blk, &cfg);
+}
+
static int img_convert(int argc, char **argv)
{
int c, bs_i, flags, src_flags = 0;
bool force_share = false;
bool explict_min_sparse = false;
bool bitmaps = false;
+ int64_t rate_limit = 0;
ImgConvertState s = (ImgConvertState) {
/* Need at least 4k of zeros for sparse detection */
{"bitmaps", no_argument, 0, OPTION_BITMAPS},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU",
+ c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WUr:",
long_options, NULL);
if (c == -1) {
break;
case 'U':
force_share = true;
break;
+ case 'r':
+ rate_limit = cvtnum("rate limit", optarg);
+ if (rate_limit < 0) {
+ goto fail_getopt;
+ }
+ break;
case OPTION_OBJECT: {
QemuOpts *object_opts;
object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
s.src = g_new0(BlockBackend *, s.src_num);
s.src_sectors = g_new(int64_t, s.src_num);
+ s.src_alignment = g_new(int, s.src_num);
for (bs_i = 0; bs_i < s.src_num; bs_i++) {
+ BlockDriverState *src_bs;
s.src[bs_i] = img_open(image_opts, argv[optind + bs_i],
fmt, src_flags, src_writethrough, s.quiet,
force_share);
ret = -1;
goto out;
}
+ src_bs = blk_bs(s.src[bs_i]);
+ s.src_alignment[bs_i] = DIV_ROUND_UP(src_bs->bl.request_alignment,
+ BDRV_SECTOR_SIZE);
+ if (!bdrv_get_info(src_bs, &bdi)) {
+ s.src_alignment[bs_i] = MAX(s.src_alignment[bs_i],
+ bdi.cluster_size / BDRV_SECTOR_SIZE);
+ }
s.total_sectors += s.src_sectors[bs_i];
}
}
}
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s.total_sectors * 512,
- &error_abort);
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
+ s.total_sectors * BDRV_SECTOR_SIZE, &error_abort);
ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL);
if (ret < 0) {
goto out;
* s.target_backing_sectors has to be negative, which it will
* be automatically). The backing file length is used only
* for optimizations, so such a case is not fatal. */
- s.target_backing_sectors = bdrv_nb_sectors(out_bs->backing->bs);
+ s.target_backing_sectors =
+ bdrv_nb_sectors(bdrv_backing_chain_next(out_bs));
} else {
s.target_backing_sectors = -1;
}
s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
}
+ if (rate_limit) {
+ set_rate_limit(s.target, rate_limit);
+ }
+
ret = convert_do_copy(&s);
/* Now copy the bitmaps */
qemu_progress_end();
qemu_opts_del(opts);
qemu_opts_free(create_opts);
- qemu_opts_del(sn_opts);
qobject_unref(open_opts);
blk_unref(s.target);
if (s.src) {
g_free(s.src);
}
g_free(s.src_sectors);
+ g_free(s.src_alignment);
fail_getopt:
+ qemu_opts_del(sn_opts);
g_free(options);
return !!ret;
static void dump_json_image_info_list(ImageInfoList *list)
{
- QString *str;
+ GString *str;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
visit_type_ImageInfoList(v, NULL, &list, &error_abort);
visit_complete(v, &obj);
- str = qobject_to_json_pretty(obj);
+ str = qobject_to_json_pretty(obj, true);
assert(str != NULL);
- printf("%s\n", qstring_get_str(str));
+ printf("%s\n", str->str);
qobject_unref(obj);
visit_free(v);
- qobject_unref(str);
+ g_string_free(str, true);
}
static void dump_json_image_info(ImageInfo *info)
{
- QString *str;
+ GString *str;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
visit_type_ImageInfo(v, NULL, &info, &error_abort);
visit_complete(v, &obj);
- str = qobject_to_json_pretty(obj);
+ str = qobject_to_json_pretty(obj, true);
assert(str != NULL);
- printf("%s\n", qstring_get_str(str));
+ printf("%s\n", str->str);
qobject_unref(obj);
visit_free(v);
- qobject_unref(str);
+ g_string_free(str, true);
}
static void dump_human_image_info_list(ImageInfoList *list)
bool chain, bool force_share)
{
ImageInfoList *head = NULL;
- ImageInfoList **last = &head;
+ ImageInfoList **tail = &head;
GHashTable *filenames;
Error *err = NULL;
BlockBackend *blk;
BlockDriverState *bs;
ImageInfo *info;
- ImageInfoList *elem;
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
error_report("Backing file '%s' creates an infinite loop.",
goto err;
}
- elem = g_new0(ImageInfoList, 1);
- elem->value = info;
- *last = elem;
- last = &elem->next;
+ QAPI_LIST_APPEND(tail, info);
blk_unref(blk);
depth = 0;
for (;;) {
+ bs = bdrv_skip_filters(bs);
ret = bdrv_block_status(bs, offset, bytes, &bytes, &map, &file);
if (ret < 0) {
return ret;
if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) {
break;
}
- bs = backing_bs(bs);
+ bs = bdrv_cow_bs(bs);
if (bs == NULL) {
ret = 0;
break;
uint8_t *buf_old = NULL;
uint8_t *buf_new = NULL;
BlockDriverState *bs = NULL, *prefix_chain_bs = NULL;
+ BlockDriverState *unfiltered_bs;
char *filename;
const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg;
int c, flags, src_flags, ret;
}
bs = blk_bs(blk);
+ unfiltered_bs = bdrv_skip_filters(bs);
+
if (out_basefmt != NULL) {
if (bdrv_find_format(out_basefmt) == NULL) {
error_report("Invalid format name: '%s'", out_basefmt);
/* For safe rebasing we need to compare old and new backing file */
if (!unsafe) {
QDict *options = NULL;
- BlockDriverState *base_bs = backing_bs(bs);
+ BlockDriverState *base_bs = bdrv_cow_bs(unfiltered_bs);
if (base_bs) {
blk_old_backing = blk_new(qemu_get_aio_context(),
n = MIN(IO_BUF_SIZE, size - offset);
/* If the cluster is allocated, we don't need to take action */
- ret = bdrv_is_allocated(bs, offset, n, &n);
+ ret = bdrv_is_allocated(unfiltered_bs, offset, n, &n);
if (ret < 0) {
error_report("error while reading image metadata: %s",
strerror(-ret));
* If cluster wasn't changed since prefix_chain, we don't need
* to take action
*/
- ret = bdrv_is_allocated_above(backing_bs(bs), prefix_chain_bs,
- false, offset, n, &n);
+ ret = bdrv_is_allocated_above(bdrv_cow_bs(unfiltered_bs),
+ prefix_chain_bs, false,
+ offset, n, &n);
if (ret < 0) {
error_report("error while reading image metadata: %s",
strerror(-ret));
* doesn't change when we switch the backing file.
*/
if (out_baseimg && *out_baseimg) {
- ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt, true);
+ ret = bdrv_change_backing_file(unfiltered_bs, out_baseimg, out_basefmt,
+ true);
} else {
- ret = bdrv_change_backing_file(bs, NULL, NULL, false);
+ ret = bdrv_change_backing_file(unfiltered_bs, NULL, NULL, false);
}
if (ret == -ENOSPC) {
}
if (total_size < current_size && !shrink) {
+ error_report("Use the --shrink option to perform a shrink operation.");
warn_report("Shrinking an image will delete all data beyond the "
"shrunken image's end. Before performing such an "
"operation, make sure there is no important data there.");
-
- if (g_strcmp0(bdrv_get_format_name(blk_bs(blk)), "raw") != 0) {
- error_report(
- "Use the --shrink option to perform a shrink operation.");
- ret = -1;
- goto out;
- } else {
- warn_report("Using the --shrink option will suppress this message. "
- "Note that future versions of qemu-img may refuse to "
- "shrink images without this option.");
- }
+ ret = -1;
+ goto out;
}
/*
filename = argv[optind];
bitmap = argv[optind + 1];
- blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR, false, false,
- false);
+ /*
+ * No need to open backing chains; we will be manipulating bitmaps
+ * directly in this image without reference to image contents.
+ */
+ blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR | BDRV_O_NO_BACKING,
+ false, false, false);
if (!blk) {
goto out;
}
bs = blk_bs(blk);
if (src_filename) {
- src = img_open(false, src_filename, src_fmt, 0, false, false, false);
+ src = img_open(false, src_filename, src_fmt, BDRV_O_NO_BACKING,
+ false, false, false);
if (!src) {
goto out;
}
static void dump_json_block_measure_info(BlockMeasureInfo *info)
{
- QString *str;
+ GString *str;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
visit_type_BlockMeasureInfo(v, NULL, &info, &error_abort);
visit_complete(v, &obj);
- str = qobject_to_json_pretty(obj);
+ str = qobject_to_json_pretty(obj, true);
assert(str != NULL);
- printf("%s\n", qstring_get_str(str));
+ printf("%s\n", str->str);
qobject_unref(obj);
visit_free(v);
- qobject_unref(str);
+ g_string_free(str, true);
}
static int img_measure(int argc, char **argv)
const img_cmd_t *cmd;
const char *cmdname;
Error *local_error = NULL;
- char *trace_file = NULL;
int c;
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
signal(SIGPIPE, SIG_IGN);
#endif
+ socket_init();
error_init(argv[0]);
module_call_init(MODULE_INIT_TRACE);
qemu_init_exec_dir(argv[0]);
printf(QEMU_IMG_VERSION);
return 0;
case 'T':
- g_free(trace_file);
- trace_file = trace_opt_parse(optarg);
+ trace_opt_parse(optarg);
break;
}
}
if (!trace_init_backends()) {
exit(1);
}
- trace_init_file(trace_file);
+ trace_init_file();
qemu_set_log(LOG_TRACE);
/* find the command */