[submodule "roms/skiboot"]
path = roms/skiboot
url = git://git.qemu.org/skiboot.git
+[submodule "roms/QemuMacDrivers"]
+ path = roms/QemuMacDrivers
+ url = git://git.qemu.org/QemuMacDrivers.git
- env: CONFIG="--enable-trace-backends=ust"
TEST_CMD=""
compiler: gcc
- - env: CONFIG="--with-coroutine=gthread"
- TEST_CMD=""
- compiler: gcc
- env: CONFIG=""
os: osx
compiler: clang
compiler: none
env:
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
- - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user --with-coroutine=gthread"
+ - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
- TEST_CMD=""
before_script:
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
F: qemu-img*
F: qemu-io*
F: tests/qemu-iotests/
+F: util/qemu-progress.c
T: git git://repo.or.cz/qemu/kevin.git block
Block I/O path
M: Fam Zheng <famz@redhat.com>
L: qemu-block@nongnu.org
S: Supported
-F: async.c
-F: aio-*.c
+F: util/async.c
+F: util/aio-*.c
F: block/io.c
F: migration/block*
F: include/block/aio.h
M: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
F: cpus.c
-F: main-loop.c
-F: qemu-timer.c
+F: util/main-loop.c
+F: util/qemu-timer.c
F: vl.c
Human Monitor (HMP)
F: crypto/
F: include/crypto/
F: tests/test-crypto-*
+F: qemu.sasl
Coroutines
M: Stefan Hajnoczi <stefanha@redhat.com>
Linux user
M: Riku Voipio <riku.voipio@iki.fi>
+R: Laurent Vivier <laurent@vivier.eu>
S: Maintained
F: linux-user/
F: default-configs/*-linux-user.mak
s390-ccw.img \
spapr-rtas.bin slof.bin skiboot.lid \
palcode-clipper \
-u-boot.e500
+u-boot.e500 \
+qemu_vga.ndrv
else
BLOBS=
endif
}
}
+/* Returns whether the image file is opened as read-only. Note that this can
+ * return false and writing to the image file is still not possible because the
+ * image is inactivated. */
bool bdrv_is_read_only(BlockDriverState *bs)
{
return bs->read_only;
}
+/* Returns whether the image file can be written to right now */
+bool bdrv_is_writable(BlockDriverState *bs)
+{
+ return !bdrv_is_read_only(bs) && !(bs->open_flags & BDRV_O_INACTIVE);
+}
+
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
{
/* Do not set read_only if copy_on_read is enabled */
bdrv_drained_end(bs);
}
+static int bdrv_child_cb_inactivate(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ assert(bs->open_flags & BDRV_O_INACTIVE);
+ return 0;
+}
+
/*
* Returns the options and flags that a temporary snapshot should get, based on
* the originally requested flags (the originally requested image will have
* the parent. */
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
/* Inherit the read-only option from the parent if it's not set */
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
.inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .inactivate = bdrv_child_cb_inactivate,
};
/*
.inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .inactivate = bdrv_child_cb_inactivate,
};
static void bdrv_backing_attach(BdrvChild *c)
* which is only applied on the top level (BlockBackend) */
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
/* backing files always opened read-only */
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
.inherit_options = bdrv_backing_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .inactivate = bdrv_child_cb_inactivate,
};
static int bdrv_open_flags(BlockDriverState *bs, int flags)
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
},
+ {
+ .name = BDRV_OPT_FORCE_SHARE,
+ .type = QEMU_OPT_BOOL,
+ .help = "always accept other writers (default: off)",
+ },
{ /* end of list */ }
},
};
drv = bdrv_find_format(driver_name);
assert(drv != NULL);
+ bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false);
+
+ if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) {
+ error_setg(errp,
+ BDRV_OPT_FORCE_SHARE
+ "=on can only be used with read-only images");
+ ret = -EINVAL;
+ goto fail_opts;
+ }
+
if (file != NULL) {
filename = blk_bs(file)->filename;
} else {
static void bdrv_child_abort_perm_update(BdrvChild *c);
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
+static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
+ BdrvChild *c,
+ const BdrvChildRole *role,
+ uint64_t parent_perm, uint64_t parent_shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ if (bs->drv && bs->drv->bdrv_child_perm) {
+ bs->drv->bdrv_child_perm(bs, c, role,
+ parent_perm, parent_shared,
+ nperm, nshared);
+ }
+ if (child_bs && child_bs->force_share) {
+ *nshared = BLK_PERM_ALL;
+ }
+}
+
/*
* Check whether permissions on this node can be changed in a way that
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
/* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
- bdrv_is_read_only(bs))
+ !bdrv_is_writable(bs))
{
error_setg(errp, "Block node is read-only");
return -EPERM;
/* Check all children */
QLIST_FOREACH(c, &bs->children, next) {
uint64_t cur_perm, cur_shared;
- drv->bdrv_child_perm(bs, c, c->role,
- cumulative_perms, cumulative_shared_perms,
- &cur_perm, &cur_shared);
+ bdrv_child_perm(bs, c->bs, c, c->role,
+ cumulative_perms, cumulative_shared_perms,
+ &cur_perm, &cur_shared);
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children,
errp);
if (ret < 0) {
/* Update all children */
QLIST_FOREACH(c, &bs->children, next) {
uint64_t cur_perm, cur_shared;
- drv->bdrv_child_perm(bs, c, c->role,
- cumulative_perms, cumulative_shared_perms,
- &cur_perm, &cur_shared);
+ bdrv_child_perm(bs, c->bs, c, c->role,
+ cumulative_perms, cumulative_shared_perms,
+ &cur_perm, &cur_shared);
bdrv_child_set_perm(c, cur_perm, cur_shared);
}
}
return g_strdup("another user");
}
-static char *bdrv_perm_names(uint64_t perm)
+char *bdrv_perm_names(uint64_t perm)
{
struct perm_name {
uint64_t perm;
bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
/* Format drivers may touch metadata even if the guest doesn't write */
- if (!bdrv_is_read_only(bs)) {
+ if (bdrv_is_writable(bs)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
BLK_PERM_WRITE_UNCHANGED;
}
+ if (bs->open_flags & BDRV_O_INACTIVE) {
+ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
+ }
+
*nperm = perm;
*nshared = shared;
}
assert(parent_bs->drv);
assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
- parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
- perm, shared_perm, &perm, &shared_perm);
+ bdrv_child_perm(parent_bs, child_bs, NULL, child_role,
+ perm, shared_perm, &perm, &shared_perm);
child = bdrv_root_attach_child(child_bs, child_name, child_role,
perm, shared_perm, parent_bs, errp);
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
{
- BdrvChild *child;
+ BdrvChild *child, *parent;
+ uint64_t perm, shared_perm;
Error *local_err = NULL;
int ret;
error_setg_errno(errp, -ret, "Could not refresh total sector count");
return;
}
+
+ /* Update permissions, they may differ for inactive nodes */
+ bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
+ ret = bdrv_check_perm(bs, perm, shared_perm, NULL, &local_err);
+ if (ret < 0) {
+ bs->open_flags |= BDRV_O_INACTIVE;
+ error_propagate(errp, local_err);
+ return;
+ }
+ bdrv_set_perm(bs, perm, shared_perm);
+
+ QLIST_FOREACH(parent, &bs->parents, next_parent) {
+ if (parent->role->activate) {
+ parent->role->activate(parent, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+ }
}
void bdrv_invalidate_cache_all(Error **errp)
static int bdrv_inactivate_recurse(BlockDriverState *bs,
bool setting_flag)
{
- BdrvChild *child;
+ BdrvChild *child, *parent;
int ret;
if (!setting_flag && bs->drv->bdrv_inactivate) {
}
}
+ if (setting_flag) {
+ uint64_t perm, shared_perm;
+
+ bs->open_flags |= BDRV_O_INACTIVE;
+
+ QLIST_FOREACH(parent, &bs->parents, next_parent) {
+ if (parent->role->inactivate) {
+ ret = parent->role->inactivate(parent);
+ if (ret < 0) {
+ bs->open_flags &= ~BDRV_O_INACTIVE;
+ return ret;
+ }
+ }
+ }
+
+ /* Update permissions, they may differ for inactive nodes */
+ bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
+ bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort);
+ bdrv_set_perm(bs, perm, shared_perm);
+ }
+
QLIST_FOREACH(child, &bs->children, next) {
ret = bdrv_inactivate_recurse(child->bs, setting_flag);
if (ret < 0) {
}
}
- if (setting_flag) {
- bs->open_flags |= BDRV_O_INACTIVE;
- }
return 0;
}
/*
* Block protocol for I/O error injection
*
+ * Copyright (C) 2016-2017 Red Hat, Inc.
* Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
typedef struct BDRVBlkdebugState {
int state;
int new_state;
- int align;
+ uint64_t align;
+ uint64_t max_transfer;
+ uint64_t opt_write_zero;
+ uint64_t max_write_zero;
+ uint64_t opt_discard;
+ uint64_t max_discard;
/* For blkdebug_refresh_filename() */
char *config_file;
.type = QEMU_OPT_SIZE,
.help = "Required alignment in bytes",
},
+ {
+ .name = "max-transfer",
+ .type = QEMU_OPT_SIZE,
+ .help = "Maximum transfer size in bytes",
+ },
+ {
+ .name = "opt-write-zero",
+ .type = QEMU_OPT_SIZE,
+ .help = "Optimum write zero alignment in bytes",
+ },
+ {
+ .name = "max-write-zero",
+ .type = QEMU_OPT_SIZE,
+ .help = "Maximum write zero size in bytes",
+ },
+ {
+ .name = "opt-discard",
+ .type = QEMU_OPT_SIZE,
+ .help = "Optimum discard alignment in bytes",
+ },
+ {
+ .name = "max-discard",
+ .type = QEMU_OPT_SIZE,
+ .help = "Maximum discard size in bytes",
+ },
{ /* end of list */ }
},
};
BDRVBlkdebugState *s = bs->opaque;
QemuOpts *opts;
Error *local_err = NULL;
- uint64_t align;
int ret;
+ uint64_t align;
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
goto out;
}
- /* Set request alignment */
- align = qemu_opt_get_size(opts, "align", 0);
- if (align < INT_MAX && is_power_of_2(align)) {
- s->align = align;
- } else if (align) {
- error_setg(errp, "Invalid alignment");
- ret = -EINVAL;
+ bs->supported_write_flags = BDRV_REQ_FUA &
+ bs->file->bs->supported_write_flags;
+ bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
+ bs->file->bs->supported_zero_flags;
+ ret = -EINVAL;
+
+ /* Set alignment overrides */
+ s->align = qemu_opt_get_size(opts, "align", 0);
+ if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
+ error_setg(errp, "Cannot meet constraints with align %" PRIu64,
+ s->align);
goto out;
}
+ align = MAX(s->align, bs->file->bs->bl.request_alignment);
- ret = 0;
- goto out;
+ s->max_transfer = qemu_opt_get_size(opts, "max-transfer", 0);
+ if (s->max_transfer &&
+ (s->max_transfer >= INT_MAX ||
+ !QEMU_IS_ALIGNED(s->max_transfer, align))) {
+ error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
+ s->max_transfer);
+ goto out;
+ }
+
+ s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
+ if (s->opt_write_zero &&
+ (s->opt_write_zero >= INT_MAX ||
+ !QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
+ error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
+ s->opt_write_zero);
+ goto out;
+ }
+
+ s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
+ if (s->max_write_zero &&
+ (s->max_write_zero >= INT_MAX ||
+ !QEMU_IS_ALIGNED(s->max_write_zero,
+ MAX(s->opt_write_zero, align)))) {
+ error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
+ s->max_write_zero);
+ goto out;
+ }
+
+ s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
+ if (s->opt_discard &&
+ (s->opt_discard >= INT_MAX ||
+ !QEMU_IS_ALIGNED(s->opt_discard, align))) {
+ error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
+ s->opt_discard);
+ goto out;
+ }
+
+ s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
+ if (s->max_discard &&
+ (s->max_discard >= INT_MAX ||
+ !QEMU_IS_ALIGNED(s->max_discard,
+ MAX(s->opt_discard, align)))) {
+ error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
+ s->max_discard);
+ goto out;
+ }
+ ret = 0;
out:
if (ret < 0) {
g_free(s->config_file);
return ret;
}
-static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
+static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
{
BDRVBlkdebugState *s = bs->opaque;
- int error = rule->options.inject.error;
- bool immediately = rule->options.inject.immediately;
+ BlkdebugRule *rule = NULL;
+ int error;
+ bool immediately;
+
+ QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
+ uint64_t inject_offset = rule->options.inject.offset;
+
+ if (inject_offset == -1 ||
+ (bytes && inject_offset >= offset &&
+ inject_offset < offset + bytes))
+ {
+ break;
+ }
+ }
+
+ if (!rule || !rule->options.inject.error) {
+ return 0;
+ }
+
+ immediately = rule->options.inject.immediately;
+ error = rule->options.inject.error;
if (rule->options.inject.once) {
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
- BDRVBlkdebugState *s = bs->opaque;
- BlkdebugRule *rule = NULL;
-
- QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
- uint64_t inject_offset = rule->options.inject.offset;
+ int err;
- if (inject_offset == -1 ||
- (inject_offset >= offset && inject_offset < offset + bytes))
- {
- break;
- }
+ /* Sanity check block layer guarantees */
+ assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
+ assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
+ if (bs->bl.max_transfer) {
+ assert(bytes <= bs->bl.max_transfer);
}
- if (rule && rule->options.inject.error) {
- return inject_error(bs, rule);
+ err = rule_check(bs, offset, bytes);
+ if (err) {
+ return err;
}
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
- BDRVBlkdebugState *s = bs->opaque;
- BlkdebugRule *rule = NULL;
+ int err;
- QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
- uint64_t inject_offset = rule->options.inject.offset;
-
- if (inject_offset == -1 ||
- (inject_offset >= offset && inject_offset < offset + bytes))
- {
- break;
- }
+ /* Sanity check block layer guarantees */
+ assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
+ assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
+ if (bs->bl.max_transfer) {
+ assert(bytes <= bs->bl.max_transfer);
}
- if (rule && rule->options.inject.error) {
- return inject_error(bs, rule);
+ err = rule_check(bs, offset, bytes);
+ if (err) {
+ return err;
}
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
static int blkdebug_co_flush(BlockDriverState *bs)
{
- BDRVBlkdebugState *s = bs->opaque;
- BlkdebugRule *rule = NULL;
+ int err = rule_check(bs, 0, 0);
- QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
- if (rule->options.inject.offset == -1) {
- break;
- }
+ if (err) {
+ return err;
}
- if (rule && rule->options.inject.error) {
- return inject_error(bs, rule);
+ return bdrv_co_flush(bs->file->bs);
+}
+
+static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
+ int64_t offset, int count,
+ BdrvRequestFlags flags)
+{
+ uint32_t align = MAX(bs->bl.request_alignment,
+ bs->bl.pwrite_zeroes_alignment);
+ int err;
+
+ /* Only pass through requests that are larger than requested
+ * preferred alignment (so that we test the fallback to writes on
+ * unaligned portions), and check that the block layer never hands
+ * us anything unaligned that crosses an alignment boundary. */
+ if (count < align) {
+ assert(QEMU_IS_ALIGNED(offset, align) ||
+ QEMU_IS_ALIGNED(offset + count, align) ||
+ DIV_ROUND_UP(offset, align) ==
+ DIV_ROUND_UP(offset + count, align));
+ return -ENOTSUP;
+ }
+ assert(QEMU_IS_ALIGNED(offset, align));
+ assert(QEMU_IS_ALIGNED(count, align));
+ if (bs->bl.max_pwrite_zeroes) {
+ assert(count <= bs->bl.max_pwrite_zeroes);
}
- return bdrv_co_flush(bs->file->bs);
+ err = rule_check(bs, offset, count);
+ if (err) {
+ return err;
+ }
+
+ return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
}
+static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count)
+{
+ uint32_t align = bs->bl.pdiscard_alignment;
+ int err;
+
+ /* Only pass through requests that are larger than requested
+ * minimum alignment, and ensure that unaligned requests do not
+ * cross optimum discard boundaries. */
+ if (count < bs->bl.request_alignment) {
+ assert(QEMU_IS_ALIGNED(offset, align) ||
+ QEMU_IS_ALIGNED(offset + count, align) ||
+ DIV_ROUND_UP(offset, align) ==
+ DIV_ROUND_UP(offset + count, align));
+ return -ENOTSUP;
+ }
+ assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
+ assert(QEMU_IS_ALIGNED(count, bs->bl.request_alignment));
+ if (align && count >= align) {
+ assert(QEMU_IS_ALIGNED(offset, align));
+ assert(QEMU_IS_ALIGNED(count, align));
+ }
+ if (bs->bl.max_pdiscard) {
+ assert(count <= bs->bl.max_pdiscard);
+ }
+
+ err = rule_check(bs, offset, count);
+ if (err) {
+ return err;
+ }
+
+ return bdrv_co_pdiscard(bs->file->bs, offset, count);
+}
static void blkdebug_close(BlockDriverState *bs)
{
if (s->align) {
bs->bl.request_alignment = s->align;
}
+ if (s->max_transfer) {
+ bs->bl.max_transfer = s->max_transfer;
+ }
+ if (s->opt_write_zero) {
+ bs->bl.pwrite_zeroes_alignment = s->opt_write_zero;
+ }
+ if (s->max_write_zero) {
+ bs->bl.max_pwrite_zeroes = s->max_write_zero;
+ }
+ if (s->opt_discard) {
+ bs->bl.pdiscard_alignment = s->opt_discard;
+ }
+ if (s->max_discard) {
+ bs->bl.max_pdiscard = s->max_discard;
+ }
}
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
.bdrv_co_preadv = blkdebug_co_preadv,
.bdrv_co_pwritev = blkdebug_co_pwritev,
.bdrv_co_flush_to_disk = blkdebug_co_flush,
+ .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
+ .bdrv_co_pdiscard = blkdebug_co_pdiscard,
.bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
return blk_name(child->opaque);
}
+/*
+ * Notifies the user of the BlockBackend that migration has completed. qdev
+ * devices can tighten their permissions in response (specifically revoke
+ * shared write permissions that we needed for storage migration).
+ *
+ * If an error is returned, the VM cannot be allowed to be resumed.
+ */
+static void blk_root_activate(BdrvChild *child, Error **errp)
+{
+ BlockBackend *blk = child->opaque;
+ Error *local_err = NULL;
+
+ if (!blk->disable_perm) {
+ return;
+ }
+
+ blk->disable_perm = false;
+
+ blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ blk->disable_perm = true;
+ return;
+ }
+}
+
+static int blk_root_inactivate(BdrvChild *child)
+{
+ BlockBackend *blk = child->opaque;
+
+ if (blk->disable_perm) {
+ return 0;
+ }
+
+ /* Only inactivate BlockBackends for guest devices (which are inactive at
+ * this point because the VM is stopped) and unattached monitor-owned
+ * BlockBackends. If there is still any other user like a block job, then
+ * we simply can't inactivate the image. */
+ if (!blk->dev && !blk->name[0]) {
+ return -EPERM;
+ }
+
+ blk->disable_perm = true;
+ if (blk->root) {
+ bdrv_child_try_set_perm(blk->root, 0, BLK_PERM_ALL, &error_abort);
+ }
+
+ return 0;
+}
+
static const BdrvChildRole child_root = {
.inherit_options = blk_root_inherit_options,
.drained_begin = blk_root_drained_begin,
.drained_end = blk_root_drained_end,
+
+ .activate = blk_root_activate,
+ .inactivate = blk_root_inactivate,
};
/*
*shared_perm = blk->shared_perm;
}
-/*
- * Notifies the user of all BlockBackends that migration has completed. qdev
- * devices can tighten their permissions in response (specifically revoke
- * shared write permissions that we needed for storage migration).
- *
- * If an error is returned, the VM cannot be allowed to be resumed.
- */
-void blk_resume_after_migration(Error **errp)
-{
- BlockBackend *blk;
- Error *local_err = NULL;
-
- for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
- if (!blk->disable_perm) {
- continue;
- }
-
- blk->disable_perm = false;
-
- blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- blk->disable_perm = true;
- return;
- }
- }
-}
-
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
{
if (blk->dev) {
static ssize_t block_crypto_read_func(QCryptoBlock *block,
- void *opaque,
size_t offset,
uint8_t *buf,
size_t buflen,
+ void *opaque,
Error **errp)
{
BlockDriverState *bs = opaque;
static ssize_t block_crypto_write_func(QCryptoBlock *block,
- void *opaque,
size_t offset,
const uint8_t *buf,
size_t buflen,
+ void *opaque,
Error **errp)
{
struct BlockCryptoCreateData *data = opaque;
static ssize_t block_crypto_init_func(QCryptoBlock *block,
- void *opaque,
size_t headerlen,
+ void *opaque,
Error **errp)
{
struct BlockCryptoCreateData *data = opaque;
#define MAX_BLOCKSIZE 4096
+/* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes,
+ * leaving a few more bytes for its future use. */
+#define RAW_LOCK_PERM_BASE 100
+#define RAW_LOCK_SHARED_BASE 200
+
typedef struct BDRVRawState {
int fd;
+ int lock_fd;
+ bool use_lock;
int type;
int open_flags;
size_t buf_align;
+ /* The current permissions. */
+ uint64_t perm;
+ uint64_t shared_perm;
+
#ifdef CONFIG_XFS
bool is_xfs:1;
#endif
.type = QEMU_OPT_STRING,
.help = "host AIO implementation (threads, native)",
},
+ {
+ .name = "locking",
+ .type = QEMU_OPT_STRING,
+ .help = "file locking mode (on/off/auto, default: auto)",
+ },
{ /* end of list */ }
},
};
BlockdevAioOptions aio, aio_default;
int fd, ret;
struct stat st;
+ OnOffAuto locking;
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
}
s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE);
+ locking = qapi_enum_parse(OnOffAuto_lookup, qemu_opt_get(opts, "locking"),
+ ON_OFF_AUTO__MAX, ON_OFF_AUTO_AUTO, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+ switch (locking) {
+ case ON_OFF_AUTO_ON:
+ s->use_lock = true;
+#ifndef F_OFD_SETLK
+ fprintf(stderr,
+ "File lock requested but OFD locking syscall is unavailable, "
+ "falling back to POSIX file locks.\n"
+ "Due to the implementation, locks can be lost unexpectedly.\n");
+#endif
+ break;
+ case ON_OFF_AUTO_OFF:
+ s->use_lock = false;
+ break;
+ case ON_OFF_AUTO_AUTO:
+#ifdef F_OFD_SETLK
+ s->use_lock = true;
+#else
+ s->use_lock = false;
+#endif
+ break;
+ default:
+ abort();
+ }
+
s->open_flags = open_flags;
raw_parse_flags(bdrv_flags, &s->open_flags);
}
s->fd = fd;
+ s->lock_fd = -1;
+ if (s->use_lock) {
+ fd = qemu_open(filename, s->open_flags);
+ if (fd < 0) {
+ ret = -errno;
+ error_setg_errno(errp, errno, "Could not open '%s' for locking",
+ filename);
+ qemu_close(s->fd);
+ goto fail;
+ }
+ s->lock_fd = fd;
+ }
+ s->perm = 0;
+ s->shared_perm = BLK_PERM_ALL;
+
#ifdef CONFIG_LINUX_AIO
/* Currently Linux does AIO only for files opened with O_DIRECT */
if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) {
return raw_open_common(bs, options, flags, 0, errp);
}
+typedef enum {
+ RAW_PL_PREPARE,
+ RAW_PL_COMMIT,
+ RAW_PL_ABORT,
+} RawPermLockOp;
+
+#define PERM_FOREACH(i) \
+ for ((i) = 0; (1ULL << (i)) <= BLK_PERM_ALL; i++)
+
+/* Lock bytes indicated by @perm_lock_bits and @shared_perm_lock_bits in the
+ * file; if @unlock == true, also unlock the unneeded bytes.
+ * @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
+ */
+static int raw_apply_lock_bytes(BDRVRawState *s,
+ uint64_t perm_lock_bits,
+ uint64_t shared_perm_lock_bits,
+ bool unlock, Error **errp)
+{
+ int ret;
+ int i;
+
+ PERM_FOREACH(i) {
+ int off = RAW_LOCK_PERM_BASE + i;
+ if (perm_lock_bits & (1ULL << i)) {
+ ret = qemu_lock_fd(s->lock_fd, off, 1, false);
+ if (ret) {
+ error_setg(errp, "Failed to lock byte %d", off);
+ return ret;
+ }
+ } else if (unlock) {
+ ret = qemu_unlock_fd(s->lock_fd, off, 1);
+ if (ret) {
+ error_setg(errp, "Failed to unlock byte %d", off);
+ return ret;
+ }
+ }
+ }
+ PERM_FOREACH(i) {
+ int off = RAW_LOCK_SHARED_BASE + i;
+ if (shared_perm_lock_bits & (1ULL << i)) {
+ ret = qemu_lock_fd(s->lock_fd, off, 1, false);
+ if (ret) {
+ error_setg(errp, "Failed to lock byte %d", off);
+ return ret;
+ }
+ } else if (unlock) {
+ ret = qemu_unlock_fd(s->lock_fd, off, 1);
+ if (ret) {
+ error_setg(errp, "Failed to unlock byte %d", off);
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */
+static int raw_check_lock_bytes(BDRVRawState *s,
+ uint64_t perm, uint64_t shared_perm,
+ Error **errp)
+{
+ int ret;
+ int i;
+
+ PERM_FOREACH(i) {
+ int off = RAW_LOCK_SHARED_BASE + i;
+ uint64_t p = 1ULL << i;
+ if (perm & p) {
+ ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
+ if (ret) {
+ char *perm_name = bdrv_perm_names(p);
+ error_setg(errp,
+ "Failed to get \"%s\" lock",
+ perm_name);
+ g_free(perm_name);
+ error_append_hint(errp,
+ "Is another process using the image?\n");
+ return ret;
+ }
+ }
+ }
+ PERM_FOREACH(i) {
+ int off = RAW_LOCK_PERM_BASE + i;
+ uint64_t p = 1ULL << i;
+ if (!(shared_perm & p)) {
+ ret = qemu_lock_fd_test(s->lock_fd, off, 1, true);
+ if (ret) {
+ char *perm_name = bdrv_perm_names(p);
+ error_setg(errp,
+ "Failed to get shared \"%s\" lock",
+ perm_name);
+ g_free(perm_name);
+ error_append_hint(errp,
+ "Is another process using the image?\n");
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static int raw_handle_perm_lock(BlockDriverState *bs,
+ RawPermLockOp op,
+ uint64_t new_perm, uint64_t new_shared,
+ Error **errp)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret = 0;
+ Error *local_err = NULL;
+
+ if (!s->use_lock) {
+ return 0;
+ }
+
+ if (bdrv_get_flags(bs) & BDRV_O_INACTIVE) {
+ return 0;
+ }
+
+ assert(s->lock_fd > 0);
+
+ switch (op) {
+ case RAW_PL_PREPARE:
+ ret = raw_apply_lock_bytes(s, s->perm | new_perm,
+ ~s->shared_perm | ~new_shared,
+ false, errp);
+ if (!ret) {
+ ret = raw_check_lock_bytes(s, new_perm, new_shared, errp);
+ if (!ret) {
+ return 0;
+ }
+ }
+ op = RAW_PL_ABORT;
+ /* fall through to unlock bytes. */
+ case RAW_PL_ABORT:
+ raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err);
+ if (local_err) {
+ /* Theoretically the above call only unlocks bytes and it cannot
+ * fail. Something weird happened, report it.
+ */
+ error_report_err(local_err);
+ }
+ break;
+ case RAW_PL_COMMIT:
+ raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err);
+ if (local_err) {
+ /* Theoretically the above call only unlocks bytes and it cannot
+ * fail. Something weird happened, report it.
+ */
+ error_report_err(local_err);
+ }
+ break;
+ }
+ return ret;
+}
+
static int raw_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
qemu_close(s->fd);
s->fd = -1;
}
+ if (s->lock_fd >= 0) {
+ qemu_close(s->lock_fd);
+ s->lock_fd = -1;
+ }
}
static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
}
};
+static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
+ Error **errp)
+{
+ return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
+}
+
+static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
+{
+ BDRVRawState *s = bs->opaque;
+ raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
+ s->perm = perm;
+ s->shared_perm = shared;
+}
+
+static void raw_abort_perm_update(BlockDriverState *bs)
+{
+ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
+}
+
BlockDriver bdrv_file = {
.format_name = "file",
.protocol_name = "file",
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
-
+ .bdrv_check_perm = raw_check_perm,
+ .bdrv_set_perm = raw_set_perm,
+ .bdrv_abort_perm_update = raw_abort_perm_update,
.create_opts = &raw_create_opts,
};
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_check_perm = raw_check_perm,
+ .bdrv_set_perm = raw_set_perm,
+ .bdrv_abort_perm_update = raw_abort_perm_update,
.bdrv_probe_blocksizes = hdev_probe_blocksizes,
.bdrv_probe_geometry = hdev_probe_geometry,
goto fail;
}
+ if (qdict_get_try_bool(options, "locking", false)) {
+ error_setg(errp, "locking=on is not supported on Windows");
+ goto fail;
+ }
+
filename = qemu_opt_get(opts, "filename");
use_aio = get_aio_option(opts, flags, &local_err);
if (ret & BDRV_BLOCK_RAW) {
assert(ret & BDRV_BLOCK_OFFSET_VALID);
- ret = bdrv_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
- *pnum, pnum, file);
+ ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
+ *pnum, pnum, file);
goto out;
}
uint64_t *l2_table, uint64_t stop_flags)
{
int i;
+ QCow2ClusterType first_cluster_type;
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
uint64_t first_entry = be64_to_cpu(l2_table[0]);
uint64_t offset = first_entry & mask;
- if (!offset)
+ if (!offset) {
return 0;
+ }
- assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
+ /* must be allocated */
+ first_cluster_type = qcow2_get_cluster_type(first_entry);
+ assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
+ first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
for (i = 0; i < nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
return i;
}
-static int count_contiguous_clusters_by_type(int nb_clusters,
- uint64_t *l2_table,
- int wanted_type)
+/*
+ * Checks how many consecutive unallocated clusters in a given L2
+ * table have the same cluster type.
+ */
+static int count_contiguous_clusters_unallocated(int nb_clusters,
+ uint64_t *l2_table,
+ QCow2ClusterType wanted_type)
{
int i;
+ assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN ||
+ wanted_type == QCOW2_CLUSTER_UNALLOCATED);
for (i = 0; i < nb_clusters; i++) {
- int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
+ uint64_t entry = be64_to_cpu(l2_table[i]);
+ QCow2ClusterType type = qcow2_get_cluster_type(entry);
if (type != wanted_type) {
break;
int l1_bits, c;
unsigned int offset_in_cluster;
uint64_t bytes_available, bytes_needed, nb_clusters;
+ QCow2ClusterType type;
int ret;
offset_in_cluster = offset_into_cluster(s, offset);
l1_index = offset >> l1_bits;
if (l1_index >= s->l1_size) {
- ret = QCOW2_CLUSTER_UNALLOCATED;
+ type = QCOW2_CLUSTER_UNALLOCATED;
goto out;
}
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
if (!l2_offset) {
- ret = QCOW2_CLUSTER_UNALLOCATED;
+ type = QCOW2_CLUSTER_UNALLOCATED;
goto out;
}
* true */
assert(nb_clusters <= INT_MAX);
- ret = qcow2_get_cluster_type(*cluster_offset);
- switch (ret) {
+ type = qcow2_get_cluster_type(*cluster_offset);
+ if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
+ type == QCOW2_CLUSTER_ZERO_ALLOC)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
+ " in pre-v3 image (L2 offset: %#" PRIx64
+ ", L2 index: %#x)", l2_offset, l2_index);
+ ret = -EIO;
+ goto fail;
+ }
+ switch (type) {
case QCOW2_CLUSTER_COMPRESSED:
/* Compressed clusters can only be processed one by one */
c = 1;
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
break;
- case QCOW2_CLUSTER_ZERO:
- if (s->qcow_version < 3) {
- qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
- " in pre-v3 image (L2 offset: %#" PRIx64
- ", L2 index: %#x)", l2_offset, l2_index);
- ret = -EIO;
- goto fail;
- }
- c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
- QCOW2_CLUSTER_ZERO);
- *cluster_offset = 0;
- break;
+ case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_UNALLOCATED:
/* how many empty clusters ? */
- c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
- QCOW2_CLUSTER_UNALLOCATED);
+ c = count_contiguous_clusters_unallocated(nb_clusters,
+ &l2_table[l2_index], type);
*cluster_offset = 0;
break;
+ case QCOW2_CLUSTER_ZERO_ALLOC:
case QCOW2_CLUSTER_NORMAL:
/* how many allocated clusters ? */
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
- &l2_table[l2_index], QCOW_OFLAG_ZERO);
+ &l2_table[l2_index], QCOW_OFLAG_ZERO);
*cluster_offset &= L2E_OFFSET_MASK;
if (offset_into_cluster(s, *cluster_offset)) {
- qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
+ qcow2_signal_corruption(bs, true, -1, -1,
+ "Cluster allocation offset %#"
PRIx64 " unaligned (L2 offset: %#" PRIx64
", L2 index: %#x)", *cluster_offset,
l2_offset, l2_index);
assert(bytes_available - offset_in_cluster <= UINT_MAX);
*bytes = bytes_available - offset_in_cluster;
- return ret;
+ return type;
fail:
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
* Don't discard clusters that reach a refcount of 0 (e.g. compressed
* clusters), the next write will reuse them anyway.
*/
- if (j != 0) {
+ if (!m->keep_old_clusters && j != 0) {
for (i = 0; i < j; i++) {
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
QCOW2_DISCARD_NEVER);
for (i = 0; i < nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]);
- int cluster_type = qcow2_get_cluster_type(l2_entry);
+ QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
switch(cluster_type) {
case QCOW2_CLUSTER_NORMAL:
break;
case QCOW2_CLUSTER_UNALLOCATED:
case QCOW2_CLUSTER_COMPRESSED:
- case QCOW2_CLUSTER_ZERO:
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ case QCOW2_CLUSTER_ZERO_ALLOC:
break;
default:
abort();
uint64_t entry;
uint64_t nb_clusters;
int ret;
+ bool keep_old_clusters = false;
- uint64_t alloc_cluster_offset;
+ uint64_t alloc_cluster_offset = 0;
trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset,
*bytes);
* wrong with our code. */
assert(nb_clusters > 0);
- qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
+ if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO_ALLOC &&
+ (entry & QCOW_OFLAG_COPIED) &&
+ (!*host_offset ||
+ start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
+ {
+ /* Try to reuse preallocated zero clusters; contiguous normal clusters
+ * would be fine, too, but count_cow_clusters() above has limited
+ * nb_clusters already to a range of COW clusters */
+ int preallocated_nb_clusters =
+ count_contiguous_clusters(nb_clusters, s->cluster_size,
+ &l2_table[l2_index], QCOW_OFLAG_COPIED);
+ assert(preallocated_nb_clusters > 0);
- /* Allocate, if necessary at a given offset in the image file */
- alloc_cluster_offset = start_of_cluster(s, *host_offset);
- ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
- &nb_clusters);
- if (ret < 0) {
- goto fail;
- }
+ nb_clusters = preallocated_nb_clusters;
+ alloc_cluster_offset = entry & L2E_OFFSET_MASK;
- /* Can't extend contiguous allocation */
- if (nb_clusters == 0) {
- *bytes = 0;
- return 0;
+ /* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2()
+ * should not free them. */
+ keep_old_clusters = true;
}
- /* !*host_offset would overwrite the image header and is reserved for "no
- * host offset preferred". If 0 was a valid host offset, it'd trigger the
- * following overlap check; do that now to avoid having an invalid value in
- * *host_offset. */
+ qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
+
if (!alloc_cluster_offset) {
- ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
- nb_clusters * s->cluster_size);
- assert(ret < 0);
- goto fail;
+ /* Allocate, if necessary at a given offset in the image file */
+ alloc_cluster_offset = start_of_cluster(s, *host_offset);
+ ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset,
+ &nb_clusters);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* Can't extend contiguous allocation */
+ if (nb_clusters == 0) {
+ *bytes = 0;
+ return 0;
+ }
+
+ /* !*host_offset would overwrite the image header and is reserved for
+ * "no host offset preferred". If 0 was a valid host offset, it'd
+ * trigger the following overlap check; do that now to avoid having an
+ * invalid value in *host_offset. */
+ if (!alloc_cluster_offset) {
+ ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset,
+ nb_clusters * s->cluster_size);
+ assert(ret < 0);
+ goto fail;
+ }
}
/*
.offset = start_of_cluster(s, guest_offset),
.nb_clusters = nb_clusters,
+ .keep_old_clusters = keep_old_clusters,
+
.cow_start = {
.offset = 0,
.nb_bytes = offset_into_cluster(s, guest_offset),
* but rather fall through to the backing file.
*/
switch (qcow2_get_cluster_type(old_l2_entry)) {
- case QCOW2_CLUSTER_UNALLOCATED:
- if (full_discard || !bs->backing) {
- continue;
- }
- break;
+ case QCOW2_CLUSTER_UNALLOCATED:
+ if (full_discard || !bs->backing) {
+ continue;
+ }
+ break;
- case QCOW2_CLUSTER_ZERO:
- if (!full_discard) {
- continue;
- }
- break;
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ if (!full_discard) {
+ continue;
+ }
+ break;
- case QCOW2_CLUSTER_NORMAL:
- case QCOW2_CLUSTER_COMPRESSED:
- break;
+ case QCOW2_CLUSTER_ZERO_ALLOC:
+ case QCOW2_CLUSTER_NORMAL:
+ case QCOW2_CLUSTER_COMPRESSED:
+ break;
- default:
- abort();
+ default:
+ abort();
}
/* First remove L2 entries */
return nb_clusters;
}
-int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
- int nb_sectors, enum qcow2_discard_type type, bool full_discard)
+int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, enum qcow2_discard_type type,
+ bool full_discard)
{
BDRVQcow2State *s = bs->opaque;
- uint64_t end_offset;
+ uint64_t end_offset = offset + bytes;
uint64_t nb_clusters;
+ int64_t cleared;
int ret;
- end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
-
- /* The caller must cluster-align start; round end down except at EOF */
+ /* Caller must pass aligned values, except at image end */
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
- if (end_offset != bs->total_sectors * BDRV_SECTOR_SIZE) {
- end_offset = start_of_cluster(s, end_offset);
- }
+ assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
+ end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
- nb_clusters = size_to_clusters(s, end_offset - offset);
+ nb_clusters = size_to_clusters(s, bytes);
s->cache_discards = true;
/* Each L2 table is handled by its own loop iteration */
while (nb_clusters > 0) {
- ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard);
- if (ret < 0) {
+ cleared = discard_single_l2(bs, offset, nb_clusters, type,
+ full_discard);
+ if (cleared < 0) {
+ ret = cleared;
goto fail;
}
- nb_clusters -= ret;
- offset += (ret * s->cluster_size);
+ nb_clusters -= cleared;
+ offset += (cleared * s->cluster_size);
}
ret = 0;
int l2_index;
int ret;
int i;
+ bool unmap = !!(flags & BDRV_REQ_MAY_UNMAP);
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
if (ret < 0) {
for (i = 0; i < nb_clusters; i++) {
uint64_t old_offset;
+ QCow2ClusterType cluster_type;
old_offset = be64_to_cpu(l2_table[l2_index + i]);
- /* Update L2 entries */
+ /*
+ * Minimize L2 changes if the cluster already reads back as
+ * zeroes with correct allocation.
+ */
+ cluster_type = qcow2_get_cluster_type(old_offset);
+ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN ||
+ (cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) {
+ continue;
+ }
+
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
- if (old_offset & QCOW_OFLAG_COMPRESSED || flags & BDRV_REQ_MAY_UNMAP) {
+ if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) {
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
} else {
return nb_clusters;
}
-int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
- int flags)
+int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, int flags)
{
BDRVQcow2State *s = bs->opaque;
+ uint64_t end_offset = offset + bytes;
uint64_t nb_clusters;
+ int64_t cleared;
int ret;
+ /* Caller must pass aligned values, except at image end */
+ assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
+ assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
+ end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
+
/* The zero flag is only supported by version 3 and newer */
if (s->qcow_version < 3) {
return -ENOTSUP;
}
/* Each L2 table is handled by its own loop iteration */
- nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);
+ nb_clusters = size_to_clusters(s, bytes);
s->cache_discards = true;
while (nb_clusters > 0) {
- ret = zero_single_l2(bs, offset, nb_clusters, flags);
- if (ret < 0) {
+ cleared = zero_single_l2(bs, offset, nb_clusters, flags);
+ if (cleared < 0) {
+ ret = cleared;
goto fail;
}
- nb_clusters -= ret;
- offset += (ret * s->cluster_size);
+ nb_clusters -= cleared;
+ offset += (cleared * s->cluster_size);
}
ret = 0;
for (j = 0; j < s->l2_size; j++) {
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
int64_t offset = l2_entry & L2E_OFFSET_MASK;
- int cluster_type = qcow2_get_cluster_type(l2_entry);
- bool preallocated = offset != 0;
+ QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
- if (cluster_type != QCOW2_CLUSTER_ZERO) {
+ if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN &&
+ cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) {
continue;
}
- if (!preallocated) {
+ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
if (!bs->backing) {
/* not backed; therefore we can simply deallocate the
* cluster */
"%#" PRIx64 " unaligned (L2 offset: %#"
PRIx64 ", L2 index: %#x)", offset,
l2_offset, j);
- if (!preallocated) {
+ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
qcow2_free_clusters(bs, offset, s->cluster_size,
QCOW2_DISCARD_ALWAYS);
}
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
if (ret < 0) {
- if (!preallocated) {
+ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
qcow2_free_clusters(bs, offset, s->cluster_size,
QCOW2_DISCARD_ALWAYS);
}
ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0);
if (ret < 0) {
- if (!preallocated) {
+ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) {
qcow2_free_clusters(bs, offset, s->cluster_size,
QCOW2_DISCARD_ALWAYS);
}
}
break;
case QCOW2_CLUSTER_NORMAL:
- case QCOW2_CLUSTER_ZERO:
- if (l2_entry & L2E_OFFSET_MASK) {
- if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
- qcow2_signal_corruption(bs, false, -1, -1,
- "Cannot free unaligned cluster %#llx",
- l2_entry & L2E_OFFSET_MASK);
- } else {
- qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
- nb_clusters << s->cluster_bits, type);
- }
+ case QCOW2_CLUSTER_ZERO_ALLOC:
+ if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
+ qcow2_signal_corruption(bs, false, -1, -1,
+ "Cannot free unaligned cluster %#llx",
+ l2_entry & L2E_OFFSET_MASK);
+ } else {
+ qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
+ nb_clusters << s->cluster_bits, type);
}
break;
+ case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_UNALLOCATED:
break;
default:
int64_t l1_table_offset, int l1_size, int addend)
{
BDRVQcow2State *s = bs->opaque;
- uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
+ uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount;
bool l1_allocated = false;
- int64_t old_offset, old_l2_offset;
+ int64_t old_entry, old_l2_offset;
int i, j, l1_modified = 0, nb_csectors;
int ret;
goto fail;
}
- for(i = 0;i < l1_size; i++)
+ for (i = 0; i < l1_size; i++) {
be64_to_cpus(&l1_table[i]);
+ }
} else {
assert(l1_size == s->l1_size);
l1_table = s->l1_table;
l1_allocated = false;
}
- for(i = 0; i < l1_size; i++) {
+ for (i = 0; i < l1_size; i++) {
l2_offset = l1_table[i];
if (l2_offset) {
old_l2_offset = l2_offset;
goto fail;
}
- for(j = 0; j < s->l2_size; j++) {
+ for (j = 0; j < s->l2_size; j++) {
uint64_t cluster_index;
-
- offset = be64_to_cpu(l2_table[j]);
- old_offset = offset;
- offset &= ~QCOW_OFLAG_COPIED;
-
- switch (qcow2_get_cluster_type(offset)) {
- case QCOW2_CLUSTER_COMPRESSED:
- nb_csectors = ((offset >> s->csize_shift) &
- s->csize_mask) + 1;
- if (addend != 0) {
- ret = update_refcount(bs,
- (offset & s->cluster_offset_mask) & ~511,
+ uint64_t offset;
+
+ entry = be64_to_cpu(l2_table[j]);
+ old_entry = entry;
+ entry &= ~QCOW_OFLAG_COPIED;
+ offset = entry & L2E_OFFSET_MASK;
+
+ switch (qcow2_get_cluster_type(entry)) {
+ case QCOW2_CLUSTER_COMPRESSED:
+ nb_csectors = ((entry >> s->csize_shift) &
+ s->csize_mask) + 1;
+ if (addend != 0) {
+ ret = update_refcount(bs,
+ (entry & s->cluster_offset_mask) & ~511,
nb_csectors * 512, abs(addend), addend < 0,
QCOW2_DISCARD_SNAPSHOT);
- if (ret < 0) {
- goto fail;
- }
- }
- /* compressed clusters are never modified */
- refcount = 2;
- break;
-
- case QCOW2_CLUSTER_NORMAL:
- case QCOW2_CLUSTER_ZERO:
- if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
- qcow2_signal_corruption(bs, true, -1, -1, "Data "
- "cluster offset %#llx "
- "unaligned (L2 offset: %#"
- PRIx64 ", L2 index: %#x)",
- offset & L2E_OFFSET_MASK,
- l2_offset, j);
- ret = -EIO;
+ if (ret < 0) {
goto fail;
}
+ }
+ /* compressed clusters are never modified */
+ refcount = 2;
+ break;
+
+ case QCOW2_CLUSTER_NORMAL:
+ case QCOW2_CLUSTER_ZERO_ALLOC:
+ if (offset_into_cluster(s, offset)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Cluster "
+ "allocation offset %#" PRIx64
+ " unaligned (L2 offset: %#"
+ PRIx64 ", L2 index: %#x)",
+ offset, l2_offset, j);
+ ret = -EIO;
+ goto fail;
+ }
- cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
- if (!cluster_index) {
- /* unallocated */
- refcount = 0;
- break;
- }
- if (addend != 0) {
- ret = qcow2_update_cluster_refcount(bs,
+ cluster_index = offset >> s->cluster_bits;
+ assert(cluster_index);
+ if (addend != 0) {
+ ret = qcow2_update_cluster_refcount(bs,
cluster_index, abs(addend), addend < 0,
QCOW2_DISCARD_SNAPSHOT);
- if (ret < 0) {
- goto fail;
- }
- }
-
- ret = qcow2_get_refcount(bs, cluster_index, &refcount);
if (ret < 0) {
goto fail;
}
- break;
+ }
- case QCOW2_CLUSTER_UNALLOCATED:
- refcount = 0;
- break;
+ ret = qcow2_get_refcount(bs, cluster_index, &refcount);
+ if (ret < 0) {
+ goto fail;
+ }
+ break;
+
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ case QCOW2_CLUSTER_UNALLOCATED:
+ refcount = 0;
+ break;
- default:
- abort();
+ default:
+ abort();
}
if (refcount == 1) {
- offset |= QCOW_OFLAG_COPIED;
+ entry |= QCOW_OFLAG_COPIED;
}
- if (offset != old_offset) {
+ if (entry != old_entry) {
if (addend > 0) {
qcow2_cache_set_dependency(bs, s->l2_table_cache,
s->refcount_block_cache);
}
- l2_table[j] = cpu_to_be64(offset);
+ l2_table[j] = cpu_to_be64(entry);
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,
l2_table);
}
}
break;
- case QCOW2_CLUSTER_ZERO:
- if ((l2_entry & L2E_OFFSET_MASK) == 0) {
- break;
- }
- /* fall through */
-
+ case QCOW2_CLUSTER_ZERO_ALLOC:
case QCOW2_CLUSTER_NORMAL:
{
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
break;
}
+ case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_UNALLOCATED:
break;
for (j = 0; j < s->l2_size; j++) {
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
- int cluster_type = qcow2_get_cluster_type(l2_entry);
+ QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry);
- if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
- ((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
+ if (cluster_type == QCOW2_CLUSTER_NORMAL ||
+ cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) {
ret = qcow2_get_refcount(bs,
data_offset >> s->cluster_bits,
&refcount);
/* The VM state isn't needed any more in the active L1 table; in fact, it
* hurts by causing expensive COW for the next snapshot. */
- qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
- align_offset(sn->vm_state_size, s->cluster_size)
- >> BDRV_SECTOR_BITS,
- QCOW2_DISCARD_NEVER, false);
+ qcow2_cluster_discard(bs, qcow2_vm_state_offset(s),
+ align_offset(sn->vm_state_size, s->cluster_size),
+ QCOW2_DISCARD_NEVER, false);
#ifdef DEBUG_ALLOC
{
*file = bs->file->bs;
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
}
- if (ret == QCOW2_CLUSTER_ZERO) {
+ if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
status |= BDRV_BLOCK_ZERO;
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
status |= BDRV_BLOCK_DATA;
}
break;
- case QCOW2_CLUSTER_ZERO:
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ case QCOW2_CLUSTER_ZERO_ALLOC:
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
break;
* too, as long as the bulk is allocated here). Therefore, using
* floating point arithmetic is fine. */
int64_t meta_size = 0;
- uint64_t nreftablee, nrefblocke, nl1e, nl2e;
+ uint64_t nreftablee, nrefblocke, nl1e, nl2e, refblock_count;
int64_t aligned_total_size = align_offset(total_size, cluster_size);
int refblock_bits, refblock_size;
/* refcount entry size in bytes */
nrefblocke = (aligned_total_size + meta_size + cluster_size)
/ (cluster_size - rces - rces * sizeof(uint64_t)
/ cluster_size);
- meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size;
+ refblock_count = DIV_ROUND_UP(nrefblocke, refblock_size);
+ meta_size += refblock_count * cluster_size;
/* total size of refcount tables */
- nreftablee = nrefblocke / refblock_size;
- nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t));
+ nreftablee = align_offset(refblock_count,
+ cluster_size / sizeof(uint64_t));
meta_size += nreftablee * sizeof(uint64_t);
qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
BlockDriverState *file;
int64_t res;
+ if (start + count > bs->total_sectors) {
+ count = bs->total_sectors - start;
+ }
+
if (!count) {
return true;
}
uint32_t tail = (offset + count) % s->cluster_size;
trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, count);
+ if (offset + count == bs->total_sectors * BDRV_SECTOR_SIZE) {
+ tail = 0;
+ }
if (head || tail) {
int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS;
count = s->cluster_size;
nr = s->cluster_size;
ret = qcow2_get_cluster_offset(bs, offset, &nr, &off);
- if (ret != QCOW2_CLUSTER_UNALLOCATED && ret != QCOW2_CLUSTER_ZERO) {
+ if (ret != QCOW2_CLUSTER_UNALLOCATED &&
+ ret != QCOW2_CLUSTER_ZERO_PLAIN &&
+ ret != QCOW2_CLUSTER_ZERO_ALLOC) {
qemu_co_mutex_unlock(&s->lock);
return -ENOTSUP;
}
trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count);
/* Whatever is left can use real zero clusters */
- ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS, flags);
+ ret = qcow2_cluster_zeroize(bs, offset, count, flags);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
qemu_co_mutex_lock(&s->lock);
- ret = qcow2_discard_clusters(bs, offset, count >> BDRV_SECTOR_BITS,
- QCOW2_DISCARD_REQUEST, false);
+ ret = qcow2_cluster_discard(bs, offset, count, QCOW2_DISCARD_REQUEST,
+ false);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
static int qcow2_make_empty(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
- uint64_t start_sector;
- int sector_step = (QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size) /
- BDRV_SECTOR_SIZE);
+ uint64_t offset, end_offset;
+ int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size);
int l1_clusters, ret = 0;
l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
/* This fallback code simply discards every active cluster; this is slow,
* but works in all cases */
- for (start_sector = 0; start_sector < bs->total_sectors;
- start_sector += sector_step)
- {
+ end_offset = bs->total_sectors * BDRV_SECTOR_SIZE;
+ for (offset = 0; offset < end_offset; offset += step) {
/* As this function is generally used after committing an external
* snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the
* default action for this kind of discard is to pass the discard,
* which will ideally result in an actually smaller image file, as
* is probably desired. */
- ret = qcow2_discard_clusters(bs, start_sector * BDRV_SECTOR_SIZE,
- MIN(sector_step,
- bs->total_sectors - start_sector),
- QCOW2_DISCARD_SNAPSHOT, true);
+ ret = qcow2_cluster_discard(bs, offset, MIN(step, end_offset - offset),
+ QCOW2_DISCARD_SNAPSHOT, true);
if (ret < 0) {
break;
}
/** Number of newly allocated clusters */
int nb_clusters;
+ /** Do not free the old clusters */
+ bool keep_old_clusters;
+
/**
* Requests that overlap with this allocation and wait to be restarted
* when the allocating request has completed.
QLIST_ENTRY(QCowL2Meta) next_in_flight;
} QCowL2Meta;
-enum {
+typedef enum QCow2ClusterType {
QCOW2_CLUSTER_UNALLOCATED,
+ QCOW2_CLUSTER_ZERO_PLAIN,
+ QCOW2_CLUSTER_ZERO_ALLOC,
QCOW2_CLUSTER_NORMAL,
QCOW2_CLUSTER_COMPRESSED,
- QCOW2_CLUSTER_ZERO
-};
+} QCow2ClusterType;
typedef enum QCow2MetadataOverlap {
QCOW2_OL_MAIN_HEADER_BITNR = 0,
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
}
-static inline int qcow2_get_cluster_type(uint64_t l2_entry)
+static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
{
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
return QCOW2_CLUSTER_COMPRESSED;
} else if (l2_entry & QCOW_OFLAG_ZERO) {
- return QCOW2_CLUSTER_ZERO;
+ if (l2_entry & L2E_OFFSET_MASK) {
+ return QCOW2_CLUSTER_ZERO_ALLOC;
+ }
+ return QCOW2_CLUSTER_ZERO_PLAIN;
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
return QCOW2_CLUSTER_UNALLOCATED;
} else {
int compressed_size);
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
-int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
- int nb_sectors, enum qcow2_discard_type type, bool full_discard);
-int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
- int flags);
+int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, enum qcow2_discard_type type,
+ bool full_discard);
+int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, int flags);
int qcow2_expand_zero_clusters(BlockDriverState *bs,
BlockDriverAmendStatusCB *status_cb,
#include "qapi/error.h"
#include "replication.h"
+typedef enum {
+ BLOCK_REPLICATION_NONE, /* block replication is not started */
+ BLOCK_REPLICATION_RUNNING, /* block replication is running */
+ BLOCK_REPLICATION_FAILOVER, /* failover is running in background */
+ BLOCK_REPLICATION_FAILOVER_FAILED, /* failover failed */
+ BLOCK_REPLICATION_DONE, /* block replication is done */
+} ReplicationStage;
+
typedef struct BDRVReplicationState {
ReplicationMode mode;
- int replication_state;
+ ReplicationStage stage;
BdrvChild *active_disk;
BdrvChild *hidden_disk;
BdrvChild *secondary_disk;
int error;
} BDRVReplicationState;
-enum {
- BLOCK_REPLICATION_NONE, /* block replication is not started */
- BLOCK_REPLICATION_RUNNING, /* block replication is running */
- BLOCK_REPLICATION_FAILOVER, /* failover is running in background */
- BLOCK_REPLICATION_FAILOVER_FAILED, /* failover failed */
- BLOCK_REPLICATION_DONE, /* block replication is done */
-};
-
static void replication_start(ReplicationState *rs, ReplicationMode mode,
Error **errp);
static void replication_do_checkpoint(ReplicationState *rs, Error **errp);
{
BDRVReplicationState *s = bs->opaque;
- if (s->replication_state == BLOCK_REPLICATION_RUNNING) {
+ if (s->stage == BLOCK_REPLICATION_RUNNING) {
replication_stop(s->rs, false, NULL);
}
- if (s->replication_state == BLOCK_REPLICATION_FAILOVER) {
+ if (s->stage == BLOCK_REPLICATION_FAILOVER) {
block_job_cancel_sync(s->active_disk->bs->job);
}
static int replication_get_io_status(BDRVReplicationState *s)
{
- switch (s->replication_state) {
+ switch (s->stage) {
case BLOCK_REPLICATION_NONE:
return -EIO;
case BLOCK_REPLICATION_RUNNING:
BlockDriverState *bs = opaque;
BDRVReplicationState *s = bs->opaque;
- if (s->replication_state != BLOCK_REPLICATION_FAILOVER) {
+ if (s->stage != BLOCK_REPLICATION_FAILOVER) {
/* The backup job is cancelled unexpectedly */
s->error = -EIO;
}
aio_context_acquire(aio_context);
s = bs->opaque;
- if (s->replication_state != BLOCK_REPLICATION_NONE) {
+ if (s->stage != BLOCK_REPLICATION_NONE) {
error_setg(errp, "Block replication is running or done");
aio_context_release(aio_context);
return;
abort();
}
- s->replication_state = BLOCK_REPLICATION_RUNNING;
+ s->stage = BLOCK_REPLICATION_RUNNING;
if (s->mode == REPLICATION_MODE_SECONDARY) {
secondary_do_checkpoint(s, errp);
aio_context_acquire(aio_context);
s = bs->opaque;
- if (s->replication_state != BLOCK_REPLICATION_RUNNING) {
+ if (s->stage != BLOCK_REPLICATION_RUNNING) {
error_setg(errp, "Block replication is not running");
aio_context_release(aio_context);
return;
BDRVReplicationState *s = bs->opaque;
if (ret == 0) {
- s->replication_state = BLOCK_REPLICATION_DONE;
+ s->stage = BLOCK_REPLICATION_DONE;
/* refresh top bs's filename */
bdrv_refresh_filename(bs);
s->hidden_disk = NULL;
s->error = 0;
} else {
- s->replication_state = BLOCK_REPLICATION_FAILOVER_FAILED;
+ s->stage = BLOCK_REPLICATION_FAILOVER_FAILED;
s->error = -EIO;
}
}
aio_context_acquire(aio_context);
s = bs->opaque;
- if (s->replication_state != BLOCK_REPLICATION_RUNNING) {
+ if (s->stage != BLOCK_REPLICATION_RUNNING) {
error_setg(errp, "Block replication is not running");
aio_context_release(aio_context);
return;
switch (s->mode) {
case REPLICATION_MODE_PRIMARY:
- s->replication_state = BLOCK_REPLICATION_DONE;
+ s->stage = BLOCK_REPLICATION_DONE;
s->error = 0;
break;
case REPLICATION_MODE_SECONDARY:
if (!failover) {
secondary_do_checkpoint(s, errp);
- s->replication_state = BLOCK_REPLICATION_DONE;
+ s->stage = BLOCK_REPLICATION_DONE;
aio_context_release(aio_context);
return;
}
- s->replication_state = BLOCK_REPLICATION_FAILOVER;
+ s->stage = BLOCK_REPLICATION_FAILOVER;
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
NULL, replication_done, bs, true, errp);
goto out;
}
- /* complete all in-flight operations before resizing the device */
- bdrv_drain_all();
-
+ bdrv_drained_begin(bs);
ret = blk_truncate(blk, size, errp);
+ bdrv_drained_end(bs);
out:
blk_unref(blk);
Error *local_err = NULL;
int flags;
int64_t size;
+ bool set_backing_hd = false;
if (!backup->has_speed) {
backup->speed = 0;
}
if (backup->sync == MIRROR_SYNC_MODE_NONE) {
source = bs;
+ flags |= BDRV_O_NO_BACKING;
+ set_backing_hd = true;
}
size = bdrv_getlength(bs);
}
if (backup->format) {
- options = qdict_new();
+ if (!options) {
+ options = qdict_new();
+ }
qdict_put_str(options, "driver", backup->format);
}
bdrv_set_aio_context(target_bs, aio_context);
+ if (set_backing_hd) {
+ bdrv_set_backing_hd(target_bs, source, &local_err);
+ if (local_err) {
+ bdrv_unref(target_bs);
+ goto out;
+ }
+ }
+
if (backup->has_bitmap) {
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
if (!bmap) {
qemu_init_cpu_list();
module_call_init(MODULE_INIT_QOM);
- if ((envlist = envlist_create()) == NULL) {
- (void) fprintf(stderr, "Unable to allocate envlist\n");
- exit(1);
- }
+ envlist = envlist_create();
/* add current environment into the list */
for (wrk = environ; *wrk != NULL; wrk++) {
usage();
} else if (!strcmp(r, "ignore-environment")) {
envlist_free(envlist);
- if ((envlist = envlist_create()) == NULL) {
- (void) fprintf(stderr, "Unable to allocate envlist\n");
- exit(1);
- }
+ envlist = envlist_create();
} else if (!strcmp(r, "U")) {
r = argv[optind++];
if (envlist_unsetenv(envlist, r) != 0)
}
for (wrk = target_environ; *wrk; wrk++) {
- free(*wrk);
+ g_free(*wrk);
}
- free(target_environ);
+ g_free(target_environ);
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
qemu_log("guest_base 0x%lx\n", guest_base);
--oss-lib path to OSS library
--cpu=CPU Build for host CPU [$cpu]
--with-coroutine=BACKEND coroutine backend. Supported options:
- gthread, ucontext, sigaltstack, windows
+ ucontext, sigaltstack, windows
--enable-gcov enable test coverage analysis with gcov
--gcov=GCOV use specified gcov [$gcov_tool]
--disable-blobs disable installing provided firmware blobs
# check and set a backend for coroutine
# We prefer ucontext, but it's not always possible. The fallback
-# is sigcontext. gthread is not selectable except explicitly, because
-# it is not functional enough to run QEMU proper. (It is occasionally
-# useful for debugging purposes.) On Windows the only valid backend
-# is the Windows-specific one.
+# is sigcontext. On Windows the only valid backend is the Windows
+# specific one.
ucontext_works=no
if test "$darwin" != "yes"; then
feature_not_found "ucontext"
fi
;;
- gthread|sigaltstack)
+ sigaltstack)
if test "$mingw32" = "yes"; then
error_exit "only the 'windows' coroutine backend is valid for Windows"
fi
fi
if test "$coroutine_pool" = ""; then
- if test "$coroutine" = "gthread"; then
- coroutine_pool=no
- else
- coroutine_pool=yes
- fi
-fi
-if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then
- error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)"
+ coroutine_pool=yes
fi
if test "$debug_stack_usage" = "yes"; then
ppc64)
TARGET_BASE_ARCH=ppc
TARGET_ABI_DIR=ppc
+ mttcg=yes
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
;;
ppc64le)
TARGET_ARCH=ppc64
TARGET_BASE_ARCH=ppc
TARGET_ABI_DIR=ppc
+ mttcg=yes
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
;;
ppc64abi32)
#include "qapi-event.h"
#include "hw/nmi.h"
#include "sysemu/replay.h"
+#include "hw/boards.h"
#ifdef CONFIG_LINUX
/* Ignore everything else? */
break;
}
+ } else if (cpu->unplug) {
+ qemu_tcg_destroy_vcpu(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ return NULL;
}
atomic_mb_set(&cpu->exit_request, 0);
CpuInfoList *qmp_query_cpus(Error **errp)
{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
CpuInfoList *head = NULL, *cur_item = NULL;
CPUState *cpu;
#else
info->value->arch = CPU_INFO_ARCH_OTHER;
#endif
+ info->value->has_props = !!mc->cpu_index_to_instance_props;
+ if (info->value->has_props) {
+ CpuInstanceProperties *props;
+ props = g_malloc0(sizeof(*props));
+ *props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
+ info->value->props = props;
+ }
/* XXX: waiting for the qapi to support GSList */
if (!cur_item) {
tlb_addr = tlbe->addr_write;
}
- /* Notice an IO access, or a notdirty page. */
+ /* Check notdirty */
+ if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
+ tlb_set_dirty(ENV_GET_CPU(env), addr);
+ tlb_addr = tlb_addr & ~TLB_NOTDIRTY;
+ }
+
+ /* Notice an IO access */
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
/* There's really nothing that can be done to
support this apart from stop-the-world. */
* then encrypted.
*/
rv = readfunc(block,
- opaque,
slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
splitkey, splitkeylen,
+ opaque,
errp);
if (rv < 0) {
goto cleanup;
/* Read the entire LUKS header, minus the key material from
* the underlying device */
- rv = readfunc(block, opaque, 0,
+ rv = readfunc(block, 0,
(uint8_t *)&luks->header,
sizeof(luks->header),
+ opaque,
errp);
if (rv < 0) {
ret = rv;
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
/* Reserve header space to match payload offset */
- initfunc(block, opaque, block->payload_offset, &local_err);
+ initfunc(block, block->payload_offset, opaque, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto error;
/* Write out the partition header and key slot headers */
- writefunc(block, opaque, 0,
+ writefunc(block, 0,
(const uint8_t *)&luks->header,
sizeof(luks->header),
+ opaque,
&local_err);
/* Delay checking local_err until we've byte-swapped */
/* Write out the master key material, starting at the
* sector immediately following the partition header. */
- if (writefunc(block, opaque,
+ if (writefunc(block,
luks->header.key_slots[0].key_offset *
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
splitkey, splitkeylen,
+ opaque,
errp) != splitkeylen) {
goto error;
}
#include <gcrypt.h>
#endif
+#include "crypto/random.h"
+
/* #define DEBUG_GNUTLS */
/*
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
#endif
+ if (qcrypto_random_init(errp) < 0) {
+ return -1;
+ }
+
return 0;
}
gcry_randomize(buf, buflen, GCRY_STRONG_RANDOM);
return 0;
}
+
+int qcrypto_random_init(Error **errp G_GNUC_UNUSED) { return 0; }
return 0;
}
+
+
+int qcrypto_random_init(Error **errp G_GNUC_UNUSED) { return 0; }
#include "crypto/random.h"
-int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
- size_t buflen G_GNUC_UNUSED,
- Error **errp)
-{
- int fd;
- int ret = -1;
- int got;
+#ifdef _WIN32
+#include <Wincrypt.h>
+static HCRYPTPROV hCryptProv;
+#else
+static int fd; /* a file handle to either /dev/urandom or /dev/random */
+#endif
+int qcrypto_random_init(Error **errp)
+{
+#ifndef _WIN32
/* TBD perhaps also add support for BSD getentropy / Linux
* getrandom syscalls directly */
fd = open("/dev/urandom", O_RDONLY);
error_setg(errp, "No /dev/urandom or /dev/random found");
return -1;
}
+#else
+ if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_SILENT | CRYPT_VERIFYCONTEXT)) {
+ error_setg_win32(errp, GetLastError(),
+ "Unable to create cryptographic provider");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
+ size_t buflen G_GNUC_UNUSED,
+ Error **errp)
+{
+#ifndef _WIN32
+ int ret = -1;
+ int got;
while (buflen > 0) {
got = read(fd, buf, buflen);
ret = 0;
cleanup:
- close(fd);
return ret;
+#else
+ if (!CryptGenRandom(hCryptProv, buflen, buf)) {
+ error_setg_win32(errp, GetLastError(),
+ "Unable to read random bytes");
+ return -1;
+ }
+
+ return 0;
+#endif
}
d = opendir(dirname);
if (!d) {
error_setg(&error_fatal, "%s cannot open %s", __func__, dirname);
+ return;
}
while ((de = readdir(d)) != NULL) {
#include "hw/acpi/aml-build.h"
#include "qemu/bswap.h"
#include "qemu/bitops.h"
+#include "sysemu/numa.h"
static GArray *build_alloc_array(void)
{
numamem->base_addr = cpu_to_le64(base);
numamem->range_length = cpu_to_le64(len);
}
+
+/*
+ * ACPI spec 5.2.17 System Locality Distance Information Table
+ * (Revision 2.0 or later)
+ */
+void build_slit(GArray *table_data, BIOSLinker *linker)
+{
+ int slit_start, i, j;
+ slit_start = table_data->len;
+
+ acpi_data_push(table_data, sizeof(AcpiTableHeader));
+
+ build_append_int_noprefix(table_data, nb_numa_nodes, 8);
+ for (i = 0; i < nb_numa_nodes; i++) {
+ for (j = 0; j < nb_numa_nodes; j++) {
+ assert(numa_info[i].distance[j]);
+ build_append_int_noprefix(table_data, numa_info[i].distance[j], 1);
+ }
+ }
+
+ build_header(linker, table_data,
+ (void *)(table_data->data + slit_start),
+ "SLIT",
+ table_data->len - slit_start, 1, NULL, NULL);
+}
/* build Processor object for each processor */
for (i = 0; i < arch_ids->len; i++) {
- int j;
Aml *dev;
Aml *uid = aml_int(i);
GArray *madt_buf = g_array_new(0, 1, 1);
* as a result _PXM is required for all CPUs which might
* be hot-plugged. For simplicity, add it for all CPUs.
*/
- j = numa_get_node_for_cpu(i);
- if (j < nb_numa_nodes) {
- aml_append(dev, aml_name_decl("_PXM", aml_int(j)));
+ if (arch_ids->cpus[i].props.has_node_id) {
+ aml_append(dev, aml_name_decl("_PXM",
+ aml_int(arch_ids->cpus[i].props.node_id)));
}
aml_append(cpus_dev, dev);
AcpiSystemResourceAffinityTable *srat;
AcpiSratProcessorGiccAffinity *core;
AcpiSratMemoryAffinity *numamem;
- int i, j, srat_start;
+ int i, srat_start;
uint64_t mem_base;
- uint32_t *cpu_node = g_malloc0(vms->smp_cpus * sizeof(uint32_t));
-
- for (i = 0; i < vms->smp_cpus; i++) {
- j = numa_get_node_for_cpu(i);
- if (j < nb_numa_nodes) {
- cpu_node[i] = j;
- }
- }
+ MachineClass *mc = MACHINE_GET_CLASS(vms);
+ const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(MACHINE(vms));
srat_start = table_data->len;
srat = acpi_data_push(table_data, sizeof(*srat));
srat->reserved1 = cpu_to_le32(1);
- for (i = 0; i < vms->smp_cpus; ++i) {
+ for (i = 0; i < cpu_list->len; ++i) {
+ int node_id = cpu_list->cpus[i].props.has_node_id ?
+ cpu_list->cpus[i].props.node_id : 0;
core = acpi_data_push(table_data, sizeof(*core));
core->type = ACPI_SRAT_PROCESSOR_GICC;
core->length = sizeof(*core);
- core->proximity = cpu_to_le32(cpu_node[i]);
+ core->proximity = cpu_to_le32(node_id);
core->acpi_processor_uid = cpu_to_le32(i);
core->flags = cpu_to_le32(1);
}
- g_free(cpu_node);
mem_base = vms->memmap[VIRT_MEM].base;
for (i = 0; i < nb_numa_nodes; ++i) {
{
int cpu;
int addr_cells = 1;
- unsigned int i;
+ const MachineState *ms = MACHINE(vms);
/*
* From Documentation/devicetree/bindings/arm/cpus.txt
for (cpu = vms->smp_cpus - 1; cpu >= 0; cpu--) {
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
+ CPUState *cs = CPU(armcpu);
qemu_fdt_add_subnode(vms->fdt, nodename);
qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "cpu");
armcpu->mp_affinity);
}
- i = numa_get_node_for_cpu(cpu);
- if (i < nb_numa_nodes) {
- qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id", i);
+ if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
+ qemu_fdt_setprop_cell(vms->fdt, nodename, "numa-node-id",
+ ms->possible_cpus->cpus[cs->cpu_index].props.node_id);
}
g_free(nodename);
virt_build_smbios(vms);
}
+static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
+{
+ uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
+ VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
+
+ if (!vmc->disallow_affinity_adjustment) {
+ /* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
+ * GIC's target-list limitations. 32-bit KVM hosts currently
+ * always create clusters of 4 CPUs, but that is expected to
+ * change when they gain support for gicv3. When KVM is enabled
+ * it will override the changes we make here, therefore our
+ * purposes are to make TCG consistent (with 64-bit KVM hosts)
+ * and to improve SGI efficiency.
+ */
+ if (vms->gic_version == 3) {
+ clustersz = GICV3_TARGETLIST_BITS;
+ } else {
+ clustersz = GIC_TARGETLIST_BITS;
+ }
+ }
+ return arm_cpu_mp_affinity(idx, clustersz);
+}
+
static void machvirt_init(MachineState *machine)
{
VirtMachineState *vms = VIRT_MACHINE(machine);
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ const CPUArchIdList *possible_cpus;
qemu_irq pic[NUM_IRQS];
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *secure_sysmem = NULL;
CPUClass *cc;
Error *err = NULL;
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
- uint8_t clustersz;
if (!cpu_model) {
cpu_model = "cortex-a15";
*/
if (vms->gic_version == 3) {
virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000;
- clustersz = GICV3_TARGETLIST_BITS;
} else {
virt_max_cpus = GIC_NCPU;
- clustersz = GIC_TARGETLIST_BITS;
}
if (max_cpus > virt_max_cpus) {
exit(1);
}
- for (n = 0; n < smp_cpus; n++) {
- Object *cpuobj = object_new(typename);
- if (!vmc->disallow_affinity_adjustment) {
- /* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
- * GIC's target-list limitations. 32-bit KVM hosts currently
- * always create clusters of 4 CPUs, but that is expected to
- * change when they gain support for gicv3. When KVM is enabled
- * it will override the changes we make here, therefore our
- * purposes are to make TCG consistent (with 64-bit KVM hosts)
- * and to improve SGI efficiency.
- */
- uint8_t aff1 = n / clustersz;
- uint8_t aff0 = n % clustersz;
- object_property_set_int(cpuobj, (aff1 << ARM_AFF1_SHIFT) | aff0,
- "mp-affinity", NULL);
+ possible_cpus = mc->possible_cpu_arch_ids(machine);
+ for (n = 0; n < possible_cpus->len; n++) {
+ Object *cpuobj;
+ CPUState *cs;
+ int node_id;
+
+ if (n >= smp_cpus) {
+ break;
+ }
+
+ cpuobj = object_new(typename);
+ object_property_set_int(cpuobj, possible_cpus->cpus[n].arch_id,
+ "mp-affinity", NULL);
+
+ cs = CPU(cpuobj);
+ cs->cpu_index = n;
+
+ node_id = possible_cpus->cpus[cs->cpu_index].props.node_id;
+ if (!possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
+ /* by default CPUState::numa_node was 0 if it's not set via CLI
+ * keep it this way for now but in future we probably should
+ * refuse to start up with incomplete numa mapping */
+ node_id = 0;
+ }
+ if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) {
+ cs->numa_node = node_id;
+ } else {
+ /* CPU isn't device_add compatible yet, this shouldn't happen */
+ error_setg(&error_abort, "user set node-id not implemented");
}
if (!vms->secure) {
}
}
+static CpuInstanceProperties
+virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+
+ assert(cpu_index < possible_cpus->len);
+ return possible_cpus->cpus[cpu_index].props;
+}
+
+static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
+{
+ int n;
+ VirtMachineState *vms = VIRT_MACHINE(ms);
+
+ if (ms->possible_cpus) {
+ assert(ms->possible_cpus->len == max_cpus);
+ return ms->possible_cpus;
+ }
+
+ ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
+ sizeof(CPUArchId) * max_cpus);
+ ms->possible_cpus->len = max_cpus;
+ for (n = 0; n < ms->possible_cpus->len; n++) {
+ ms->possible_cpus->cpus[n].arch_id =
+ virt_cpu_mp_affinity(vms, n);
+ ms->possible_cpus->cpus[n].props.has_thread_id = true;
+ ms->possible_cpus->cpus[n].props.thread_id = n;
+
+ /* default distribution of CPUs over NUMA nodes */
+ if (nb_numa_nodes) {
+ /* preset values but do not enable them i.e. 'has_node_id = false',
+ * numa init code will enable them later if manual mapping wasn't
+ * present on CLI */
+ ms->possible_cpus->cpus[n].props.node_id = n % nb_numa_nodes;
+ }
+ }
+ return ms->possible_cpus;
+}
+
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->pci_allow_0_address = true;
/* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
mc->minimum_page_bits = 12;
+ mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids;
+ mc->cpu_index_to_instance_props = virt_cpu_index_to_props;
}
static const TypeInfo virt_machine_info = {
return NVME_NO_COMPLETE;
}
+static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
+ NvmeRequest *req)
+{
+ NvmeRwCmd *rw = (NvmeRwCmd *)cmd;
+ const uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
+ const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
+ uint64_t slba = le64_to_cpu(rw->slba);
+ uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
+ uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
+ uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
+
+ if (slba + nlb > ns->id_ns.nsze) {
+ return NVME_LBA_RANGE | NVME_DNR;
+ }
+
+ req->has_sg = false;
+ block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0,
+ BLOCK_ACCT_WRITE);
+ req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, aio_slba, aio_nlb,
+ BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req);
+ return NVME_NO_COMPLETE;
+}
+
static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
NvmeRequest *req)
{
switch (cmd->opcode) {
case NVME_CMD_FLUSH:
return nvme_flush(n, ns, cmd, req);
+ case NVME_CMD_WRITE_ZEROS:
+ return nvme_write_zeros(n, ns, cmd, req);
case NVME_CMD_WRITE:
case NVME_CMD_READ:
return nvme_rw(n, ns, cmd, req);
id->sqes = (0x6 << 4) | 0x6;
id->cqes = (0x4 << 4) | 0x4;
id->nn = cpu_to_le32(n->num_namespaces);
+ id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROS);
id->psd[0].mp = cpu_to_le16(0x9c4);
id->psd[0].enlat = cpu_to_le32(0x10);
id->psd[0].exlat = cpu_to_le32(0x4);
NVME_CMD_READ = 0x02,
NVME_CMD_WRITE_UNCOR = 0x04,
NVME_CMD_COMPARE = 0x05,
+ NVME_CMD_WRITE_ZEROS = 0x08,
NVME_CMD_DSM = 0x09,
};
static void virtio_blk_free_request(VirtIOBlockReq *req)
{
- if (req) {
- g_free(req);
- }
+ g_free(req);
}
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
#endif
if (s->file) {
+ AddressSpace *as = s->cpu ? s->cpu->as : NULL;
+
if (!s->force_raw) {
size = load_elf_as(s->file, NULL, NULL, &entry, NULL, NULL,
- big_endian, 0, 0, 0, s->cpu->as);
+ big_endian, 0, 0, 0, as);
if (size < 0) {
size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL,
- s->cpu->as);
+ as);
}
}
if (size < 0 || s->force_raw) {
/* Default to the maximum size being the machine's ram size */
- size = load_image_targphys_as(s->file, s->addr, ram_size,
- s->cpu->as);
+ size = load_image_targphys_as(s->file, s->addr, ram_size, as);
} else {
s->addr = entry;
}
#include "qapi/visitor.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
+#include "sysemu/numa.h"
#include "qemu/error-report.h"
#include "qemu/cutils.h"
+#include "sysemu/numa.h"
static char *machine_get_accel(Object *obj, Error **errp)
{
return head;
}
+/**
+ * machine_set_cpu_numa_node:
+ * @machine: machine object to modify
+ * @props: specifies which cpu objects to assign to
+ * numa node specified by @props.node_id
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Associate NUMA node specified by @props.node_id with cpu slots that
+ * match socket/core/thread-ids specified by @props. It's recommended to use
+ * query-hotpluggable-cpus.props values to specify affected cpu slots,
+ * which would lead to exact 1:1 mapping of cpu slots to NUMA node.
+ *
+ * However for CLI convenience it's possible to pass in subset of properties,
+ * which would affect all cpu slots that match it.
+ * Ex for pc machine:
+ * -smp 4,cores=2,sockets=2 -numa node,nodeid=0 -numa node,nodeid=1 \
+ * -numa cpu,node-id=0,socket_id=0 \
+ * -numa cpu,node-id=1,socket_id=1
+ * will assign all child cores of socket 0 to node 0 and
+ * of socket 1 to node 1.
+ *
+ * On attempt of reassigning (already assigned) cpu slot to another NUMA node,
+ * return error.
+ * Empty subset is disallowed and function will return with error in this case.
+ */
+void machine_set_cpu_numa_node(MachineState *machine,
+ const CpuInstanceProperties *props, Error **errp)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ bool match = false;
+ int i;
+
+ if (!mc->possible_cpu_arch_ids) {
+ error_setg(errp, "mapping of CPUs to NUMA node is not supported");
+ return;
+ }
+
+ /* disabling node mapping is not supported, forbid it */
+ assert(props->has_node_id);
+
+ /* force board to initialize possible_cpus if it hasn't been done yet */
+ mc->possible_cpu_arch_ids(machine);
+
+ for (i = 0; i < machine->possible_cpus->len; i++) {
+ CPUArchId *slot = &machine->possible_cpus->cpus[i];
+
+ /* reject unsupported by board properties */
+ if (props->has_thread_id && !slot->props.has_thread_id) {
+ error_setg(errp, "thread-id is not supported");
+ return;
+ }
+
+ if (props->has_core_id && !slot->props.has_core_id) {
+ error_setg(errp, "core-id is not supported");
+ return;
+ }
+
+ if (props->has_socket_id && !slot->props.has_socket_id) {
+ error_setg(errp, "socket-id is not supported");
+ return;
+ }
+
+ /* skip slots with explicit mismatch */
+ if (props->has_thread_id && props->thread_id != slot->props.thread_id) {
+ continue;
+ }
+
+ if (props->has_core_id && props->core_id != slot->props.core_id) {
+ continue;
+ }
+
+ if (props->has_socket_id && props->socket_id != slot->props.socket_id) {
+ continue;
+ }
+
+ /* reject assignment if slot is already assigned, for compatibility
+ * of legacy cpu_index mapping with SPAPR core based mapping do not
+ * error out if cpu thread and matched core have the same node-id */
+ if (slot->props.has_node_id &&
+ slot->props.node_id != props->node_id) {
+ error_setg(errp, "CPU is already assigned to node-id: %" PRId64,
+ slot->props.node_id);
+ return;
+ }
+
+ /* assign slot to node as it's matched '-numa cpu' key */
+ match = true;
+ slot->props.node_id = props->node_id;
+ slot->props.has_node_id = props->has_node_id;
+ }
+
+ if (!match) {
+ error_setg(errp, "no match found");
+ }
+}
+
static void machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
* On Linux, each node's border has to be 8MB aligned
*/
mc->numa_mem_align_shift = 23;
+ mc->numa_auto_assign_ram = numa_default_auto_assign_ram;
object_class_property_add_str(oc, "accel",
machine_get_accel, machine_set_accel, &error_abort);
return machine->mem_merge;
}
+static char *cpu_slot_to_string(const CPUArchId *cpu)
+{
+ GString *s = g_string_new(NULL);
+ if (cpu->props.has_socket_id) {
+ g_string_append_printf(s, "socket-id: %"PRId64, cpu->props.socket_id);
+ }
+ if (cpu->props.has_core_id) {
+ if (s->len) {
+ g_string_append_printf(s, ", ");
+ }
+ g_string_append_printf(s, "core-id: %"PRId64, cpu->props.core_id);
+ }
+ if (cpu->props.has_thread_id) {
+ if (s->len) {
+ g_string_append_printf(s, ", ");
+ }
+ g_string_append_printf(s, "thread-id: %"PRId64, cpu->props.thread_id);
+ }
+ return g_string_free(s, false);
+}
+
+static void machine_numa_validate(MachineState *machine)
+{
+ int i;
+ GString *s = g_string_new(NULL);
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(machine);
+
+ assert(nb_numa_nodes);
+ for (i = 0; i < possible_cpus->len; i++) {
+ const CPUArchId *cpu_slot = &possible_cpus->cpus[i];
+
+ /* at this point numa mappings are initilized by CLI options
+ * or with default mappings so it's sufficient to list
+ * all not yet mapped CPUs here */
+ /* TODO: make it hard error in future */
+ if (!cpu_slot->props.has_node_id) {
+ char *cpu_str = cpu_slot_to_string(cpu_slot);
+ g_string_append_printf(s, "%sCPU %d [%s]", s->len ? ", " : "", i,
+ cpu_str);
+ g_free(cpu_str);
+ }
+ }
+ if (s->len) {
+ error_report("warning: CPU(s) not present in any NUMA nodes: %s",
+ s->str);
+ error_report("warning: All CPU(s) up to maxcpus should be described "
+ "in NUMA config, ability to start up with partial NUMA "
+ "mappings is obsoleted and will be removed in future");
+ }
+ g_string_free(s, true);
+}
+
+void machine_run_board_init(MachineState *machine)
+{
+ MachineClass *machine_class = MACHINE_GET_CLASS(machine);
+
+ if (nb_numa_nodes) {
+ machine_numa_validate(machine);
+ }
+ machine_class->init(machine);
+}
+
static void machine_class_finalize(ObjectClass *klass, void *data)
{
MachineClass *mc = MACHINE_CLASS(klass);
uint32_t dval;
int x, y, y_start;
unsigned int width, height;
- ram_addr_t page, page_min, page_max;
+ ram_addr_t page;
+ DirtyBitmapSnapshot *snap = NULL;
if (surface_bits_per_pixel(surface) != 32) {
return;
height = s->height;
y_start = -1;
- page_min = -1;
- page_max = 0;
- page = 0;
pix = memory_region_get_ram_ptr(&s->vram_mem);
data = (uint32_t *)surface_data(surface);
- memory_region_sync_dirty_bitmap(&s->vram_mem);
+ if (!s->full_update) {
+ memory_region_sync_dirty_bitmap(&s->vram_mem);
+ snap = memory_region_snapshot_and_clear_dirty(&s->vram_mem, 0x0,
+ memory_region_size(&s->vram_mem),
+ DIRTY_MEMORY_VGA);
+ }
+
for (y = 0; y < height; y++) {
- int update = s->full_update;
+ int update;
page = (ram_addr_t)y * width;
- update |= memory_region_get_dirty(&s->vram_mem, page, width,
- DIRTY_MEMORY_VGA);
+
+ if (s->full_update) {
+ update = 1;
+ } else {
+ update = memory_region_snapshot_get_dirty(&s->vram_mem, snap, page,
+ width);
+ }
+
if (update) {
if (y_start < 0) {
y_start = y;
}
- if (page < page_min) {
- page_min = page;
- }
- if (page > page_max) {
- page_max = page;
- }
for (x = 0; x < width; x++) {
dval = *pix++;
}
} else {
if (y_start >= 0) {
- dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start);
+ dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
y_start = -1;
}
pix += width;
}
s->full_update = 0;
if (y_start >= 0) {
- dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start);
- }
- if (page_max >= page_min) {
- memory_region_reset_dirty(&s->vram_mem,
- page_min, page_max - page_min, DIRTY_MEMORY_VGA);
+ dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
}
/* vsync interrupt? */
if (s->regs[0] & CG3_CR_ENABLE_INTS) {
s->regs[1] |= CG3_SR_PENDING_INT;
qemu_irq_raise(s->irq);
}
+ g_free(snap);
}
static void cg3_invalidate_display(void *opaque)
static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
{
LedState *s = opaque;
- char buf[2];
+ char buf[3];
dpy_text_cursor(s->con, -1, -1);
qemu_console_resize(s->con, 2, 1);
/* TODO: draw the segments */
- snprintf(buf, 2, "%02hhx\n", s->segments);
+ snprintf(buf, 3, "%02hhx", s->segments);
console_write_ch(chardata++, ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE,
QEMU_COLOR_BLACK, 1));
console_write_ch(chardata++, ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
{
SM501State *s = (SM501State *)opaque;
DisplaySurface *surface = qemu_console_surface(s->con);
+ DirtyBitmapSnapshot *snap;
int y, c_x = 0, c_y = 0;
int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0;
int width = get_width(s, crt);
draw_hwc_line_func *draw_hwc_line = NULL;
int full_update = 0;
int y_start = -1;
- ram_addr_t page_min = ~0l;
- ram_addr_t page_max = 0l;
- ram_addr_t offset;
+ ram_addr_t offset = 0;
uint32_t *palette;
uint8_t hwc_palette[3 * 3];
uint8_t *hwc_src = NULL;
/* draw each line according to conditions */
memory_region_sync_dirty_bitmap(&s->local_mem_region);
+ snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region,
+ offset, width * height * src_bpp, DIRTY_MEMORY_VGA);
for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) {
int update, update_hwc;
- ram_addr_t page0 = offset;
- ram_addr_t page1 = offset + width * src_bpp - 1;
/* check if hardware cursor is enabled and we're within its range */
update_hwc = draw_hwc_line && c_y <= y && y < c_y + SM501_HWC_HEIGHT;
update = full_update || update_hwc;
/* check dirty flags for each line */
- update |= memory_region_get_dirty(&s->local_mem_region, page0,
- page1 - page0, DIRTY_MEMORY_VGA);
+ update |= memory_region_snapshot_get_dirty(&s->local_mem_region, snap,
+ offset, width * src_bpp);
/* draw line and change status */
if (update) {
if (y_start < 0) {
y_start = y;
}
- if (page0 < page_min) {
- page_min = page0;
- }
- if (page1 > page_max) {
- page_max = page1;
- }
} else {
if (y_start >= 0) {
/* flush to display */
}
}
}
+ g_free(snap);
/* complete flush to display */
if (y_start >= 0) {
dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
}
-
- /* clear dirty flags */
- if (page_min != ~0l) {
- memory_region_reset_dirty(&s->local_mem_region,
- page_min, page_max + TARGET_PAGE_SIZE,
- DIRTY_MEMORY_VGA);
- }
}
static const GraphicHwOps sm501_ops = {
}
}
-static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len)
+static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
+ ram_addr_t addr, int len)
{
int ret;
- ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
+ ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
if (s->depth == 24) {
- ret |= memory_region_get_dirty(&s->vram_mem,
- s->vram24_offset + addr * 4, len * 4,
- DIRTY_MEMORY_VGA);
- ret |= memory_region_get_dirty(&s->vram_mem,
- s->cplane_offset + addr * 4, len * 4,
- DIRTY_MEMORY_VGA);
+ ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
+ s->vram24_offset + addr * 4, len * 4);
+ ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
+ s->cplane_offset + addr * 4, len * 4);
}
return ret;
}
-static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len)
-{
- memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
-
- if (s->depth == 24) {
- memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
- len * 4, DIRTY_MEMORY_VGA);
- memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
- len * 4, DIRTY_MEMORY_VGA);
- }
-}
-
static void update_palette_entries(TCXState *s, int start, int end)
{
DisplaySurface *surface = qemu_console_surface(s->con);
{
TCXState *ts = opaque;
DisplaySurface *surface = qemu_console_surface(ts->con);
- ram_addr_t page, page_min, page_max;
+ ram_addr_t page;
+ DirtyBitmapSnapshot *snap = NULL;
int y, y_start, dd, ds;
uint8_t *d, *s;
page = 0;
y_start = -1;
- page_min = -1;
- page_max = 0;
d = surface_data(surface);
s = ts->vram;
dd = surface_stride(surface);
ds = 1024;
memory_region_sync_dirty_bitmap(&ts->vram_mem);
+ snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
+ memory_region_size(&ts->vram_mem),
+ DIRTY_MEMORY_VGA);
+
for (y = 0; y < ts->height; y++, page += ds) {
- if (tcx_check_dirty(ts, page, ds)) {
+ if (tcx_check_dirty(ts, snap, page, ds)) {
if (y_start < 0)
y_start = y;
- if (page < page_min)
- page_min = page;
- if (page > page_max)
- page_max = page;
tcx_draw_line32(ts, d, s, ts->width);
if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
dpy_gfx_update(ts->con, 0, y_start,
ts->width, y - y_start);
}
- /* reset modified pages */
- if (page_max >= page_min) {
- tcx_reset_dirty(ts, page_min, page_max - page_min);
- }
+ g_free(snap);
}
static void tcx24_update_display(void *opaque)
{
TCXState *ts = opaque;
DisplaySurface *surface = qemu_console_surface(ts->con);
- ram_addr_t page, page_min, page_max;
+ ram_addr_t page;
+ DirtyBitmapSnapshot *snap = NULL;
int y, y_start, dd, ds;
uint8_t *d, *s;
uint32_t *cptr, *s24;
page = 0;
y_start = -1;
- page_min = -1;
- page_max = 0;
d = surface_data(surface);
s = ts->vram;
s24 = ts->vram24;
ds = 1024;
memory_region_sync_dirty_bitmap(&ts->vram_mem);
+ snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
+ memory_region_size(&ts->vram_mem),
+ DIRTY_MEMORY_VGA);
+
for (y = 0; y < ts->height; y++, page += ds) {
- if (tcx_check_dirty(ts, page, ds)) {
+ if (tcx_check_dirty(ts, snap, page, ds)) {
if (y_start < 0)
y_start = y;
- if (page < page_min)
- page_min = page;
- if (page > page_max)
- page_max = page;
+
tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
tcx_draw_cursor32(ts, d, y, ts->width);
dpy_gfx_update(ts->con, 0, y_start,
ts->width, y - y_start);
}
- /* reset modified pages */
- if (page_max >= page_min) {
- tcx_reset_dirty(ts, page_min, page_max - page_min);
- }
+ g_free(snap);
}
static void tcx_invalidate_display(void *opaque)
if (!full_update) {
vga_sync_dirty_bitmap(s);
snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1,
- bwidth * height,
+ line_offset * height,
DIRTY_MEMORY_VGA);
}
}
}
+void virtio_gpu_gl_block(void *opaque, bool block)
+{
+ VirtIOGPU *g = opaque;
+
+ if (block) {
+ g->renderer_blocked++;
+ } else {
+ g->renderer_blocked--;
+ }
+ assert(g->renderer_blocked >= 0);
+
+ if (g->renderer_blocked == 0) {
+ virtio_gpu_process_cmdq(g);
+ }
+}
+
int virtio_gpu_virgl_init(VirtIOGPU *g)
{
int ret;
return 0;
}
-static void virtio_gpu_gl_block(void *opaque, bool block)
-{
- VirtIOGPU *g = opaque;
-
- if (block) {
- g->renderer_blocked++;
- } else {
- g->renderer_blocked--;
- }
- assert(g->renderer_blocked >= 0);
-
- if (g->renderer_blocked == 0) {
- virtio_gpu_process_cmdq(g);
- }
-}
-
const GraphicHwOps virtio_gpu_ops = {
.invalidate = virtio_gpu_invalidate_display,
.gfx_update = virtio_gpu_update_display,
.text_update = virtio_gpu_text_update,
.ui_info = virtio_gpu_ui_info,
+#ifdef CONFIG_VIRGL
.gl_block = virtio_gpu_gl_block,
+#endif
};
static const VMStateDescription vmstate_virtio_gpu_scanout = {
srat->reserved1 = cpu_to_le32(1);
for (i = 0; i < apic_ids->len; i++) {
- int j = numa_get_node_for_cpu(i);
+ int node_id = apic_ids->cpus[i].props.has_node_id ?
+ apic_ids->cpus[i].props.node_id : 0;
uint32_t apic_id = apic_ids->cpus[i].arch_id;
if (apic_id < 255) {
core->type = ACPI_SRAT_PROCESSOR_APIC;
core->length = sizeof(*core);
core->local_apic_id = apic_id;
- if (j < nb_numa_nodes) {
- core->proximity_lo = j;
- }
+ core->proximity_lo = node_id;
memset(core->proximity_hi, 0, 3);
core->local_sapic_eid = 0;
core->flags = cpu_to_le32(1);
core->type = ACPI_SRAT_PROCESSOR_x2APIC;
core->length = sizeof(*core);
core->x2apic_id = cpu_to_le32(apic_id);
- if (j < nb_numa_nodes) {
- core->proximity_domain = cpu_to_le32(j);
- }
+ core->proximity_domain = cpu_to_le32(node_id);
core->flags = cpu_to_le32(1);
}
}
if (pcms->numa_nodes) {
acpi_add_table(table_offsets, tables_blob);
build_srat(tables_blob, tables->linker, machine);
+ if (have_numa_distance) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_slit(tables_blob, tables->linker);
+ }
}
if (acpi_get_mcfg(&mcfg)) {
acpi_add_table(table_offsets, tables_blob);
{
FWCfgState *fw_cfg;
uint64_t *numa_fw_cfg;
- int i, j;
+ int i;
+ const CPUArchIdList *cpus;
+ MachineClass *mc = MACHINE_GET_CLASS(pcms);
fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, as);
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus);
*/
numa_fw_cfg = g_new0(uint64_t, 1 + pcms->apic_id_limit + nb_numa_nodes);
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
- for (i = 0; i < max_cpus; i++) {
- unsigned int apic_id = x86_cpu_apic_id_from_index(i);
+ cpus = mc->possible_cpu_arch_ids(MACHINE(pcms));
+ for (i = 0; i < cpus->len; i++) {
+ unsigned int apic_id = cpus->cpus[i].arch_id;
assert(apic_id < pcms->apic_id_limit);
- j = numa_get_node_for_cpu(i);
- if (j < nb_numa_nodes) {
- numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
+ if (cpus->cpus[i].props.has_node_id) {
+ numa_fw_cfg[apic_id + 1] = cpu_to_le64(cpus->cpus[i].props.node_id);
}
}
for (i = 0; i < nb_numa_nodes; i++) {
DeviceState *dev, Error **errp)
{
int idx;
+ int node_id;
CPUState *cs;
CPUArchId *cpu_slot;
X86CPUTopoInfo topo;
cs = CPU(cpu);
cs->cpu_index = idx;
+
+ node_id = cpu_slot->props.node_id;
+ if (!cpu_slot->props.has_node_id) {
+ /* by default CPUState::numa_node was 0 if it's not set via CLI
+ * keep it this way for now but in future we probably should
+ * refuse to start up with incomplete numa mapping */
+ node_id = 0;
+ }
+ if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) {
+ cs->numa_node = node_id;
+ } else if (cs->numa_node != node_id) {
+ error_setg(errp, "node-id %d must match numa node specified"
+ "with -numa option for cpu-index %d",
+ cs->numa_node, cs->cpu_index);
+ return;
+ }
}
static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
}
}
-static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
+static CpuInstanceProperties
+pc_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
{
- X86CPUTopoInfo topo;
- x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index,
- &topo);
- return topo.pkg_id;
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+
+ assert(cpu_index < possible_cpus->len);
+ return possible_cpus->cpus[cpu_index].props;
}
static const CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *ms)
ms->possible_cpus->cpus[i].props.core_id = topo.core_id;
ms->possible_cpus->cpus[i].props.has_thread_id = true;
ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id;
+
+ /* default distribution of CPUs over NUMA nodes */
+ if (nb_numa_nodes) {
+ /* preset values but do not enable them i.e. 'has_node_id = false',
+ * numa init code will enable them later if manual mapping wasn't
+ * present on CLI */
+ ms->possible_cpus->cpus[i].props.node_id =
+ topo.pkg_id % nb_numa_nodes;
+ }
}
return ms->possible_cpus;
}
pcmc->acpi_data_size = 0x20000 + 0x8000;
pcmc->save_tsc_khz = true;
mc->get_hotplug_handler = pc_get_hotpug_handler;
- mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
+ mc->cpu_index_to_instance_props = pc_cpu_index_to_props;
mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
mc->has_hotpluggable_cpus = true;
mc->default_boot_order = "cad";
#endif
#include "migration/migration.h"
#include "kvm_i386.h"
+#include "sysemu/numa.h"
#define MAX_IDE_BUS 2
pc_i440fx_machine_options(m);
m->alias = "pc";
m->is_default = 1;
+ m->numa_auto_assign_ram = numa_legacy_auto_assign_ram;
}
DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", NULL,
#include "hw/usb.h"
#include "qemu/error-report.h"
#include "migration/migration.h"
+#include "sysemu/numa.h"
/* ICH9 AHCI has 6 ports */
#define MAX_SATA_PORTS 6
{
pc_q35_machine_options(m);
m->alias = "q35";
+ m->numa_auto_assign_ram = numa_legacy_auto_assign_ram;
}
DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL,
.select = VIRTIO_INPUT_CFG_ABS_INFO,
.subsel = ABS_X,
.size = sizeof(virtio_input_absinfo),
- .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1),
+ .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
+ .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
},{
.select = VIRTIO_INPUT_CFG_ABS_INFO,
.subsel = ABS_Y,
.size = sizeof(virtio_input_absinfo),
- .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1),
+ .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
+ .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
},
{ /* end of list */ },
};
irq->priority = irq->saved_priority;
}
+ irq->status = 0;
if (state & KVM_XICS_PENDING) {
if (state & KVM_XICS_LEVEL_SENSITIVE) {
irq->status |= XICS_STATUS_ASSERTED;
| XICS_STATUS_REJECTED;
}
}
+ if (state & KVM_XICS_PRESENTED) {
+ irq->status |= XICS_STATUS_PRESENTED;
+ }
+ if (state & KVM_XICS_QUEUED) {
+ irq->status |= XICS_STATUS_QUEUED;
+ }
}
}
state |= KVM_XICS_PENDING;
}
}
+ if (irq->status & XICS_STATUS_PRESENTED) {
+ state |= KVM_XICS_PRESENTED;
+ }
+ if (irq->status & XICS_STATUS_QUEUED) {
+ state |= KVM_XICS_QUEUED;
+ }
ret = ioctl(kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr);
if (ret != 0) {
ram_size - initrd_offset);
}
if (initrd_size < 0) {
- error_report("qemu: could not load initrd '%s'",
+ error_report("could not load initrd '%s'",
initrd_filename);
exit(EXIT_FAILURE);
}
ram_size - initrd_offset);
}
if (initrd_size < 0) {
- error_report("qemu: could not load initrd '%s'",
+ error_report("could not load initrd '%s'",
initrd_filename);
exit(EXIT_FAILURE);
}
#define CLOCKFREQ (266UL * 1000UL * 1000UL)
#define BUSFREQ (100UL * 1000UL * 1000UL)
+#define NDRV_VGA_FILENAME "qemu_vga.ndrv"
+
/* UniN device */
static void unin_write(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
MACIOIDEState *macio_ide;
BusState *adb_bus;
MacIONVRAMState *nvr;
- int bios_size;
+ int bios_size, ndrv_size;
+ uint8_t *ndrv_file;
MemoryRegion *pic_mem, *escc_mem;
MemoryRegion *escc_bar = g_new(MemoryRegion, 1);
int ppc_boot_device;
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ);
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_NVRAM_ADDR, nvram_addr);
+ /* MacOS NDRV VGA driver */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, NDRV_VGA_FILENAME);
+ if (filename) {
+ ndrv_size = get_image_size(filename);
+ if (ndrv_size != -1) {
+ ndrv_file = g_malloc(ndrv_size);
+ ndrv_size = load_image(filename, ndrv_file);
+
+ fw_cfg_add_file(fw_cfg, "ndrv/qemu_vga.ndrv", ndrv_file, ndrv_size);
+ }
+ g_free(filename);
+ }
+
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
}
#define CLOCKFREQ 266000000UL
#define BUSFREQ 66000000UL
+#define NDRV_VGA_FILENAME "qemu_vga.ndrv"
+
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
MACIOIDEState *macio_ide;
DeviceState *dev;
BusState *adb_bus;
- int bios_size;
+ int bios_size, ndrv_size;
+ uint8_t *ndrv_file;
MemoryRegion *pic_mem;
MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1);
uint16_t ppc_boot_device;
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, CLOCKFREQ);
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ);
+ /* MacOS NDRV VGA driver */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, NDRV_VGA_FILENAME);
+ if (filename) {
+ ndrv_size = get_image_size(filename);
+ if (ndrv_size != -1) {
+ ndrv_file = g_malloc(ndrv_size);
+ ndrv_size = load_image(filename, ndrv_file);
+
+ fw_cfg_add_file(fw_cfg, "ndrv/qemu_vga.ndrv", ndrv_file, ndrv_size);
+ }
+ g_free(filename);
+ }
+
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
}
* This is the internal simulator but it could also be an external
* BMC.
*/
- obj = object_resolve_path_type("", TYPE_IPMI_BMC, NULL);
+ obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL);
if (obj) {
pnv->bmc = IPMI_BMC(obj);
}
/* Create the processor chips */
chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
if (!object_class_by_name(chip_typename)) {
- error_report("qemu: invalid CPU model '%s' for %s machine",
+ error_report("invalid CPU model '%s' for %s machine",
machine->cpu_model, MACHINE_GET_CLASS(machine)->name);
exit(1);
}
ppc_tb_t *tb_env = env->tb_env;
booke_timer_t *booke_timer = tb_env->opaque;
- tb_env = env->tb_env;
env->spr[SPR_BOOKE_TCR] = val;
kvmppc_set_tcr(cpu);
/* 16: Vector */
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */
/* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */
- 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 18 - 23 */
+ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */
/* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */
/* 30: MMR, 32: LE atomic, 34: EBB + ext EBB */
* option vector 5: */
static void spapr_dt_ov5_platform_support(void *fdt, int chosen)
{
+ PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
+
char val[2 * 3] = {
24, 0x00, /* Hash/Radix, filled in below. */
25, 0x00, /* Hash options: Segment Tables == no, GTSE == no. */
val[1] = 0x00; /* Hash */
}
} else {
- /* TODO: TCG case, hash */
- val[1] = 0x00;
+ if (first_ppc_cpu->env.mmu_model & POWERPC_MMU_V3) {
+ /* V3 MMU supports both hash and radix (with dynamic switching) */
+ val[1] = 0xC0;
+ } else {
+ /* Otherwise we can only do hash */
+ val[1] = 0x00;
+ }
}
_FDT(fdt_setprop(fdt, chosen, "ibm,arch-vec-5-platform-support",
val, sizeof(val)));
}
spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY);
- if (kvmppc_has_cap_mmu_radix()) {
- /* KVM always allows GTSE with radix... */
+ if (!kvm_enabled() || kvmppc_has_cap_mmu_radix()) {
+ /* KVM and TCG always allow GTSE with radix... */
spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE);
}
/* ... but not with hash (currently). */
MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev);
Error *local_err = NULL;
CPUCore *cc = CPU_CORE(dev);
+ sPAPRCPUCore *sc = SPAPR_CPU_CORE(dev);
char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model);
const char *type = object_get_typename(OBJECT(dev));
CPUArchId *core_slot;
+ int node_id;
int index;
if (dev->hotplugged && !mc->has_hotpluggable_cpus) {
goto out;
}
+ node_id = core_slot->props.node_id;
+ if (!core_slot->props.has_node_id) {
+ /* by default CPUState::numa_node was 0 if it's not set via CLI
+ * keep it this way for now but in future we probably should
+ * refuse to start up with incomplete numa mapping */
+ node_id = 0;
+ }
+ if (sc->node_id == CPU_UNSET_NUMA_NODE_ID) {
+ sc->node_id = node_id;
+ } else if (sc->node_id != node_id) {
+ error_setg(&local_err, "node-id %d must match numa node specified"
+ "with -numa option for cpu-index %d", sc->node_id, cc->core_id);
+ goto out;
+ }
+
out:
g_free(base_core_type);
error_propagate(errp, local_err);
return NULL;
}
-static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index)
+static CpuInstanceProperties
+spapr_cpu_index_to_props(MachineState *machine, unsigned cpu_index)
{
- /* Allocate to NUMA nodes on a "socket" basis (not that concept of
- * socket means much for the paravirtualized PAPR platform) */
- return cpu_index / smp_threads / smp_cores;
+ CPUArchId *core_slot;
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
+ /* make sure possible_cpu are intialized */
+ mc->possible_cpu_arch_ids(machine);
+ /* get CPU core slot containing thread that matches cpu_index */
+ core_slot = spapr_find_cpu_slot(machine, cpu_index, NULL);
+ assert(core_slot);
+ return core_slot->props;
}
static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine)
machine->possible_cpus->cpus[i].arch_id = core_id;
machine->possible_cpus->cpus[i].props.has_core_id = true;
machine->possible_cpus->cpus[i].props.core_id = core_id;
- /* TODO: add 'has_node/node' here to describe
- to which node core belongs */
+
+ /* default distribution of CPUs over NUMA nodes */
+ if (nb_numa_nodes) {
+ /* preset values but do not enable them i.e. 'has_node_id = false',
+ * numa init code will enable them later if manual mapping wasn't
+ * present on CLI */
+ machine->possible_cpus->cpus[i].props.node_id =
+ core_id / smp_threads / smp_cores % nb_numa_nodes;
+ }
}
return machine->possible_cpus;
}
hc->pre_plug = spapr_machine_device_pre_plug;
hc->plug = spapr_machine_device_plug;
hc->unplug = spapr_machine_device_unplug;
- mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id;
+ mc->cpu_index_to_instance_props = spapr_cpu_index_to_props;
mc->possible_cpu_arch_ids = spapr_possible_cpu_arch_ids;
hc->unplug_request = spapr_machine_device_unplug_request;
{
spapr_machine_2_10_class_options(mc);
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_9);
+ mc->numa_auto_assign_ram = numa_legacy_auto_assign_ram;
}
DEFINE_SPAPR_MACHINE(2_9, "2.9", false);
const char *typename = object_class_get_name(scc->cpu_class);
size_t size = object_type_get_instance_size(typename);
Error *local_err = NULL;
- int core_node_id = numa_get_node_for_cpu(cc->core_id);;
void *obj;
int i, j;
sc->threads = g_malloc0(size * cc->nr_threads);
for (i = 0; i < cc->nr_threads; i++) {
- int node_id;
char id[32];
CPUState *cs;
cs = CPU(obj);
cs->cpu_index = cc->core_id + i;
- /* Set NUMA node for the added CPUs */
- node_id = numa_get_node_for_cpu(cs->cpu_index);
- if (node_id != core_node_id) {
- error_setg(&local_err, "Invalid node-id=%d of thread[cpu-index: %d]"
- " on CPU[core-id: %d, node-id: %d], node-id must be the same",
- node_id, cs->cpu_index, cc->core_id, core_node_id);
- goto err;
- }
- if (node_id < nb_numa_nodes) {
- cs->numa_node = node_id;
- }
+ /* Set NUMA node for the threads belonged to core */
+ cs->numa_node = sc->node_id;
snprintf(id, sizeof(id), "thread[%d]", i);
object_property_add_child(OBJECT(sc), id, obj, &local_err);
"POWER9_v1.0",
};
+static Property spapr_cpu_core_properties[] = {
+ DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID),
+ DEFINE_PROP_END_OF_LIST()
+};
+
void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = spapr_cpu_core_realize;
dc->unrealize = spapr_cpu_core_unrealizefn;
+ dc->props = spapr_cpu_core_properties;
scc->cpu_class = cpu_class_by_name(TYPE_POWERPC_CPU, data);
g_assert(scc->cpu_class);
}
target_ulong opcode,
target_ulong *args)
{
- CPUPPCState *env = &cpu->env;
+ CPUState *cs;
target_ulong flags = args[0];
target_ulong proc_tbl = args[1];
target_ulong page_size = args[2];
spapr_check_setup_free_hpt(spapr, spapr->patb_entry, cproc);
spapr->patb_entry = cproc; /* Save new process table */
- if ((flags & FLAG_RADIX) || (flags & FLAG_HASH_PROC_TBL)) {
- /* Use Process TBL */
- env->spr[SPR_LPCR] |= LPCR_UPRT;
- } else {
- env->spr[SPR_LPCR] &= ~LPCR_UPRT;
- }
- if (flags & FLAG_GTSE) { /* Partition Uses Guest Translation Shootdwn */
- env->spr[SPR_LPCR] |= LPCR_GTSE;
- } else {
- env->spr[SPR_LPCR] &= ~LPCR_GTSE;
+
+ /* Update the UPRT and GTSE bits in the LPCR for all cpus */
+ CPU_FOREACH(cs) {
+ set_spr(cs, SPR_LPCR, LPCR_UPRT | LPCR_GTSE,
+ ((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) |
+ ((flags & FLAG_GTSE) ? LPCR_GTSE : 0));
}
if (kvm_enabled()) {
ret = s390_set_memory_limit(machine->maxram_size, &hw_limit);
if (ret == -E2BIG) {
- error_setg(&err, "qemu: host supports a maximum of %" PRIu64 " GB",
+ error_setg(&err, "host supports a maximum of %" PRIu64 " GB",
hw_limit >> 30);
} else if (ret) {
- error_setg(&err, "qemu: setting the guest size failed");
+ error_setg(&err, "setting the guest size failed");
}
out:
NULL, 0,
EM_TRICORE, 1, 0);
if (kernel_size <= 0) {
- error_report("qemu: no kernel file '%s'",
+ error_report("no kernel file '%s'",
tricoretb_binfo.kernel_filename);
exit(1);
}
if (p->b.bError) {
DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError);
}
- memcpy(p->abData, data, len);
+ if (len) {
+ g_assert_nonnull(data);
+ memcpy(p->abData, data, len);
+ }
ccid_reset_error_status(s);
usb_wakeup(s->bulk, 0);
}
*/
static inline void aio_enable_external(AioContext *ctx)
{
- assert(ctx->external_disable_cnt > 0);
- atomic_dec(&ctx->external_disable_cnt);
+ int old;
+
+ old = atomic_fetch_dec(&ctx->external_disable_cnt);
+ assert(old > 0);
+ if (old == 1) {
+ /* Kick event loop so it re-arms file descriptors */
+ aio_notify(ctx);
+ }
}
/**
#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
#define BDRV_OPT_READ_ONLY "read-only"
#define BDRV_OPT_DISCARD "discard"
+#define BDRV_OPT_FORCE_SHARE "force-share"
#define BDRV_SECTOR_BITS 9
#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS)
/*
- * Allocation status flags
- * BDRV_BLOCK_DATA: data is read from a file returned by bdrv_get_block_status.
- * BDRV_BLOCK_ZERO: sectors read as zero
- * BDRV_BLOCK_OFFSET_VALID: sector stored as raw data in a file returned by
- * bdrv_get_block_status.
+ * Allocation status flags for bdrv_get_block_status() and friends.
+ *
+ * Public flags:
+ * BDRV_BLOCK_DATA: allocation for data at offset is tied to this layer
+ * BDRV_BLOCK_ZERO: offset reads as zero
+ * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data
* BDRV_BLOCK_ALLOCATED: the content of the block is determined by this
- * layer (as opposed to the backing file)
- * BDRV_BLOCK_RAW: used internally to indicate that the request
- * was answered by the raw driver and that one
- * should look in bs->file directly.
+ * layer (short for DATA || ZERO), set by block layer
*
- * If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 represent the offset in
- * bs->file where sector data can be read from as raw data.
+ * Internal flag:
+ * BDRV_BLOCK_RAW: used internally to indicate that the request was
+ * answered by a passthrough driver such as raw and that the
+ * block layer should recompute the answer from bs->file.
*
- * DATA == 0 && ZERO == 0 means that data is read from backing_hd if present.
+ * If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 (BDRV_BLOCK_OFFSET_MASK)
+ * represent the offset in the returned BDS that is allocated for the
+ * corresponding raw data; however, whether that offset actually contains
+ * data also depends on BDRV_BLOCK_DATA and BDRV_BLOCK_ZERO, as follows:
*
* DATA ZERO OFFSET_VALID
- * t t t sectors read as zero, bs->file is zero at offset
- * t f t sectors read as valid from bs->file at offset
- * f t t sectors preallocated, read as zero, bs->file not
+ * t t t sectors read as zero, returned file is zero at offset
+ * t f t sectors read as valid from file at offset
+ * f t t sectors preallocated, read as zero, returned file not
* necessarily zero at offset
* f f t sectors preallocated but read from backing_hd,
- * bs->file contains garbage at offset
+ * returned file contains garbage at offset
* t t f sectors preallocated, read as zero, unknown offset
* t f f sectors read from unknown file or offset
* f t f not allocated or unknown offset, read as zero
BLK_PERM_ALL = 0x1f,
};
+char *bdrv_perm_names(uint64_t perm);
+
/* disk I/O throttling */
void bdrv_init(void);
void bdrv_init_with_whitelist(void);
void bdrv_invalidate_cache_all(Error **errp);
int bdrv_inactivate_all(void);
-void blk_resume_after_migration(Error **errp);
-
/* Ensure contents are flushed to disk. */
int bdrv_flush(BlockDriverState *bs);
int coroutine_fn bdrv_co_flush(BlockDriverState *bs);
int64_t sector_num, int nb_sectors, int *pnum);
bool bdrv_is_read_only(BlockDriverState *bs);
+bool bdrv_is_writable(BlockDriverState *bs);
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
bool bdrv_is_sg(BlockDriverState *bs);
int64_t offset, int count, BdrvRequestFlags flags);
int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs,
int64_t offset, int count);
+
+ /*
+ * Building block for bdrv_block_status[_above]. The driver should
+ * answer only according to the current layer, and should not
+ * set BDRV_BLOCK_ALLOCATED, but may set BDRV_BLOCK_RAW. See block.h
+ * for the meaning of _DATA, _ZERO, and _OFFSET_VALID.
+ */
int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum,
BlockDriverState **file);
void (*drained_begin)(BdrvChild *child);
void (*drained_end)(BdrvChild *child);
+ /* Notifies the parent that the child has been activated/inactivated (e.g.
+ * when migration is completing) and it can start/stop requesting
+ * permissions and doing I/O on it. */
+ void (*activate)(BdrvChild *child, Error **errp);
+ int (*inactivate)(BdrvChild *child);
+
void (*attach)(BdrvChild *child);
void (*detach)(BdrvChild *child);
};
bool valid_key; /* if true, a valid encryption key has been set */
bool sg; /* if true, the device is a /dev/sg* */
bool probed; /* if true, format was probed rather than specified */
+ bool force_share; /* if true, always allow all shared permissions */
BlockDriver *drv; /* NULL means no media */
void *opaque;
* and QCryptoBlockOpenOptions in qapi/crypto.json */
typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block,
- void *opaque,
size_t offset,
uint8_t *buf,
size_t buflen,
+ void *opaque,
Error **errp);
typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block,
- void *opaque,
size_t headerlen,
+ void *opaque,
Error **errp);
typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block,
- void *opaque,
size_t offset,
const uint8_t *buf,
size_t buflen,
+ void *opaque,
Error **errp);
/**
size_t buflen,
Error **errp);
+/**
+ * qcrypto_random_init:
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Initializes the handles used by qcrypto_random_bytes
+ *
+ * Returns 0 on success, -1 on error
+ */
+int qcrypto_random_init(Error **errp);
#endif /* QCRYPTO_RANDOM_H */
void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
uint64_t len, int node, MemoryAffinityFlags flags);
+void build_slit(GArray *table_data, BIOSLinker *linker);
#endif
MachineClass *find_default_machine(void);
extern MachineState *current_machine;
+void machine_run_board_init(MachineState *machine);
bool machine_usb(MachineState *machine);
bool machine_kernel_irqchip_allowed(MachineState *machine);
bool machine_kernel_irqchip_required(MachineState *machine);
bool machine_mem_merge(MachineState *machine);
void machine_register_compat_props(MachineState *machine);
HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine);
+void machine_set_cpu_numa_node(MachineState *machine,
+ const CpuInstanceProperties *props,
+ Error **errp);
/**
* CPUArchId:
* of HotplugHandler object, which handles hotplug operation
* for a given @dev. It may return NULL if @dev doesn't require
* any actions to be performed by hotplug handler.
- * @cpu_index_to_socket_id:
+ * @cpu_index_to_instance_props:
+ * used to provide @cpu_index to socket/core/thread number mapping, allowing
+ * legacy code to perform maping from cpu_index to topology properties
+ * Returns: tuple of socket/core/thread ids given cpu_index belongs to.
* used to provide @cpu_index to socket number mapping, allowing
* a machine to group CPU threads belonging to the same socket/package
* Returns: socket number given cpu_index belongs to.
int minimum_page_bits;
bool has_hotpluggable_cpus;
int numa_mem_align_shift;
+ void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,
+ int nb_nodes, ram_addr_t size);
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
DeviceState *dev);
- unsigned (*cpu_index_to_socket_id)(unsigned cpu_index);
+ CpuInstanceProperties (*cpu_index_to_instance_props)(MachineState *machine,
+ unsigned cpu_index);
const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
};
#include "hw/boards.h"
#include "hw/sysbus.h"
+#include "hw/ipmi/ipmi.h"
#include "hw/ppc/pnv_lpc.h"
#include "hw/ppc/pnv_psi.h"
#include "hw/ppc/pnv_occ.h"
#define POWERNV_MACHINE(obj) \
OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
-typedef struct IPMIBmc IPMIBmc;
-
typedef struct PnvMachineState {
/*< private >*/
MachineState parent_obj;
#ifndef _PPC_PNV_LPC_H
#define _PPC_PNV_LPC_H
+#include "hw/ppc/pnv_psi.h"
+
#define TYPE_PNV_LPC "pnv-lpc"
#define PNV_LPC(obj) \
OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
-typedef struct PnvPsi PnvPsi;
-
typedef struct PnvLpcController {
DeviceState parent;
#ifndef _PPC_PNV_OCC_H
#define _PPC_PNV_OCC_H
+#include "hw/ppc/pnv_psi.h"
+
#define TYPE_PNV_OCC "pnv-occ"
#define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC)
-typedef struct PnvPsi PnvPsi;
-
typedef struct PnvOCC {
DeviceState xd;
/*< public >*/
void *threads;
+ int node_id;
} sPAPRCPUCore;
typedef struct sPAPRCPUCoreClass {
#define XICS_H
#include "hw/qdev.h"
+#include "target/ppc/cpu-qom.h"
#define XICS_IPI 0x2
#define XICS_BUID 0x1
typedef struct ICSState ICSState;
typedef struct ICSIRQState ICSIRQState;
typedef struct XICSFabric XICSFabric;
-typedef struct PowerPCCPU PowerPCCPU;
#define TYPE_ICP "icp"
#define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP)
#define XICS_STATUS_SENT 0x2
#define XICS_STATUS_REJECTED 0x4
#define XICS_STATUS_MASKED_PENDING 0x8
+#define XICS_STATUS_PRESENTED 0x10
+#define XICS_STATUS_QUEUED 0x20
uint8_t status;
/* (flags & XICS_FLAGS_IRQ_MASK) == 0 means the interrupt is not allocated */
#define XICS_FLAGS_IRQ_LSI 0x1
struct virtio_gpu_ctrl_command *cmd);
void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
void virtio_gpu_virgl_reset(VirtIOGPU *g);
+void virtio_gpu_gl_block(void *opaque, bool block);
int virtio_gpu_virgl_init(VirtIOGPU *g);
#endif
/**
* qio_channel_file_new_path:
- * @fd: the file descriptor
+ * @path: the file path
* @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc)
* @mode: the file creation mode if O_WRONLY is set in @flags
* @errp: pointer to initialized error object
Error **errp);
/**
- * qio_channel_writev:
+ * qio_channel_write:
* @ioc: the channel object
* @buf: the memory regions to send data from
* @buflen: the length of @buf
return find_next_zero_bit(addr, size, 0);
}
-static inline unsigned long hweight_long(unsigned long w)
-{
- unsigned long count;
-
- for (count = 0; w; w >>= 1) {
- count += w & 1;
- }
- return count;
-}
-
/**
* rol8 - rotate an 8-bit value left
* @word: value to rotate
#ifndef _WIN32
int qemu_dup(int fd);
#endif
+int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive);
+int qemu_unlock_fd(int fd, int64_t start, int64_t len);
+int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive);
#if defined(__HAIKU__) && defined(__i386__)
#define FMT_pid "%ld"
typedef struct uWireSlave uWireSlave;
typedef struct VirtIODevice VirtIODevice;
typedef struct Visitor Visitor;
+typedef struct node_info NodeInfo;
#endif /* QEMU_TYPEDEFS_H */
struct qemu_work_item;
+#define CPU_UNSET_NUMA_NODE_ID -1
+
/**
* CPUState:
* @cpu_index: CPU index (informative).
#include "hw/boards.h"
extern int nb_numa_nodes; /* Number of NUMA nodes */
+extern bool have_numa_distance;
struct numa_addr_range {
ram_addr_t mem_start;
QLIST_ENTRY(numa_addr_range) entry;
};
-typedef struct node_info {
+struct node_info {
uint64_t node_mem;
- unsigned long *node_cpu;
struct HostMemoryBackend *node_memdev;
bool present;
QLIST_HEAD(, numa_addr_range) addr; /* List to store address ranges */
-} NodeInfo;
+ uint8_t distance[MAX_NODES];
+};
extern NodeInfo numa_info[MAX_NODES];
-void parse_numa_opts(MachineClass *mc);
-void numa_post_machine_init(void);
+void parse_numa_opts(MachineState *ms);
void query_numa_node_mem(uint64_t node_mem[]);
extern QemuOptsList qemu_numa_opts;
void numa_set_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node);
void numa_unset_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node);
uint32_t numa_get_node(ram_addr_t addr, Error **errp);
-
-/* on success returns node index in numa_info,
- * on failure returns nb_numa_nodes */
-int numa_get_node_for_cpu(int idx);
+void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
+ int nb_nodes, ram_addr_t size);
+void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
+ int nb_nodes, ram_addr_t size);
#endif
#define MAX_NODES 128
#define NUMA_NODE_UNASSIGNED MAX_NODES
+#define NUMA_DISTANCE_MIN 10
+#define NUMA_DISTANCE_DEFAULT 20
+#define NUMA_DISTANCE_MAX 254
+#define NUMA_DISTANCE_UNREACHABLE 255
#define MAX_OPTION_ROMS 16
typedef struct QEMUOptionRom {
}
#endif
+/* egl-headless.c */
+void egl_headless_init(void);
+
#endif
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win);
-int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug);
+int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy);
+int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy);
EGLContext qemu_egl_init_ctx(void);
#endif /* EGL_HELPERS_H */
#define INPUT_EVENT_MASK_REL (1<<INPUT_EVENT_KIND_REL)
#define INPUT_EVENT_MASK_ABS (1<<INPUT_EVENT_KIND_ABS)
-#define INPUT_EVENT_ABS_SIZE 0x8000
+#define INPUT_EVENT_ABS_MIN 0x0000
+#define INPUT_EVENT_ABS_MAX 0x7FFF
typedef struct QemuInputHandler QemuInputHandler;
typedef struct QemuInputHandlerState QemuInputHandlerState;
uint32_t button_old, uint32_t button_new);
bool qemu_input_is_absolute(void);
-int qemu_input_scale_axis(int value, int size_in, int size_out);
+int qemu_input_scale_axis(int value,
+ int min_in, int max_in,
+ int min_out, int max_out);
InputEvent *qemu_input_event_new_move(InputEventKind kind,
InputAxis axis, int value);
void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value);
-void qemu_input_queue_abs(QemuConsole *src, InputAxis axis,
- int value, int size);
+void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value,
+ int min_in, int max_in);
void qemu_input_check_mode_change(void);
void qemu_add_mouse_mode_change_notifier(Notifier *notify);
qemu_init_cpu_list();
module_call_init(MODULE_INIT_QOM);
- if ((envlist = envlist_create()) == NULL) {
- (void) fprintf(stderr, "Unable to allocate envlist\n");
- exit(EXIT_FAILURE);
- }
+ envlist = envlist_create();
/* add current environment into the list */
for (wrk = environ; *wrk != NULL; wrk++) {
}
for (wrk = target_environ; *wrk; wrk++) {
- free(*wrk);
+ g_free(*wrk);
}
- free(target_environ);
+ g_free(target_environ);
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
qemu_log("guest_base 0x%lx\n", guest_base);
Error *local_err = NULL;
MigrationIncomingState *mis = opaque;
- /* Make sure all file formats flush their mutable metadata */
+ /* Make sure all file formats flush their mutable metadata.
+ * If we get an error here, just don't restart the VM yet. */
bdrv_invalidate_cache_all(&local_err);
if (local_err) {
- migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
- MIGRATION_STATUS_FAILED);
error_report_err(local_err);
- migrate_decompress_threads_join();
- exit(EXIT_FAILURE);
- }
-
- /* If we get an error here, just don't restart the VM yet. */
- blk_resume_after_migration(&local_err);
- if (local_err) {
- error_free(local_err);
local_err = NULL;
autostart = false;
}
qemu_announce_self();
- /* Make sure all file formats flush their mutable metadata */
+ /* Make sure all file formats flush their mutable metadata.
+ * If we get an error here, just don't restart the VM yet. */
bdrv_invalidate_cache_all(&local_err);
if (local_err) {
error_report_err(local_err);
- }
-
- /* If we get an error here, just don't restart the VM yet. */
- blk_resume_after_migration(&local_err);
- if (local_err) {
- error_free(local_err);
local_err = NULL;
autostart = false;
}
* For all nodes, nodeid < max_numa_nodeid
*/
int nb_numa_nodes;
+bool have_numa_distance;
NodeInfo numa_info[MAX_NODES];
void numa_set_mem_node_id(ram_addr_t addr, uint64_t size, uint32_t node)
return -1;
}
-static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
+static void parse_numa_node(MachineState *ms, NumaNodeOptions *node,
+ QemuOpts *opts, Error **errp)
{
uint16_t nodenr;
uint16List *cpus = NULL;
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
if (node->has_nodeid) {
nodenr = node->nodeid;
return;
}
+ if (!mc->cpu_index_to_instance_props) {
+ error_report("NUMA is not supported by this machine-type");
+ exit(1);
+ }
for (cpus = node->cpus; cpus; cpus = cpus->next) {
+ CpuInstanceProperties props;
if (cpus->value >= max_cpus) {
error_setg(errp,
"CPU index (%" PRIu16 ")"
cpus->value, max_cpus);
return;
}
- bitmap_set(numa_info[nodenr].node_cpu, cpus->value, 1);
+ props = mc->cpu_index_to_instance_props(ms, cpus->value);
+ props.node_id = nodenr;
+ props.has_node_id = true;
+ machine_set_cpu_numa_node(ms, &props, &error_fatal);
}
if (node->has_mem && node->has_memdev) {
- error_setg(errp, "qemu: cannot specify both mem= and memdev=");
+ error_setg(errp, "cannot specify both mem= and memdev=");
return;
}
have_memdevs = node->has_memdev;
}
if (node->has_memdev != have_memdevs) {
- error_setg(errp, "qemu: memdev option must be specified for either "
+ error_setg(errp, "memdev option must be specified for either "
"all or no nodes");
return;
}
max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1);
}
+static void parse_numa_distance(NumaDistOptions *dist, Error **errp)
+{
+ uint16_t src = dist->src;
+ uint16_t dst = dist->dst;
+ uint8_t val = dist->val;
+
+ if (src >= MAX_NODES || dst >= MAX_NODES) {
+ error_setg(errp,
+ "Invalid node %" PRIu16
+ ", max possible could be %" PRIu16,
+ MAX(src, dst), MAX_NODES);
+ return;
+ }
+
+ if (!numa_info[src].present || !numa_info[dst].present) {
+ error_setg(errp, "Source/Destination NUMA node is missing. "
+ "Please use '-numa node' option to declare it first.");
+ return;
+ }
+
+ if (val < NUMA_DISTANCE_MIN) {
+ error_setg(errp, "NUMA distance (%" PRIu8 ") is invalid, "
+ "it shouldn't be less than %d.",
+ val, NUMA_DISTANCE_MIN);
+ return;
+ }
+
+ if (src == dst && val != NUMA_DISTANCE_MIN) {
+ error_setg(errp, "Local distance of node %d should be %d.",
+ src, NUMA_DISTANCE_MIN);
+ return;
+ }
+
+ numa_info[src].distance[dst] = val;
+ have_numa_distance = true;
+}
+
static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
{
NumaOptions *object = NULL;
+ MachineState *ms = opaque;
Error *err = NULL;
{
switch (object->type) {
case NUMA_OPTIONS_TYPE_NODE:
- numa_node_parse(&object->u.node, opts, &err);
+ parse_numa_node(ms, &object->u.node, opts, &err);
if (err) {
goto end;
}
nb_numa_nodes++;
break;
+ case NUMA_OPTIONS_TYPE_DIST:
+ parse_numa_distance(&object->u.dist, &err);
+ if (err) {
+ goto end;
+ }
+ break;
+ case NUMA_OPTIONS_TYPE_CPU:
+ if (!object->u.cpu.has_node_id) {
+ error_setg(&err, "Missing mandatory node-id property");
+ goto end;
+ }
+ if (!numa_info[object->u.cpu.node_id].present) {
+ error_setg(&err, "Invalid node-id=%" PRId64 ", NUMA node must be "
+ "defined with -numa node,nodeid=ID before it's used with "
+ "-numa cpu,node-id=ID", object->u.cpu.node_id);
+ goto end;
+ }
+
+ machine_set_cpu_numa_node(ms, qapi_NumaCpuOptions_base(&object->u.cpu),
+ &err);
+ break;
default:
abort();
}
return 0;
}
-static char *enumerate_cpus(unsigned long *cpus, int max_cpus)
+/* If all node pair distances are symmetric, then only distances
+ * in one direction are enough. If there is even one asymmetric
+ * pair, though, then all distances must be provided. The
+ * distance from a node to itself is always NUMA_DISTANCE_MIN,
+ * so providing it is never necessary.
+ */
+static void validate_numa_distance(void)
{
- int cpu;
- bool first = true;
- GString *s = g_string_new(NULL);
+ int src, dst;
+ bool is_asymmetrical = false;
+
+ for (src = 0; src < nb_numa_nodes; src++) {
+ for (dst = src; dst < nb_numa_nodes; dst++) {
+ if (numa_info[src].distance[dst] == 0 &&
+ numa_info[dst].distance[src] == 0) {
+ if (src != dst) {
+ error_report("The distance between node %d and %d is "
+ "missing, at least one distance value "
+ "between each nodes should be provided.",
+ src, dst);
+ exit(EXIT_FAILURE);
+ }
+ }
- for (cpu = find_first_bit(cpus, max_cpus);
- cpu < max_cpus;
- cpu = find_next_bit(cpus, max_cpus, cpu + 1)) {
- g_string_append_printf(s, "%s%d", first ? "" : " ", cpu);
- first = false;
+ if (numa_info[src].distance[dst] != 0 &&
+ numa_info[dst].distance[src] != 0 &&
+ numa_info[src].distance[dst] !=
+ numa_info[dst].distance[src]) {
+ is_asymmetrical = true;
+ }
+ }
+ }
+
+ if (is_asymmetrical) {
+ for (src = 0; src < nb_numa_nodes; src++) {
+ for (dst = 0; dst < nb_numa_nodes; dst++) {
+ if (src != dst && numa_info[src].distance[dst] == 0) {
+ error_report("At least one asymmetrical pair of "
+ "distances is given, please provide distances "
+ "for both directions of all node pairs.");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
}
- return g_string_free(s, FALSE);
}
-static void validate_numa_cpus(void)
+static void complete_init_numa_distance(void)
{
- int i;
- unsigned long *seen_cpus = bitmap_new(max_cpus);
+ int src, dst;
- for (i = 0; i < nb_numa_nodes; i++) {
- if (bitmap_intersects(seen_cpus, numa_info[i].node_cpu, max_cpus)) {
- bitmap_and(seen_cpus, seen_cpus,
- numa_info[i].node_cpu, max_cpus);
- error_report("CPU(s) present in multiple NUMA nodes: %s",
- enumerate_cpus(seen_cpus, max_cpus));
- g_free(seen_cpus);
- exit(EXIT_FAILURE);
+ /* Fixup NUMA distance by symmetric policy because if it is an
+ * asymmetric distance table, it should be a complete table and
+ * there would not be any missing distance except local node, which
+ * is verified by validate_numa_distance above.
+ */
+ for (src = 0; src < nb_numa_nodes; src++) {
+ for (dst = 0; dst < nb_numa_nodes; dst++) {
+ if (numa_info[src].distance[dst] == 0) {
+ if (src == dst) {
+ numa_info[src].distance[dst] = NUMA_DISTANCE_MIN;
+ } else {
+ numa_info[src].distance[dst] = numa_info[dst].distance[src];
+ }
+ }
}
- bitmap_or(seen_cpus, seen_cpus,
- numa_info[i].node_cpu, max_cpus);
}
+}
- if (!bitmap_full(seen_cpus, max_cpus)) {
- char *msg;
- bitmap_complement(seen_cpus, seen_cpus, max_cpus);
- msg = enumerate_cpus(seen_cpus, max_cpus);
- error_report("warning: CPU(s) not present in any NUMA nodes: %s", msg);
- error_report("warning: All CPU(s) up to maxcpus should be described "
- "in NUMA config");
- g_free(msg);
+void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
+ int nb_nodes, ram_addr_t size)
+{
+ int i;
+ uint64_t usedmem = 0;
+
+ /* Align each node according to the alignment
+ * requirements of the machine class
+ */
+
+ for (i = 0; i < nb_nodes - 1; i++) {
+ nodes[i].node_mem = (size / nb_nodes) &
+ ~((1 << mc->numa_mem_align_shift) - 1);
+ usedmem += nodes[i].node_mem;
}
- g_free(seen_cpus);
+ nodes[i].node_mem = size - usedmem;
}
-void parse_numa_opts(MachineClass *mc)
+void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes,
+ int nb_nodes, ram_addr_t size)
{
int i;
+ uint64_t usedmem = 0, node_mem;
+ uint64_t granularity = size / nb_nodes;
+ uint64_t propagate = 0;
+
+ for (i = 0; i < nb_nodes - 1; i++) {
+ node_mem = (granularity + propagate) &
+ ~((1 << mc->numa_mem_align_shift) - 1);
+ propagate = granularity + propagate - node_mem;
+ nodes[i].node_mem = node_mem;
+ usedmem += node_mem;
+ }
+ nodes[i].node_mem = size - usedmem;
+}
- for (i = 0; i < MAX_NODES; i++) {
- numa_info[i].node_cpu = bitmap_new(max_cpus);
- }
+void parse_numa_opts(MachineState *ms)
+{
+ int i;
+ const CPUArchIdList *possible_cpus;
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
- if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, NULL, NULL)) {
+ if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) {
exit(1);
}
}
}
if (i == nb_numa_nodes) {
- uint64_t usedmem = 0;
-
- /* Align each node according to the alignment
- * requirements of the machine class
- */
- for (i = 0; i < nb_numa_nodes - 1; i++) {
- numa_info[i].node_mem = (ram_size / nb_numa_nodes) &
- ~((1 << mc->numa_mem_align_shift) - 1);
- usedmem += numa_info[i].node_mem;
- }
- numa_info[i].node_mem = ram_size - usedmem;
+ assert(mc->numa_auto_assign_ram);
+ mc->numa_auto_assign_ram(mc, numa_info, nb_numa_nodes, ram_size);
}
numa_total = 0;
numa_set_mem_ranges();
- for (i = 0; i < nb_numa_nodes; i++) {
- if (!bitmap_empty(numa_info[i].node_cpu, max_cpus)) {
+ /* assign CPUs to nodes using board provided default mapping */
+ if (!mc->cpu_index_to_instance_props || !mc->possible_cpu_arch_ids) {
+ error_report("default CPUs to NUMA node mapping isn't supported");
+ exit(1);
+ }
+
+ possible_cpus = mc->possible_cpu_arch_ids(ms);
+ for (i = 0; i < possible_cpus->len; i++) {
+ if (possible_cpus->cpus[i].props.has_node_id) {
break;
}
}
- /* Historically VCPUs were assigned in round-robin order to NUMA
- * nodes. However it causes issues with guest not handling it nice
- * in case where cores/threads from a multicore CPU appear on
- * different nodes. So allow boards to override default distribution
- * rule grouping VCPUs by socket so that VCPUs from the same socket
- * would be on the same node.
- */
- if (i == nb_numa_nodes) {
+
+ /* no CPUs are assigned to NUMA nodes */
+ if (i == possible_cpus->len) {
for (i = 0; i < max_cpus; i++) {
- unsigned node_id = i % nb_numa_nodes;
- if (mc->cpu_index_to_socket_id) {
- node_id = mc->cpu_index_to_socket_id(i) % nb_numa_nodes;
- }
+ CpuInstanceProperties props;
+ /* fetch default mapping from board and enable it */
+ props = mc->cpu_index_to_instance_props(ms, i);
+ props.has_node_id = true;
- set_bit(i, numa_info[node_id].node_cpu);
+ machine_set_cpu_numa_node(ms, &props, &error_fatal);
}
}
- validate_numa_cpus();
- } else {
- numa_set_mem_node_id(0, ram_size, 0);
- }
-}
-
-void numa_post_machine_init(void)
-{
- CPUState *cpu;
- int i;
+ /* QEMU needs at least all unique node pair distances to build
+ * the whole NUMA distance table. QEMU treats the distance table
+ * as symmetric by default, i.e. distance A->B == distance B->A.
+ * Thus, QEMU is able to complete the distance table
+ * initialization even though only distance A->B is provided and
+ * distance B->A is not. QEMU knows the distance of a node to
+ * itself is always 10, so A->A distances may be omitted. When
+ * the distances of two nodes of a pair differ, i.e. distance
+ * A->B != distance B->A, then that means the distance table is
+ * asymmetric. In this case, the distances for both directions
+ * of all node pairs are required.
+ */
+ if (have_numa_distance) {
+ /* Validate enough NUMA distance information was provided. */
+ validate_numa_distance();
- CPU_FOREACH(cpu) {
- for (i = 0; i < nb_numa_nodes; i++) {
- assert(cpu->cpu_index < max_cpus);
- if (test_bit(cpu->cpu_index, numa_info[i].node_cpu)) {
- cpu->numa_node = i;
- }
+ /* Validation succeeded, now fill in any missing distances. */
+ complete_init_numa_distance();
}
+ } else {
+ numa_set_mem_node_id(0, ram_size, 0);
}
}
return list;
}
-int numa_get_node_for_cpu(int idx)
-{
- int i;
-
- assert(idx < max_cpus);
-
- for (i = 0; i < nb_numa_nodes; i++) {
- if (test_bit(idx, numa_info[i].node_cpu)) {
- break;
- }
- }
- return i;
-}
-
void ram_block_notifier_add(RAMBlockNotifier *n)
{
QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next);
(OpenPower Abstraction Layer) firmware for OpenPOWER systems. It can
run an hypervisor OS or simply a host OS on the "baremetal"
platform, also known as the PowerNV (Non-Virtualized) platform.
+
+- QemuMacDrivers (https://github.com/ozbenh/QemuMacDrivers) is a project to
+ provide virtualised drivers for PPC MacOS guests.
#
# @thread_id: ID of the underlying host thread
#
+# @props: properties describing to which node/socket/core/thread
+# virtual CPU belongs to, provided if supported by board (since 2.10)
+#
# @arch: architecture of the cpu, which determines which additional fields
# will be listed (since 2.6)
#
##
{ 'union': 'CpuInfo',
'base': {'CPU': 'int', 'current': 'bool', 'halted': 'bool',
- 'qom_path': 'str', 'thread_id': 'int', 'arch': 'CpuInfoArch' },
+ 'qom_path': 'str', 'thread_id': 'int',
+ '*props': 'CpuInstanceProperties', 'arch': 'CpuInfoArch' },
'discriminator': 'arch',
'data': { 'x86': 'CpuInfoX86',
'sparc': 'CpuInfoSPARC',
##
# @NumaOptionsType:
#
+# @node: NUMA nodes configuration
+#
+# @dist: NUMA distance configuration (since 2.10)
+#
+# @cpu: property based CPU(s) to node mapping (Since: 2.10)
+#
# Since: 2.1
##
{ 'enum': 'NumaOptionsType',
- 'data': [ 'node' ] }
+ 'data': [ 'node', 'dist', 'cpu' ] }
##
# @NumaOptions:
'base': { 'type': 'NumaOptionsType' },
'discriminator': 'type',
'data': {
- 'node': 'NumaNodeOptions' }}
+ 'node': 'NumaNodeOptions',
+ 'dist': 'NumaDistOptions',
+ 'cpu': 'NumaCpuOptions' }}
##
# @NumaNodeOptions:
'*mem': 'size',
'*memdev': 'str' }}
+##
+# @NumaDistOptions:
+#
+# Set the distance between 2 NUMA nodes.
+#
+# @src: source NUMA node.
+#
+# @dst: destination NUMA node.
+#
+# @val: NUMA distance from source node to destination node.
+# When a node is unreachable from another node, set the distance
+# between them to 255.
+#
+# Since: 2.10
+##
+{ 'struct': 'NumaDistOptions',
+ 'data': {
+ 'src': 'uint16',
+ 'dst': 'uint16',
+ 'val': 'uint8' }}
+
+##
+# @NumaCpuOptions:
+#
+# Option "-numa cpu" overrides default cpu to node mapping.
+# It accepts the same set of cpu properties as returned by
+# query-hotpluggable-cpus[].props, where node-id could be used to
+# override default node mapping.
+#
+# Since: 2.10
+##
+{ 'struct': 'NumaCpuOptions',
+ 'base': 'CpuInstanceProperties',
+ 'data' : {} }
+
##
# @HostMemPolicy:
#
#
# @filename: path to the image file
# @aio: AIO backend (default: threads) (since: 2.8)
+# @locking: whether to enable file locking. If set to 'auto', only enable
+# when Open File Descriptor (OFD) locking API is available
+# (default: auto, since 2.10)
#
# Since: 2.9
##
{ 'struct': 'BlockdevOptionsFile',
'data': { 'filename': 'str',
+ '*locking': 'OnOffAuto',
'*aio': 'BlockdevAioOptions' } }
##
#
# @config: filename of the configuration file
#
-# @align: required alignment for requests in bytes,
-# must be power of 2, or 0 for default
+# @align: required alignment for requests in bytes, must be
+# positive power of 2, or 0 for default
+#
+# @max-transfer: maximum size for I/O transfers in bytes, must be
+# positive multiple of @align and of the underlying
+# file's request alignment (but need not be a power of
+# 2), or 0 for default (since 2.10)
+#
+# @opt-write-zero: preferred alignment for write zero requests in bytes,
+# must be positive multiple of @align and of the
+# underlying file's request alignment (but need not be a
+# power of 2), or 0 for default (since 2.10)
+#
+# @max-write-zero: maximum size for write zero requests in bytes, must be
+# positive multiple of @align, of @opt-write-zero, and of
+# the underlying file's request alignment (but need not
+# be a power of 2), or 0 for default (since 2.10)
+#
+# @opt-discard: preferred alignment for discard requests in bytes, must
+# be positive multiple of @align and of the underlying
+# file's request alignment (but need not be a power of
+# 2), or 0 for default (since 2.10)
+#
+# @max-discard: maximum size for discard requests in bytes, must be
+# positive multiple of @align, of @opt-discard, and of
+# the underlying file's request alignment (but need not
+# be a power of 2), or 0 for default (since 2.10)
#
# @inject-error: array of error injection descriptions
#
{ 'struct': 'BlockdevOptionsBlkdebug',
'data': { 'image': 'BlockdevRef',
'*config': 'str',
- '*align': 'int',
+ '*align': 'int', '*max-transfer': 'int32',
+ '*opt-write-zero': 'int32', '*max-write-zero': 'int32',
+ '*opt-discard': 'int32', '*max-discard': 'int32',
'*inject-error': ['BlkdebugInjectErrorOptions'],
'*set-state': ['BlkdebugSetStateOptions'] } }
# (default: false)
# @detect-zeroes: detect and optimize zero writes (Since 2.1)
# (default: off)
+# @force-share: force share all permission on added nodes.
+# Requires read-only=true. (Since 2.10)
#
# Remaining options are determined by the block driver.
#
'*discard': 'BlockdevDiscardOptions',
'*cache': 'BlockdevCacheOptions',
'*read-only': 'bool',
+ '*force-share': 'bool',
'*detect-zeroes': 'BlockdevDetectZeroesOptions' },
'discriminator': 'driver',
'data': {
unprivileged user, an environment variable SASL_CONF_PATH can be used
to make it search alternate locations for the service config.
-The default configuration might contain
+If the TLS option is enabled for VNC, then it will provide session encryption,
+otherwise the SASL mechanism will have to provide encryption. In the latter
+case the list of possible plugins that can be used is drastically reduced. In
+fact only the GSSAPI SASL mechanism provides an acceptable level of security
+by modern standards. Previous versions of QEMU referred to the DIGEST-MD5
+mechanism, however, it has multiple serious flaws described in detail in
+RFC 6331 and thus should never be used any more. The SCRAM-SHA-1 mechanism
+provides a simple username/password auth facility similar to DIGEST-MD5, but
+does not support session encryption, so can only be used in combination with
+TLS.
+
+When not using TLS the recommended configuration is
@example
-mech_list: digest-md5
-sasldb_path: /etc/qemu/passwd.db
+mech_list: gssapi
+keytab: /etc/qemu/krb5.tab
@end example
-This says to use the 'Digest MD5' mechanism, which is similar to the HTTP
-Digest-MD5 mechanism. The list of valid usernames & passwords is maintained
-in the /etc/qemu/passwd.db file, and can be updated using the saslpasswd2
-command. While this mechanism is easy to configure and use, it is not
-considered secure by modern standards, so only suitable for developers /
-ad-hoc testing.
+This says to use the 'GSSAPI' mechanism with the Kerberos v5 protocol, with
+the server principal stored in /etc/qemu/krb5.tab. For this to work the
+administrator of your KDC must generate a Kerberos principal for the server,
+with a name of 'qemu/somehost.example.com@@EXAMPLE.COM' replacing
+'somehost.example.com' with the fully qualified host name of the machine
+running QEMU, and 'EXAMPLE.COM' with the Kerberos Realm.
-A more serious deployment might use Kerberos, which is done with the 'gssapi'
-mechanism
+When using TLS, if username+password authentication is desired, then a
+reasonable configuration is
@example
-mech_list: gssapi
-keytab: /etc/qemu/krb5.tab
+mech_list: scram-sha-1
+sasldb_path: /etc/qemu/passwd.db
@end example
-For this to work the administrator of your KDC must generate a Kerberos
-principal for the server, with a name of 'qemu/somehost.example.com@@EXAMPLE.COM'
-replacing 'somehost.example.com' with the fully qualified host name of the
-machine running QEMU, and 'EXAMPLE.COM' with the Kerberos Realm.
+The saslpasswd2 program can be used to populate the passwd.db file with
+accounts.
-Other configurations will be left as an exercise for the reader. It should
-be noted that only Digest-MD5 and GSSAPI provides a SSF layer for data
-encryption. For all other mechanisms, VNC should always be configured to
-use TLS and x509 certificates to protect security credentials from snooping.
+Other SASL configurations will be left as an exercise for the reader. Note that
+all mechanisms except GSSAPI, should be combined with use of TLS to ensure a
+secure data channel.
@node gdb_usage
@section GDB usage
Windows 9x does not correctly use the CPU HLT
instruction. The result is that it takes host CPU cycles even when
idle. You can install the utility from
-@url{http://www.user.cityline.ru/~maxamn/amnhltm.zip} to solve this
-problem. Note that no such tool is needed for NT, 2000 or XP.
+@url{http://web.archive.org/web/20060212132151/http://www.user.cityline.ru/~maxamn/amnhltm.zip}
+to solve this problem. Note that no such tool is needed for NT, 2000 or XP.
@subsubsection Windows 2000 disk full problem
@subsubsection CPU usage reduction
DOS does not correctly use the CPU HLT instruction. The result is that
-it takes host CPU cycles even when idle. You can install the utility
-from @url{http://www.vmware.com/software/dosidle210.zip} to solve this
-problem.
+it takes host CPU cycles even when idle. You can install the utility from
+@url{http://web.archive.org/web/20051222085335/http://www.vmware.com/software/dosidle210.zip}
+to solve this problem.
@node QEMU System emulator for non PC targets
@chapter QEMU System emulator for non PC targets
ETEXI
DEF("bench", img_bench,
- "bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] filename")
+ "bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename")
STEXI
-@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] @var{filename}
+@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename}
ETEXI
DEF("check", img_check,
- "check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] filename")
+ "check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename")
STEXI
-@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename}
+@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename}
ETEXI
DEF("create", img_create,
ETEXI
DEF("compare", img_compare,
- "compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] filename1 filename2")
+ "compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] [-U] filename1 filename2")
STEXI
-@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] @var{filename1} @var{filename2}
+@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2}
ETEXI
DEF("convert", img_convert,
- "convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
+ "convert [--object objectdef] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
STEXI
-@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
+@item convert [--object @var{objectdef}] [--image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("dd", img_dd,
- "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
+ "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
STEXI
-@item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
+@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
ETEXI
DEF("info", img_info,
- "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] filename")
+ "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename")
STEXI
-@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
+@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename}
ETEXI
DEF("map", img_map,
- "map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] filename")
+ "map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename")
STEXI
-@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
+@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename}
ETEXI
DEF("snapshot", img_snapshot,
- "snapshot [--object objectdef] [--image-opts] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
+ "snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
STEXI
-@item snapshot [--object @var{objectdef}] [--image-opts] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
+@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
ETEXI
DEF("rebase", img_rebase,
- "rebase [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
+ "rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
STEXI
-@item rebase [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
ETEXI
DEF("resize", img_resize,
#include "qapi/qobject-output-visitor.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qbool.h"
#include "qemu/cutils.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
static BlockBackend *img_open_opts(const char *optstr,
QemuOpts *opts, int flags, bool writethrough,
- bool quiet)
+ bool quiet, bool force_share)
{
QDict *options;
Error *local_err = NULL;
BlockBackend *blk;
options = qemu_opts_to_qdict(opts, NULL);
+ if (force_share) {
+ if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE)
+ && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) {
+ error_report("--force-share/-U conflicts with image options");
+ return NULL;
+ }
+ qdict_put(options, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true));
+ }
blk = blk_new_open(NULL, NULL, options, flags, &local_err);
if (!blk) {
error_reportf_err(local_err, "Could not open '%s': ", optstr);
static BlockBackend *img_open_file(const char *filename,
const char *fmt, int flags,
- bool writethrough, bool quiet)
+ bool writethrough, bool quiet,
+ bool force_share)
{
BlockBackend *blk;
Error *local_err = NULL;
- QDict *options = NULL;
+ QDict *options = qdict_new();
if (fmt) {
- options = qdict_new();
qdict_put_str(options, "driver", fmt);
}
+ if (force_share) {
+ qdict_put(options, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true));
+ }
blk = blk_new_open(filename, NULL, options, flags, &local_err);
if (!blk) {
error_reportf_err(local_err, "Could not open '%s': ", filename);
static BlockBackend *img_open(bool image_opts,
const char *filename,
const char *fmt, int flags, bool writethrough,
- bool quiet)
+ bool quiet, bool force_share)
{
BlockBackend *blk;
if (image_opts) {
if (!opts) {
return NULL;
}
- blk = img_open_opts(filename, opts, flags, writethrough, quiet);
+ blk = img_open_opts(filename, opts, flags, writethrough, quiet,
+ force_share);
} else {
- blk = img_open_file(filename, fmt, flags, writethrough, quiet);
+ blk = img_open_file(filename, fmt, flags, writethrough, quiet,
+ force_share);
}
return blk;
}
ImageCheck *check;
bool quiet = false;
bool image_opts = false;
+ bool force_share = false;
fmt = NULL;
output = NULL;
{"output", required_argument, 0, OPTION_OUTPUT},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:r:T:q",
+ c = getopt_long(argc, argv, ":hf:r:T:qU",
long_options, &option_index);
if (c == -1) {
break;
case 'q':
quiet = true;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OBJECT: {
QemuOpts *opts;
opts = qemu_opts_parse_noisily(&qemu_object_opts,
return 1;
}
- blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
+ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
+ force_share);
if (!blk) {
return 1;
}
return 1;
}
- blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
+ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
+ false);
if (!blk) {
return 1;
}
int c, pnum;
uint64_t progress_base;
bool image_opts = false;
+ bool force_share = false;
cache = BDRV_DEFAULT_CACHE;
for (;;) {
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:F:T:pqs",
+ c = getopt_long(argc, argv, ":hf:F:T:pqsU",
long_options, NULL);
if (c == -1) {
break;
case 's':
strict = true;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OBJECT: {
QemuOpts *opts;
opts = qemu_opts_parse_noisily(&qemu_object_opts,
goto out3;
}
- blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet);
+ blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet,
+ force_share);
if (!blk1) {
ret = 2;
goto out3;
}
- blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet);
+ blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet,
+ force_share);
if (!blk2) {
ret = 2;
goto out2;
qemu_co_mutex_lock(&s->lock);
if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) {
qemu_co_mutex_unlock(&s->lock);
- goto out;
+ break;
}
n = convert_iteration_sectors(s, s->sector_num);
if (n < 0) {
qemu_co_mutex_unlock(&s->lock);
s->ret = n;
- goto out;
+ break;
}
/* save current sector and allocation status to local variables */
sector_num = s->sector_num;
error_report("error while reading sector %" PRId64
": %s", sector_num, strerror(-ret));
s->ret = ret;
- goto out;
}
} else if (!s->min_sparse && status == BLK_ZERO) {
status = BLK_DATA;
if (s->wr_in_order) {
/* keep writes in order */
- while (s->wr_offs != sector_num) {
- if (s->ret != -EINPROGRESS) {
- goto out;
- }
+ while (s->wr_offs != sector_num && s->ret == -EINPROGRESS) {
s->wait_sector_num[index] = sector_num;
qemu_coroutine_yield();
}
s->wait_sector_num[index] = -1;
}
- ret = convert_co_write(s, sector_num, n, buf, status);
- if (ret < 0) {
- error_report("error while writing sector %" PRId64
- ": %s", sector_num, strerror(-ret));
- s->ret = ret;
- goto out;
+ if (s->ret == -EINPROGRESS) {
+ ret = convert_co_write(s, sector_num, n, buf, status);
+ if (ret < 0) {
+ error_report("error while writing sector %" PRId64
+ ": %s", sector_num, strerror(-ret));
+ s->ret = ret;
+ }
}
if (s->wr_in_order) {
}
}
-out:
qemu_vfree(buf);
s->co[index] = NULL;
s->running_coroutines--;
qemu_coroutine_enter(s->co[i]);
}
- while (s->ret == -EINPROGRESS) {
+ while (s->running_coroutines) {
main_loop_wait(false);
}
bool writethrough, src_writethrough, quiet = false, image_opts = false,
skip_create = false, progress = false;
int64_t ret = -EINVAL;
+ bool force_share = false;
ImgConvertState s = (ImgConvertState) {
/* Need at least 4k of zeros for sparse detection */
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:O:B:ce6o:s:l:S:pt:T:qnm:W",
+ c = getopt_long(argc, argv, ":hf:O:B:ce6o:s:l:S:pt:T:qnm:WU",
long_options, NULL);
if (c == -1) {
break;
case 'W':
s.wr_in_order = false;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OBJECT: {
QemuOpts *object_opts;
object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
for (bs_i = 0; bs_i < s.src_num; bs_i++) {
s.src[bs_i] = img_open(image_opts, argv[optind + bs_i],
- fmt, src_flags, src_writethrough, quiet);
+ fmt, src_flags, src_writethrough, quiet,
+ force_share);
if (!s.src[bs_i]) {
ret = -1;
goto out;
* the bdrv_create() call which takes different params.
* Not critical right now, so fix can wait...
*/
- s.target = img_open_file(out_filename, out_fmt, flags, writethrough, quiet);
+ s.target = img_open_file(out_filename, out_fmt, flags, writethrough, quiet,
+ false);
if (!s.target) {
ret = -1;
goto out;
static ImageInfoList *collect_image_info_list(bool image_opts,
const char *filename,
const char *fmt,
- bool chain)
+ bool chain, bool force_share)
{
ImageInfoList *head = NULL;
ImageInfoList **last = &head;
g_hash_table_insert(filenames, (gpointer)filename, NULL);
blk = img_open(image_opts, filename, fmt,
- BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false);
+ BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false,
+ force_share);
if (!blk) {
goto err;
}
const char *filename, *fmt, *output;
ImageInfoList *list;
bool image_opts = false;
+ bool force_share = false;
fmt = NULL;
output = NULL;
{"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":f:h",
+ c = getopt_long(argc, argv, ":f:hU",
long_options, &option_index);
if (c == -1) {
break;
case 'f':
fmt = optarg;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OUTPUT:
output = optarg;
break;
return 1;
}
- list = collect_image_info_list(image_opts, filename, fmt, chain);
+ list = collect_image_info_list(image_opts, filename, fmt, chain,
+ force_share);
if (!list) {
return 1;
}
MapEntry curr = { .length = 0 }, next;
int ret = 0;
bool image_opts = false;
+ bool force_share = false;
fmt = NULL;
output = NULL;
{"output", required_argument, 0, OPTION_OUTPUT},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":f:h",
+ c = getopt_long(argc, argv, ":f:hU",
long_options, &option_index);
if (c == -1) {
break;
case 'f':
fmt = optarg;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OUTPUT:
output = optarg;
break;
return 1;
}
- blk = img_open(image_opts, filename, fmt, 0, false, false);
+ blk = img_open(image_opts, filename, fmt, 0, false, false, force_share);
if (!blk) {
return 1;
}
bool quiet = false;
Error *err = NULL;
bool image_opts = false;
+ bool force_share = false;
bdrv_oflags = BDRV_O_RDWR;
/* Parse commandline parameters */
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":la:c:d:hq",
+ c = getopt_long(argc, argv, ":la:c:d:hqU",
long_options, NULL);
if (c == -1) {
break;
case 'q':
quiet = true;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OBJECT: {
QemuOpts *opts;
opts = qemu_opts_parse_noisily(&qemu_object_opts,
}
/* Open the image */
- blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet);
+ blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet,
+ force_share);
if (!blk) {
return 1;
}
int c, flags, src_flags, ret;
bool writethrough, src_writethrough;
int unsafe = 0;
+ bool force_share = false;
int progress = 0;
bool quiet = false;
Error *local_err = NULL;
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:F:b:upt:T:q",
+ c = getopt_long(argc, argv, ":hf:F:b:upt:T:qU",
long_options, NULL);
if (c == -1) {
break;
case OPTION_IMAGE_OPTS:
image_opts = true;
break;
+ case 'U':
+ force_share = true;
+ break;
}
}
* Ignore the old backing file for unsafe rebase in case we want to correct
* the reference to a renamed or moved backing file.
*/
- blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
+ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
+ false);
if (!blk) {
ret = -1;
goto out;
qdict_put_str(options, "driver", bs->backing_format);
}
+ if (force_share) {
+ if (!options) {
+ options = qdict_new();
+ }
+ qdict_put(options, BDRV_OPT_FORCE_SHARE,
+ qbool_from_bool(true));
+ }
bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
blk_old_backing = blk_new_open(backing_name, NULL,
options, src_flags, &local_err);
}
if (out_baseimg[0]) {
+ options = qdict_new();
if (out_basefmt) {
- options = qdict_new();
qdict_put_str(options, "driver", out_basefmt);
- } else {
- options = NULL;
+ }
+ if (force_share) {
+ qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
}
blk_new_backing = blk_new_open(out_baseimg, NULL,
qemu_opts_del(param);
blk = img_open(image_opts, filename, fmt,
- BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet);
+ BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet,
+ false);
if (!blk) {
ret = -1;
goto out;
goto out;
}
- blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
+ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
+ false);
if (!blk) {
ret = -1;
goto out;
bool writethrough = false;
struct timeval t1, t2;
int i;
+ bool force_share = false;
for (;;) {
static const struct option long_options[] = {
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
{"pattern", required_argument, 0, OPTION_PATTERN},
{"no-drain", no_argument, 0, OPTION_NO_DRAIN},
+ {"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hc:d:f:no:qs:S:t:w", long_options, NULL);
+ c = getopt_long(argc, argv, ":hc:d:f:no:qs:S:t:wU", long_options, NULL);
if (c == -1) {
break;
}
flags |= BDRV_O_RDWR;
is_write = true;
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_PATTERN:
{
unsigned long res;
goto out;
}
- blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet);
+ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet,
+ force_share);
if (!blk) {
ret = -1;
goto out;
const char *fmt = NULL;
int64_t size = 0;
int64_t block_count = 0, out_pos, in_pos;
+ bool force_share = false;
struct DdInfo dd = {
.flags = 0,
.count = 0,
const struct option long_options[] = {
{ "help", no_argument, 0, 'h'},
{ "image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ { "force-share", no_argument, 0, 'U'},
{ 0, 0, 0, 0 }
};
- while ((c = getopt_long(argc, argv, ":hf:O:", long_options, NULL))) {
+ while ((c = getopt_long(argc, argv, ":hf:O:U", long_options, NULL))) {
if (c == EOF) {
break;
}
case 'h':
help();
break;
+ case 'U':
+ force_share = true;
+ break;
case OPTION_IMAGE_OPTS:
image_opts = true;
break;
ret = -1;
goto out;
}
- blk1 = img_open(image_opts, in.filename, fmt, 0, false, false);
+ blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
+ force_share);
if (!blk1) {
ret = -1;
}
blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR,
- false, false);
+ false, false, false);
if (!blk2) {
ret = -1;
}
if (bflag) {
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
+ if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
+ printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset);
return 0;
}
- if (count & 0x1ff) {
- printf("count %"PRId64" is not sector aligned\n",
+ if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
+ printf("%"PRId64" is not a sector-aligned value for 'count'\n",
count);
return 0;
}
}
if (bflag || cflag) {
- if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
+ if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
+ printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset);
return 0;
}
- if (count & 0x1ff) {
- printf("count %"PRId64" is not sector aligned\n",
+ if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
+ printf("%"PRId64" is not a sector-aligned value for 'count'\n",
count);
return 0;
}
static int alloc_f(BlockBackend *blk, int argc, char **argv)
{
BlockDriverState *bs = blk_bs(blk);
- int64_t offset, sector_num, nb_sectors, remaining;
+ int64_t offset, sector_num, nb_sectors, remaining, count;
char s1[64];
int num, ret;
int64_t sum_alloc;
if (offset < 0) {
print_cvtnum_err(offset, argv[1]);
return 0;
- } else if (offset & 0x1ff) {
- printf("offset %" PRId64 " is not sector aligned\n",
+ } else if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
+ printf("%" PRId64 " is not a sector-aligned value for 'offset'\n",
offset);
return 0;
}
if (argc == 3) {
- nb_sectors = cvtnum(argv[2]);
- if (nb_sectors < 0) {
- print_cvtnum_err(nb_sectors, argv[2]);
+ count = cvtnum(argv[2]);
+ if (count < 0) {
+ print_cvtnum_err(count, argv[2]);
return 0;
- } else if (nb_sectors > INT_MAX) {
- printf("length argument cannot exceed %d, given %s\n",
- INT_MAX, argv[2]);
+ } else if (count > INT_MAX * BDRV_SECTOR_SIZE) {
+ printf("length argument cannot exceed %llu, given %s\n",
+ INT_MAX * BDRV_SECTOR_SIZE, argv[2]);
return 0;
}
} else {
- nb_sectors = 1;
+ count = BDRV_SECTOR_SIZE;
+ }
+ if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) {
+ printf("%" PRId64 " is not a sector-aligned value for 'count'\n",
+ count);
+ return 0;
}
+ nb_sectors = count >> BDRV_SECTOR_BITS;
remaining = nb_sectors;
sum_alloc = 0;
cvtstr(offset, s1, sizeof(s1));
- printf("%"PRId64"/%"PRId64" sectors allocated at offset %s\n",
- sum_alloc, nb_sectors, s1);
+ printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n",
+ sum_alloc << BDRV_SECTOR_BITS, nb_sectors << BDRV_SECTOR_BITS, s1);
return 0;
}
.argmin = 1,
.argmax = 2,
.cfunc = alloc_f,
- .args = "off [sectors]",
- .oneline = "checks if a sector is present in the file",
+ .args = "offset [count]",
+ .oneline = "checks if offset is allocated in the file",
};
{
int64_t offset;
int64_t nb_sectors, total_sectors;
- char s1[64];
+ char s1[64], s2[64];
int64_t num;
int ret;
const char *retstr;
}
retstr = ret ? " allocated" : "not allocated";
- cvtstr(offset << 9ULL, s1, sizeof(s1));
- printf("[% 24" PRId64 "] % 8" PRId64 "/% 8" PRId64 " sectors %s "
- "at offset %s (%d)\n",
- offset << 9ULL, num, nb_sectors, retstr, s1, ret);
+ cvtstr(num << BDRV_SECTOR_BITS, s1, sizeof(s1));
+ cvtstr(offset << BDRV_SECTOR_BITS, s2, sizeof(s2));
+ printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n",
+ s1, num << BDRV_SECTOR_BITS, retstr,
+ s2, offset << BDRV_SECTOR_BITS);
offset += num;
nb_sectors -= num;
#include "qemu/readline.h"
#include "qemu/log.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qbool.h"
#include "qom/object_interfaces.h"
#include "sysemu/block-backend.h"
#include "block/block_int.h"
.oneline = "close the current open file",
};
-static int openfile(char *name, int flags, bool writethrough, QDict *opts)
+static int openfile(char *name, int flags, bool writethrough, bool force_share,
+ QDict *opts)
{
Error *local_err = NULL;
BlockDriverState *bs;
return 1;
}
+ if (force_share) {
+ if (!opts) {
+ opts = qdict_new();
+ }
+ if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE)
+ && !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) {
+ error_report("-U conflicts with image options");
+ QDECREF(opts);
+ return 1;
+ }
+ qdict_put(opts, BDRV_OPT_FORCE_SHARE, qbool_from_bool(true));
+ }
qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err);
if (!qemuio_blk) {
error_reportf_err(local_err, "can't open%s%s: ",
" -r, -- open file read-only\n"
" -s, -- use snapshot file\n"
" -n, -- disable host cache, short for -t none\n"
+" -U, -- force shared permissions\n"
" -k, -- use kernel AIO implementation (on Linux only)\n"
" -t, -- use the given cache mode for the image\n"
" -d, -- use the given discard mode for the image\n"
.argmin = 1,
.argmax = -1,
.flags = CMD_NOFILE_OK,
- .args = "[-rsnk] [-t cache] [-d discard] [-o options] [path]",
+ .args = "[-rsnkU] [-t cache] [-d discard] [-o options] [path]",
.oneline = "open the file specified by path",
.help = open_help,
};
int c;
QemuOpts *qopts;
QDict *opts;
+ bool force_share = false;
- while ((c = getopt(argc, argv, "snro:kt:d:")) != -1) {
+ while ((c = getopt(argc, argv, "snro:kt:d:U")) != -1) {
switch (c) {
case 's':
flags |= BDRV_O_SNAPSHOT;
return 0;
}
break;
+ case 'U':
+ force_share = true;
+ break;
default:
qemu_opts_reset(&empty_opts);
return qemuio_command_usage(&open_cmd);
qemu_opts_reset(&empty_opts);
if (optind == argc - 1) {
- return openfile(argv[optind], flags, writethrough, opts);
+ return openfile(argv[optind], flags, writethrough, force_share, opts);
} else if (optind == argc) {
- return openfile(NULL, flags, writethrough, opts);
+ return openfile(NULL, flags, writethrough, force_share, opts);
} else {
QDECREF(opts);
return qemuio_command_usage(&open_cmd);
" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
" specify tracing options\n"
" see qemu-img(1) man page for full description\n"
+" -U, --force-share force shared permissions\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
"\n"
int main(int argc, char **argv)
{
int readonly = 0;
- const char *sopt = "hVc:d:f:rsnmkt:T:";
+ const char *sopt = "hVc:d:f:rsnmkt:T:U";
const struct option lopt[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "trace", required_argument, NULL, 'T' },
{ "object", required_argument, NULL, OPTION_OBJECT },
{ "image-opts", no_argument, NULL, OPTION_IMAGE_OPTS },
+ { "force-share", no_argument, 0, 'U'},
{ NULL, 0, NULL, 0 }
};
int c;
QDict *opts = NULL;
const char *format = NULL;
char *trace_file = NULL;
+ bool force_share = false;
#ifdef CONFIG_POSIX
signal(SIGPIPE, SIG_IGN);
case 'h':
usage(progname);
exit(0);
+ case 'U':
+ force_share = true;
+ break;
case OPTION_OBJECT: {
QemuOpts *qopts;
qopts = qemu_opts_parse_noisily(&qemu_object_opts,
exit(1);
}
opts = qemu_opts_to_qdict(qopts, NULL);
- if (openfile(NULL, flags, writethrough, opts)) {
+ if (openfile(NULL, flags, writethrough, force_share, opts)) {
exit(1);
}
} else {
opts = qdict_new();
qdict_put_str(opts, "driver", format);
}
- if (openfile(argv[optind], flags, writethrough, opts)) {
+ if (openfile(argv[optind], flags, writethrough,
+ force_share, opts)) {
exit(1);
}
}
DEF("numa", HAS_ARG, QEMU_OPTION_numa,
"-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
- "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
+ "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
+ "-numa dist,src=source,dst=destination,val=distance\n", QEMU_ARCH_ALL)
STEXI
@item -numa node[,mem=@var{size}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}]
@itemx -numa node[,memdev=@var{id}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}]
+@itemx -numa dist,src=@var{source},dst=@var{destination},val=@var{distance}
+@itemx -numa cpu,node-id=@var{node}[,socket-id=@var{x}][,core-id=@var{y}][,thread-id=@var{z}]
@findex -numa
Define a NUMA node and assign RAM and VCPUs to it.
+Set the NUMA distance from a source node to a destination node.
+Legacy VCPU assignment uses @samp{cpus} option where
@var{firstcpu} and @var{lastcpu} are CPU indexes. Each
@samp{cpus} option represent a contiguous range of CPU indexes
(or a single VCPU if @var{lastcpu} is omitted). A non-contiguous
-numa node,cpus=0-2,cpus=5
@end example
+@samp{cpu} option is a new alternative to @samp{cpus} option
+which uses @samp{socket-id|core-id|thread-id} properties to assign
+CPU objects to a @var{node} using topology layout properties of CPU.
+The set of properties is machine specific, and depends on used
+machine type/@samp{smp} options. It could be queried with
+@samp{hotpluggable-cpus} monitor command.
+@samp{node-id} property specifies @var{node} to which CPU object
+will be assigned, it's required for @var{node} to be declared
+with @samp{node} option before it's used with @samp{cpu} option.
+
+For example:
+@example
+-M pc \
+-smp 1,sockets=2,maxcpus=2 \
+-numa node,nodeid=0 -numa node,nodeid=1 \
+-numa cpu,node-id=0,socket-id=0 -numa cpu,node-id=1,socket-id=1
+@end example
+
@samp{mem} assigns a given RAM amount to a node. @samp{memdev}
assigns RAM from a given memory backend device to a node. If
@samp{mem} and @samp{memdev} are omitted in all nodes, RAM is
@samp{mem} and @samp{memdev} are mutually exclusive. Furthermore,
if one node uses @samp{memdev}, all of them have to use it.
+@var{source} and @var{destination} are NUMA node IDs.
+@var{distance} is the NUMA distance from @var{source} to @var{destination}.
+The distance from a node to itself is always 10. If any pair of nodes is
+given a distance, then all pairs must be given distances. Although, when
+distances are only given in one direction for each pair of nodes, then
+the distances in the opposite directions are assumed to be the same. If,
+however, an asymmetrical pair of distances is given for even one node
+pair, then all node pairs must be provided distance values for both
+directions, even when they are symmetrical. When a node is unreachable
+from another node, set the pair's distance to 255.
+
Note that the -@option{numa} option doesn't allocate any of the
specified resources, it just assigns existing resources to NUMA
nodes. This means that one still has to use the @option{-m},
specific URLs. See the section for "Device URL Syntax" for more information.
@item if=@var{interface}
This option defines on which type on interface the drive is connected.
-Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio.
+Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio, none.
@item bus=@var{bus},unit=@var{unit}
These options define where is connected the drive by defining the bus number and
the unit id.
DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
"-virtfs local,path=path,mount_tag=tag,security_model=[mapped-xattr|mapped-file|passthrough|none]\n"
- " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n",
+ " [,id=id][,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n",
QEMU_ARCH_ALL)
STEXI
-# If you want to use the non-TLS socket, then you *must* include
-# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only
-# ones that can offer session encryption as well as authentication.
+# If you want to use VNC remotely without TLS, then you *must*
+# pick a mechanism which provides session encryption as well
+# as authentication.
#
-# If you're only using TLS, then you can turn on any mechanisms
+# If you are only using TLS, then you can turn on any mechanisms
# you like for authentication, because TLS provides the encryption
#
-# Default to a simple username+password mechanism
-# NB digest-md5 is no longer considered secure by current standards
-mech_list: digest-md5
+# If you are only using UNIX sockets then encryption is not
+# required at all.
+#
+# NB, previously DIGEST-MD5 was set as the default mechanism for
+# QEMU VNC. Per RFC 6331 this is vulnerable to many serious security
+# flaws as should no longer be used. Thus GSSAPI is now the default.
+#
+# To use GSSAPI requires that a QEMU service principal is
+# added to the Kerberos server for each host running QEMU.
+# This principal needs to be exported to the keytab file listed below
+mech_list: gssapi
-# Before you can use GSSAPI, you need a service principle on the
-# KDC server for libvirt, and that to be exported to the keytab
-# file listed below
-#mech_list: gssapi
+# If using TLS with VNC, or a UNIX socket only, it is possible to
+# enable plugins which don't provide session encryption. The
+# 'scram-sha-1' plugin allows plain username/password authentication
+# to be performed
#
-# You can also list many mechanisms at once, then the user can choose
-# by adding '?auth=sasl.gssapi' to their libvirt URI, eg
-# qemu+tcp://hostname/system?auth=sasl.gssapi
-#mech_list: digest-md5 gssapi
+#mech_list: scram-sha-1
+
+# You can also list many mechanisms at once, and the VNC server will
+# negotiate which to use by considering the list enabled on the VNC
+# client.
+#mech_list: scram-sha-1 gssapi
# Some older builds of MIT kerberos on Linux ignore this option &
# instead need KRB5_KTNAME env var.
# For modern Linux, and other OS, this should be sufficient
#
-# There is no default value here, uncomment if you need this
-#keytab: /etc/qemu/krb5.tab
+# This file needs to be populated with the service principal that
+# was created on the Kerberos v5 server. If switching to a non-gssapi
+# mechanism this can be commented out.
+keytab: /etc/qemu/krb5.tab
-# If using digest-md5 for username/passwds, then this is the file
+# If using scram-sha-1 for username/passwds, then this is the file
# containing the passwds. Use 'saslpasswd2 -a qemu [username]'
# to add entries, and 'sasldblistusers2 -f [sasldb_path]' to browse it
-sasldb_path: /etc/qemu/passwd.db
-
-
-auxprop_plugin: sasldb
-
+#sasldb_path: /etc/qemu/passwd.db
* we think this VM does not support online/offline memory block,
* any other solution?
*/
- if (!dp && errno == ENOENT) {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
+ if (!dp) {
+ if (errno == ENOENT) {
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
+ }
goto out1;
}
closedir(dp);
}
/* Continuing after completed migration. Images have been inactivated to
- * allow the destination to take control. Need to get control back now. */
- if (runstate_check(RUN_STATE_FINISH_MIGRATE) ||
- runstate_check(RUN_STATE_POSTMIGRATE))
- {
- bdrv_invalidate_cache_all(&local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- }
-
- blk_resume_after_migration(&local_err);
+ * allow the destination to take control. Need to get control back now.
+ *
+ * If there are no inactive block nodes (e.g. because the VM was just
+ * paused rather than completing a migration), bdrv_inactivate_all() simply
+ * doesn't do anything. */
+ bdrv_invalidate_cache_all(&local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
static void cpu_common_initfn(Object *obj)
{
+ uint32_t count;
CPUState *cpu = CPU(obj);
CPUClass *cc = CPU_GET_CLASS(obj);
QTAILQ_INIT(&cpu->breakpoints);
QTAILQ_INIT(&cpu->watchpoints);
- cpu->trace_dstate = bitmap_new(trace_get_vcpu_event_count());
+ count = trace_get_vcpu_event_count();
+ if (count) {
+ cpu->trace_dstate = bitmap_new(count);
+ }
cpu_exec_initfn(cpu);
}
--- /dev/null
+Subproject commit d4e7d7ac663fcb55f1b93575445fcbca372f17a7
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
# (c) 2001, Dave Jones. (the file handling bit)
# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
# Licensed under the terms of the GNU GPL License version 2
use strict;
+use warnings;
my $P = $0;
$P =~ s@.*/@@g;
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
#
# Clean up include guards in headers
#
# "cc -E -DGUARD_H -c -P -", and fed the test program on stdin.
use strict;
+use warnings;
use Getopt::Std;
# Stuff we don't want to clean because we import it into our tree:
-#!/usr/bin/perl
+#!/usr/bin/env perl
# Copyright (C) 2013 Red Hat, Inc.
#
# Authors:
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
+
+use warnings;
use File::Temp qw/ tempfile /;
use Getopt::Long;
-#!/usr/bin/perl -w
+#!/usr/bin/env perl
# (c) 2007, Joe Perches <joe@perches.com>
# created from checkpatch.pl
#
# Licensed under the terms of the GNU GPL License version 2
use strict;
+use warnings;
my $P = $0;
my $V = '0.26';
shift
# check given cpu is in the supported CPU list
for cpu in ${qemu_target_list} ; do
- if [ "$cpu" == "$1" ] ; then
+ if [ "$cpu" = "$1" ] ; then
break
fi
done
- if [ "$cpu" == "$1" ] ; then
+ if [ "$cpu" = "$1" ] ; then
qemu_target_list="$1"
else
echo "ERROR: unknown CPU \"$1\"" 1>&2
-#!/usr/bin/perl
+#!/usr/bin/env perl
use strict;
use warnings;
-#!/usr/bin/perl
+#!/usr/bin/env perl
use strict;
use warnings;
-#! /usr/bin/perl -w
+#! /usr/bin/env perl
# Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
# markup to Perl POD format. It's intended to be used to extract
# something suitable for a manpage from a Texinfo document.
+use warnings;
+
$output = 0;
$skipping = 0;
%sects = ();
}
}
+uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz)
+{
+ uint32_t Aff1 = idx / clustersz;
+ uint32_t Aff0 = idx % clustersz;
+ return (Aff1 << ARM_AFF1_SHIFT) | Aff0;
+}
+
static void arm_cpu_initfn(Object *obj)
{
CPUState *cs = CPU(obj);
* so these bits always RAZ.
*/
if (cpu->mp_affinity == ARM64_AFFINITY_INVALID) {
- uint32_t Aff1 = cs->cpu_index / ARM_DEFAULT_CPUS_PER_CLUSTER;
- uint32_t Aff0 = cs->cpu_index % ARM_DEFAULT_CPUS_PER_CLUSTER;
- cpu->mp_affinity = (Aff1 << ARM_AFF1_SHIFT) | Aff0;
+ cpu->mp_affinity = arm_cpu_mp_affinity(cs->cpu_index,
+ ARM_DEFAULT_CPUS_PER_CLUSTER);
}
if (cpu->reset_hivecs) {
DEFINE_PROP_UINT32("midr", ARMCPU, midr, 0),
DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
mp_affinity, ARM64_AFFINITY_INVALID),
+ DEFINE_PROP_INT32("node-id", CPUState, numa_node, CPU_UNSET_NUMA_NODE_ID),
DEFINE_PROP_END_OF_LIST()
};
return container_of(env, ARMCPU, env);
}
+uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz);
+
#define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e))
#define ENV_OFFSET offsetof(ARMCPU, env)
X86CPU *cpu = x86_env_get_cpu(env);
CPUState *cs = CPU(cpu);
uint32_t pkg_offset;
+ uint32_t limit;
- /* test if maximum index reached */
- if (index & 0x80000000) {
- if (index > env->cpuid_xlevel) {
- if (env->cpuid_xlevel2 > 0) {
- /* Handle the Centaur's CPUID instruction. */
- if (index > env->cpuid_xlevel2) {
- index = env->cpuid_xlevel2;
- } else if (index < 0xC0000000) {
- index = env->cpuid_xlevel;
- }
- } else {
- /* Intel documentation states that invalid EAX input will
- * return the same information as EAX=cpuid_level
- * (Intel SDM Vol. 2A - Instruction Set Reference - CPUID)
- */
- index = env->cpuid_level;
- }
- }
+ /* Calculate & apply limits for different index ranges */
+ if (index >= 0xC0000000) {
+ limit = env->cpuid_xlevel2;
+ } else if (index >= 0x80000000) {
+ limit = env->cpuid_xlevel;
} else {
- if (index > env->cpuid_level)
- index = env->cpuid_level;
+ limit = env->cpuid_level;
+ }
+
+ if (index > limit) {
+ /* Intel documentation states that invalid EAX input will
+ * return the same information as EAX=cpuid_level
+ * (Intel SDM Vol. 2A - Instruction Set Reference - CPUID)
+ */
+ index = env->cpuid_level;
}
switch(index) {
DEFINE_PROP_INT32("core-id", X86CPU, core_id, -1),
DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, -1),
#endif
+ DEFINE_PROP_INT32("node-id", CPUState, numa_node, CPU_UNSET_NUMA_NODE_ID),
DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
{ .name = "hv-spinlocks", .info = &qdev_prop_spinlocks },
DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false),
#define VMSTATE_MTRR_VARS(_field, _state, _n, _v) \
VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_mtrr_var, MTRRVar)
-static int put_fpreg_error(QEMUFile *f, void *opaque, size_t size,
- VMStateField *field, QJSON *vmdesc)
-{
- fprintf(stderr, "call put_fpreg() with invalid arguments\n");
- exit(0);
- return 0;
-}
-
-/* XXX: add that in a FPU generic layer */
-union x86_longdouble {
- uint64_t mant;
- uint16_t exp;
-};
+typedef struct x86_FPReg_tmp {
+ FPReg *parent;
+ uint64_t tmp_mant;
+ uint16_t tmp_exp;
+} x86_FPReg_tmp;
-#define MANTD1(fp) (fp & ((1LL << 52) - 1))
-#define EXPBIAS1 1023
-#define EXPD1(fp) ((fp >> 52) & 0x7FF)
-#define SIGND1(fp) ((fp >> 32) & 0x80000000)
-
-static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp)
+static void fpreg_pre_save(void *opaque)
{
- int e;
- /* mantissa */
- p->mant = (MANTD1(temp) << 11) | (1LL << 63);
- /* exponent + sign */
- e = EXPD1(temp) - EXPBIAS1 + 16383;
- e |= SIGND1(temp) >> 16;
- p->exp = e;
-}
+ x86_FPReg_tmp *tmp = opaque;
-static int get_fpreg(QEMUFile *f, void *opaque, size_t size,
- VMStateField *field)
-{
- FPReg *fp_reg = opaque;
- uint64_t mant;
- uint16_t exp;
-
- qemu_get_be64s(f, &mant);
- qemu_get_be16s(f, &exp);
- fp_reg->d = cpu_set_fp80(mant, exp);
- return 0;
-}
-
-static int put_fpreg(QEMUFile *f, void *opaque, size_t size,
- VMStateField *field, QJSON *vmdesc)
-{
- FPReg *fp_reg = opaque;
- uint64_t mant;
- uint16_t exp;
/* we save the real CPU data (in case of MMX usage only 'mant'
contains the MMX register */
- cpu_get_fp80(&mant, &exp, fp_reg->d);
- qemu_put_be64s(f, &mant);
- qemu_put_be16s(f, &exp);
-
- return 0;
+ cpu_get_fp80(&tmp->tmp_mant, &tmp->tmp_exp, tmp->parent->d);
}
-static const VMStateInfo vmstate_fpreg = {
- .name = "fpreg",
- .get = get_fpreg,
- .put = put_fpreg,
-};
-
-static int get_fpreg_1_mmx(QEMUFile *f, void *opaque, size_t size,
- VMStateField *field)
+static int fpreg_post_load(void *opaque, int version)
{
- union x86_longdouble *p = opaque;
- uint64_t mant;
+ x86_FPReg_tmp *tmp = opaque;
- qemu_get_be64s(f, &mant);
- p->mant = mant;
- p->exp = 0xffff;
+ tmp->parent->d = cpu_set_fp80(tmp->tmp_mant, tmp->tmp_exp);
return 0;
}
-static const VMStateInfo vmstate_fpreg_1_mmx = {
- .name = "fpreg_1_mmx",
- .get = get_fpreg_1_mmx,
- .put = put_fpreg_error,
-};
-
-static int get_fpreg_1_no_mmx(QEMUFile *f, void *opaque, size_t size,
- VMStateField *field)
-{
- union x86_longdouble *p = opaque;
- uint64_t mant;
-
- qemu_get_be64s(f, &mant);
- fp64_to_fp80(p, mant);
- return 0;
-}
-
-static const VMStateInfo vmstate_fpreg_1_no_mmx = {
- .name = "fpreg_1_no_mmx",
- .get = get_fpreg_1_no_mmx,
- .put = put_fpreg_error,
+static const VMStateDescription vmstate_fpreg_tmp = {
+ .name = "fpreg_tmp",
+ .post_load = fpreg_post_load,
+ .pre_save = fpreg_pre_save,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(tmp_mant, x86_FPReg_tmp),
+ VMSTATE_UINT16(tmp_exp, x86_FPReg_tmp),
+ VMSTATE_END_OF_LIST()
+ }
};
-static bool fpregs_is_0(void *opaque, int version_id)
-{
- X86CPU *cpu = opaque;
- CPUX86State *env = &cpu->env;
-
- return (env->fpregs_format_vmstate == 0);
-}
-
-static bool fpregs_is_1_mmx(void *opaque, int version_id)
-{
- X86CPU *cpu = opaque;
- CPUX86State *env = &cpu->env;
- int guess_mmx;
-
- guess_mmx = ((env->fptag_vmstate == 0xff) &&
- (env->fpus_vmstate & 0x3800) == 0);
- return (guess_mmx && (env->fpregs_format_vmstate == 1));
-}
-
-static bool fpregs_is_1_no_mmx(void *opaque, int version_id)
-{
- X86CPU *cpu = opaque;
- CPUX86State *env = &cpu->env;
- int guess_mmx;
-
- guess_mmx = ((env->fptag_vmstate == 0xff) &&
- (env->fpus_vmstate & 0x3800) == 0);
- return (!guess_mmx && (env->fpregs_format_vmstate == 1));
-}
-
-#define VMSTATE_FP_REGS(_field, _state, _n) \
- VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0, vmstate_fpreg, FPReg), \
- VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_mmx, vmstate_fpreg_1_mmx, FPReg), \
- VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_no_mmx, vmstate_fpreg_1_no_mmx, FPReg)
-
-static bool version_is_5(void *opaque, int version_id)
-{
- return version_id == 5;
-}
-
-#ifdef TARGET_X86_64
-static bool less_than_7(void *opaque, int version_id)
-{
- return version_id < 7;
-}
-
-static int get_uint64_as_uint32(QEMUFile *f, void *pv, size_t size,
- VMStateField *field)
-{
- uint64_t *v = pv;
- *v = qemu_get_be32(f);
- return 0;
-}
-
-static int put_uint64_as_uint32(QEMUFile *f, void *pv, size_t size,
- VMStateField *field, QJSON *vmdesc)
-{
- uint64_t *v = pv;
- qemu_put_be32(f, *v);
-
- return 0;
-}
-
-static const VMStateInfo vmstate_hack_uint64_as_uint32 = {
- .name = "uint64_as_uint32",
- .get = get_uint64_as_uint32,
- .put = put_uint64_as_uint32,
+static const VMStateDescription vmstate_fpreg = {
+ .name = "fpreg",
+ .fields = (VMStateField[]) {
+ VMSTATE_WITH_TMP(FPReg, x86_FPReg_tmp, vmstate_fpreg_tmp),
+ VMSTATE_END_OF_LIST()
+ }
};
-#define VMSTATE_HACK_UINT32(_f, _s, _t) \
- VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint64_as_uint32, uint64_t)
-#endif
-
static void cpu_pre_save(void *opaque)
{
X86CPU *cpu = opaque;
return -EINVAL;
}
+ if (env->fpregs_format_vmstate) {
+ error_report("Unsupported old non-softfloat CPU state");
+ return -EINVAL;
+ }
/*
* Real mode guest segments register DPL should be zero.
* Older KVM version were setting it wrongly.
VMStateDescription vmstate_x86_cpu = {
.name = "cpu",
.version_id = 12,
- .minimum_version_id = 3,
+ .minimum_version_id = 11,
.pre_save = cpu_pre_save,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT16(env.fpus_vmstate, X86CPU),
VMSTATE_UINT16(env.fptag_vmstate, X86CPU),
VMSTATE_UINT16(env.fpregs_format_vmstate, X86CPU),
- VMSTATE_FP_REGS(env.fpregs, X86CPU, 8),
+
+ VMSTATE_STRUCT_ARRAY(env.fpregs, X86CPU, 8, 0, vmstate_fpreg, FPReg),
VMSTATE_SEGMENT_ARRAY(env.segs, X86CPU, 6),
VMSTATE_SEGMENT(env.ldt, X86CPU),
VMSTATE_SEGMENT(env.idt, X86CPU),
VMSTATE_UINT32(env.sysenter_cs, X86CPU),
-#ifdef TARGET_X86_64
- /* Hack: In v7 size changed from 32 to 64 bits on x86_64 */
- VMSTATE_HACK_UINT32(env.sysenter_esp, X86CPU, less_than_7),
- VMSTATE_HACK_UINT32(env.sysenter_eip, X86CPU, less_than_7),
- VMSTATE_UINTTL_V(env.sysenter_esp, X86CPU, 7),
- VMSTATE_UINTTL_V(env.sysenter_eip, X86CPU, 7),
-#else
VMSTATE_UINTTL(env.sysenter_esp, X86CPU),
VMSTATE_UINTTL(env.sysenter_eip, X86CPU),
-#endif
VMSTATE_UINTTL(env.cr[0], X86CPU),
VMSTATE_UINTTL(env.cr[2], X86CPU),
VMSTATE_UINT64(env.fmask, X86CPU),
VMSTATE_UINT64(env.kernelgsbase, X86CPU),
#endif
- VMSTATE_UINT32_V(env.smbase, X86CPU, 4),
-
- VMSTATE_UINT64_V(env.pat, X86CPU, 5),
- VMSTATE_UINT32_V(env.hflags2, X86CPU, 5),
-
- VMSTATE_UINT32_TEST(parent_obj.halted, X86CPU, version_is_5),
- VMSTATE_UINT64_V(env.vm_hsave, X86CPU, 5),
- VMSTATE_UINT64_V(env.vm_vmcb, X86CPU, 5),
- VMSTATE_UINT64_V(env.tsc_offset, X86CPU, 5),
- VMSTATE_UINT64_V(env.intercept, X86CPU, 5),
- VMSTATE_UINT16_V(env.intercept_cr_read, X86CPU, 5),
- VMSTATE_UINT16_V(env.intercept_cr_write, X86CPU, 5),
- VMSTATE_UINT16_V(env.intercept_dr_read, X86CPU, 5),
- VMSTATE_UINT16_V(env.intercept_dr_write, X86CPU, 5),
- VMSTATE_UINT32_V(env.intercept_exceptions, X86CPU, 5),
- VMSTATE_UINT8_V(env.v_tpr, X86CPU, 5),
+ VMSTATE_UINT32(env.smbase, X86CPU),
+
+ VMSTATE_UINT64(env.pat, X86CPU),
+ VMSTATE_UINT32(env.hflags2, X86CPU),
+
+ VMSTATE_UINT64(env.vm_hsave, X86CPU),
+ VMSTATE_UINT64(env.vm_vmcb, X86CPU),
+ VMSTATE_UINT64(env.tsc_offset, X86CPU),
+ VMSTATE_UINT64(env.intercept, X86CPU),
+ VMSTATE_UINT16(env.intercept_cr_read, X86CPU),
+ VMSTATE_UINT16(env.intercept_cr_write, X86CPU),
+ VMSTATE_UINT16(env.intercept_dr_read, X86CPU),
+ VMSTATE_UINT16(env.intercept_dr_write, X86CPU),
+ VMSTATE_UINT32(env.intercept_exceptions, X86CPU),
+ VMSTATE_UINT8(env.v_tpr, X86CPU),
/* MTRRs */
- VMSTATE_UINT64_ARRAY_V(env.mtrr_fixed, X86CPU, 11, 8),
- VMSTATE_UINT64_V(env.mtrr_deftype, X86CPU, 8),
+ VMSTATE_UINT64_ARRAY(env.mtrr_fixed, X86CPU, 11),
+ VMSTATE_UINT64(env.mtrr_deftype, X86CPU),
VMSTATE_MTRR_VARS(env.mtrr_var, X86CPU, MSR_MTRRcap_VCNT, 8),
/* KVM-related states */
- VMSTATE_INT32_V(env.interrupt_injected, X86CPU, 9),
- VMSTATE_UINT32_V(env.mp_state, X86CPU, 9),
- VMSTATE_UINT64_V(env.tsc, X86CPU, 9),
- VMSTATE_INT32_V(env.exception_injected, X86CPU, 11),
- VMSTATE_UINT8_V(env.soft_interrupt, X86CPU, 11),
- VMSTATE_UINT8_V(env.nmi_injected, X86CPU, 11),
- VMSTATE_UINT8_V(env.nmi_pending, X86CPU, 11),
- VMSTATE_UINT8_V(env.has_error_code, X86CPU, 11),
- VMSTATE_UINT32_V(env.sipi_vector, X86CPU, 11),
+ VMSTATE_INT32(env.interrupt_injected, X86CPU),
+ VMSTATE_UINT32(env.mp_state, X86CPU),
+ VMSTATE_UINT64(env.tsc, X86CPU),
+ VMSTATE_INT32(env.exception_injected, X86CPU),
+ VMSTATE_UINT8(env.soft_interrupt, X86CPU),
+ VMSTATE_UINT8(env.nmi_injected, X86CPU),
+ VMSTATE_UINT8(env.nmi_pending, X86CPU),
+ VMSTATE_UINT8(env.has_error_code, X86CPU),
+ VMSTATE_UINT32(env.sipi_vector, X86CPU),
/* MCE */
- VMSTATE_UINT64_V(env.mcg_cap, X86CPU, 10),
- VMSTATE_UINT64_V(env.mcg_status, X86CPU, 10),
- VMSTATE_UINT64_V(env.mcg_ctl, X86CPU, 10),
- VMSTATE_UINT64_ARRAY_V(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4, 10),
+ VMSTATE_UINT64(env.mcg_cap, X86CPU),
+ VMSTATE_UINT64(env.mcg_status, X86CPU),
+ VMSTATE_UINT64(env.mcg_ctl, X86CPU),
+ VMSTATE_UINT64_ARRAY(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4),
/* rdtscp */
- VMSTATE_UINT64_V(env.tsc_aux, X86CPU, 11),
+ VMSTATE_UINT64(env.tsc_aux, X86CPU),
/* KVM pvclock msr */
- VMSTATE_UINT64_V(env.system_time_msr, X86CPU, 11),
- VMSTATE_UINT64_V(env.wall_clock_msr, X86CPU, 11),
+ VMSTATE_UINT64(env.system_time_msr, X86CPU),
+ VMSTATE_UINT64(env.wall_clock_msr, X86CPU),
/* XSAVE related fields */
VMSTATE_UINT64_V(env.xcr0, X86CPU, 12),
VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 12),
ifeq ($(CONFIG_SOFTMMU),y)
obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o arch_dump.o
obj-$(TARGET_PPC64) += mmu-hash64.o mmu-book3s-v3.o compat.o
+obj-$(TARGET_PPC64) += mmu-radix64.o
endif
obj-$(CONFIG_KVM) += kvm.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
CPU_POWERPC_POWER8NVL_BASE = 0x004C0000,
CPU_POWERPC_POWER8NVL_v10 = 0x004C0100,
CPU_POWERPC_POWER9_BASE = 0x004E0000,
+ CPU_POWERPC_POWER9_DD1 = 0x004E0100,
CPU_POWERPC_970_v22 = 0x00390202,
CPU_POWERPC_970FX_v10 = 0x00391100,
CPU_POWERPC_970FX_v20 = 0x003C0200,
#define TARGET_LONG_BITS 64
#define TARGET_PAGE_BITS 12
+#define TCG_GUEST_DEFAULT_MO 0
+
/* Note that the official physical address space bits is 62-M where M
is implementation dependent. I've not looked up M for the set of
cpus we emulate at the system level. */
#define DSISR_ISSTORE 0x02000000
/* Not permitted by virtual page class key protection */
#define DSISR_AMR 0x00200000
+/* Unsupported Radix Tree Configuration */
+#define DSISR_R_BADCONFIG 0x00080000
/* SRR1 error code fields */
PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
+PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc);
struct PPCVirtualHypervisor {
Object parent;
cs->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
+ /* Reset the reservation */
+ env->reserve_addr = -1;
+
/* Any interrupt is context synchronizing, check if TCG TLB
* needs a delayed flush on ppc64
*/
#if defined(TARGET_PPC64)
pcc->radix_page_info = kvm_get_radix_page_info();
+
+ if ((pcc->pvr & 0xffffff00) == CPU_POWERPC_POWER9_DD1) {
+ /*
+ * POWER9 DD1 has some bugs which make it not really ISA 3.00
+ * compliant. More importantly, advertising ISA 3.00
+ * architected mode may prevent guests from activating
+ * necessary DD1 workarounds.
+ */
+ pcc->pcr_supported &= ~(PCR_COMPAT_3_00 | PCR_COMPAT_2_07
+ | PCR_COMPAT_2_06 | PCR_COMPAT_2_05);
+ }
#endif /* defined(TARGET_PPC64) */
}
return cap_mmu_hash_v3;
}
-static PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc)
-{
- ObjectClass *oc = OBJECT_CLASS(pcc);
-
- while (oc && !object_class_is_abstract(oc)) {
- oc = object_class_get_parent(oc);
- }
- assert(oc);
-
- return POWERPC_CPU_CLASS(oc);
-}
-
PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void)
{
uint32_t host_pvr = mfpvr();
#include "cpu.h"
#include "mmu-hash64.h"
#include "mmu-book3s-v3.h"
-#include "qemu/error-report.h"
+#include "mmu-radix64.h"
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
int mmu_idx)
{
if (ppc64_radix_guest(cpu)) { /* Guest uses radix */
- /* TODO - Unsupported */
- error_report("Guest Radix Support Unimplemented");
- exit(1);
+ return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
} else { /* Guest uses hash */
return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
}
/* Partition Table Entry Fields */
#define PATBE1_GR 0x8000000000000000
+/* Process Table Entry */
+struct prtb_entry {
+ uint64_t prtbe0, prtbe1;
+};
+
#ifdef TARGET_PPC64
static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu)
--- /dev/null
+/*
+ * PowerPC Radix MMU mulation helpers for QEMU.
+ *
+ * Copyright (c) 2016 Suraj Jitindar Singh, IBM Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+#include "qemu/error-report.h"
+#include "sysemu/kvm.h"
+#include "kvm_ppc.h"
+#include "exec/log.h"
+#include "mmu-radix64.h"
+#include "mmu-book3s-v3.h"
+
+static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
+ uint64_t *lpid, uint64_t *pid)
+{
+ /* We don't have HV support yet and shouldn't get here with it set anyway */
+ assert(!msr_hv);
+
+ if (!msr_hv) { /* !MSR[HV] -> Guest */
+ switch (eaddr & R_EADDR_QUADRANT) {
+ case R_EADDR_QUADRANT0: /* Guest application */
+ *lpid = env->spr[SPR_LPIDR];
+ *pid = env->spr[SPR_BOOKS_PID];
+ break;
+ case R_EADDR_QUADRANT1: /* Illegal */
+ case R_EADDR_QUADRANT2:
+ return false;
+ case R_EADDR_QUADRANT3: /* Guest OS */
+ *lpid = env->spr[SPR_LPIDR];
+ *pid = 0; /* pid set to 0 -> addresses guest operating system */
+ break;
+ }
+ }
+
+ return true;
+}
+
+static void ppc_radix64_raise_segi(PowerPCCPU *cpu, int rwx, vaddr eaddr)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+
+ if (rwx == 2) { /* Instruction Segment Interrupt */
+ cs->exception_index = POWERPC_EXCP_ISEG;
+ } else { /* Data Segment Interrupt */
+ cs->exception_index = POWERPC_EXCP_DSEG;
+ env->spr[SPR_DAR] = eaddr;
+ }
+ env->error_code = 0;
+}
+
+static void ppc_radix64_raise_si(PowerPCCPU *cpu, int rwx, vaddr eaddr,
+ uint32_t cause)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+
+ if (rwx == 2) { /* Instruction Storage Interrupt */
+ cs->exception_index = POWERPC_EXCP_ISI;
+ env->error_code = cause;
+ } else { /* Data Storage Interrupt */
+ cs->exception_index = POWERPC_EXCP_DSI;
+ if (rwx == 1) { /* Write -> Store */
+ cause |= DSISR_ISSTORE;
+ }
+ env->spr[SPR_DSISR] = cause;
+ env->spr[SPR_DAR] = eaddr;
+ env->error_code = 0;
+ }
+}
+
+
+static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte,
+ int *fault_cause, int *prot)
+{
+ CPUPPCState *env = &cpu->env;
+ const int need_prot[] = { PAGE_READ, PAGE_WRITE, PAGE_EXEC };
+
+ /* Check Page Attributes (pte58:59) */
+ if (((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO) && (rwx == 2)) {
+ /*
+ * Radix PTE entries with the non-idempotent I/O attribute are treated
+ * as guarded storage
+ */
+ *fault_cause |= SRR1_NOEXEC_GUARD;
+ return true;
+ }
+
+ /* Determine permissions allowed by Encoded Access Authority */
+ if ((pte & R_PTE_EAA_PRIV) && msr_pr) { /* Insufficient Privilege */
+ *prot = 0;
+ } else if (msr_pr || (pte & R_PTE_EAA_PRIV)) {
+ *prot = ppc_radix64_get_prot_eaa(pte);
+ } else { /* !msr_pr && !(pte & R_PTE_EAA_PRIV) */
+ *prot = ppc_radix64_get_prot_eaa(pte);
+ *prot &= ppc_radix64_get_prot_amr(cpu); /* Least combined permissions */
+ }
+
+ /* Check if requested access type is allowed */
+ if (need_prot[rwx] & ~(*prot)) { /* Page Protected for that Access */
+ *fault_cause |= DSISR_PROTFAULT;
+ return true;
+ }
+
+ return false;
+}
+
+static void ppc_radix64_set_rc(PowerPCCPU *cpu, int rwx, uint64_t pte,
+ hwaddr pte_addr, int *prot)
+{
+ CPUState *cs = CPU(cpu);
+ uint64_t npte;
+
+ npte = pte | R_PTE_R; /* Always set reference bit */
+
+ if (rwx == 1) { /* Store/Write */
+ npte |= R_PTE_C; /* Set change bit */
+ } else {
+ /*
+ * Treat the page as read-only for now, so that a later write
+ * will pass through this function again to set the C bit.
+ */
+ *prot &= ~PAGE_WRITE;
+ }
+
+ if (pte ^ npte) { /* If pte has changed then write it back */
+ stq_phys(cs->as, pte_addr, npte);
+ }
+}
+
+static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, int rwx, vaddr eaddr,
+ uint64_t base_addr, uint64_t nls,
+ hwaddr *raddr, int *psize,
+ int *fault_cause, int *prot,
+ hwaddr *pte_addr)
+{
+ CPUState *cs = CPU(cpu);
+ uint64_t index, pde;
+
+ if (nls < 5) { /* Directory maps less than 2**5 entries */
+ *fault_cause |= DSISR_R_BADCONFIG;
+ return 0;
+ }
+
+ /* Read page <directory/table> entry from guest address space */
+ index = eaddr >> (*psize - nls); /* Shift */
+ index &= ((1UL << nls) - 1); /* Mask */
+ pde = ldq_phys(cs->as, base_addr + (index * sizeof(pde)));
+ if (!(pde & R_PTE_VALID)) { /* Invalid Entry */
+ *fault_cause |= DSISR_NOPTE;
+ return 0;
+ }
+
+ *psize -= nls;
+
+ /* Check if Leaf Entry -> Page Table Entry -> Stop the Search */
+ if (pde & R_PTE_LEAF) {
+ uint64_t rpn = pde & R_PTE_RPN;
+ uint64_t mask = (1UL << *psize) - 1;
+
+ if (ppc_radix64_check_prot(cpu, rwx, pde, fault_cause, prot)) {
+ return 0; /* Protection Denied Access */
+ }
+
+ /* Or high bits of rpn and low bits to ea to form whole real addr */
+ *raddr = (rpn & ~mask) | (eaddr & mask);
+ *pte_addr = base_addr + (index * sizeof(pde));
+ return pde;
+ }
+
+ /* Next Level of Radix Tree */
+ return ppc_radix64_walk_tree(cpu, rwx, eaddr, pde & R_PDE_NLB,
+ pde & R_PDE_NLS, raddr, psize,
+ fault_cause, prot, pte_addr);
+}
+
+int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
+ int mmu_idx)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ PPCVirtualHypervisorClass *vhc =
+ PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+ hwaddr raddr, pte_addr;
+ uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
+ int page_size, prot, fault_cause = 0;
+
+ assert((rwx == 0) || (rwx == 1) || (rwx == 2));
+ assert(!msr_hv); /* For now there is no Radix PowerNV Support */
+ assert(cpu->vhyp);
+ assert(ppc64_use_proc_tbl(cpu));
+
+ /* Real Mode Access */
+ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
+ /* In real mode top 4 effective addr bits (mostly) ignored */
+ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
+
+ tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
+ TARGET_PAGE_SIZE);
+ return 0;
+ }
+
+ /* Virtual Mode Access - get the fully qualified address */
+ if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
+ ppc_radix64_raise_segi(cpu, rwx, eaddr);
+ return 1;
+ }
+
+ /* Get Process Table */
+ patbe = vhc->get_patbe(cpu->vhyp);
+
+ /* Index Process Table by PID to Find Corresponding Process Table Entry */
+ offset = pid * sizeof(struct prtb_entry);
+ size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
+ if (offset >= size) {
+ /* offset exceeds size of the process table */
+ ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
+ return 1;
+ }
+ prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
+
+ /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
+ page_size = PRTBE_R_GET_RTS(prtbe0);
+ pte = ppc_radix64_walk_tree(cpu, rwx, eaddr & R_EADDR_MASK,
+ prtbe0 & PRTBE_R_RPDB, prtbe0 & PRTBE_R_RPDS,
+ &raddr, &page_size, &fault_cause, &prot,
+ &pte_addr);
+ if (!pte) {
+ ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause);
+ return 1;
+ }
+
+ /* Update Reference and Change Bits */
+ ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, &prot);
+
+ tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
+ prot, mmu_idx, 1UL << page_size);
+ return 1;
+}
--- /dev/null
+#ifndef MMU_RADIX64_H
+#define MMU_RADIX64_H
+
+#ifndef CONFIG_USER_ONLY
+
+/* Radix Quadrants */
+#define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF
+#define R_EADDR_QUADRANT 0xC000000000000000
+#define R_EADDR_QUADRANT0 0x0000000000000000
+#define R_EADDR_QUADRANT1 0x4000000000000000
+#define R_EADDR_QUADRANT2 0x8000000000000000
+#define R_EADDR_QUADRANT3 0xC000000000000000
+
+/* Radix Partition Table Entry Fields */
+#define PATBE1_R_PRTB 0x0FFFFFFFFFFFF000
+#define PATBE1_R_PRTS 0x000000000000001F
+
+/* Radix Process Table Entry Fields */
+#define PRTBE_R_GET_RTS(rts) \
+ ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31)
+#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00
+#define PRTBE_R_RPDS 0x000000000000001F
+
+/* Radix Page Directory/Table Entry Fields */
+#define R_PTE_VALID 0x8000000000000000
+#define R_PTE_LEAF 0x4000000000000000
+#define R_PTE_SW0 0x2000000000000000
+#define R_PTE_RPN 0x01FFFFFFFFFFF000
+#define R_PTE_SW1 0x0000000000000E00
+#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7))
+#define R_PTE_R 0x0000000000000100
+#define R_PTE_C 0x0000000000000080
+#define R_PTE_ATT 0x0000000000000030
+#define R_PTE_ATT_NORMAL 0x0000000000000000
+#define R_PTE_ATT_SAO 0x0000000000000010
+#define R_PTE_ATT_NI_IO 0x0000000000000020
+#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030
+#define R_PTE_EAA_PRIV 0x0000000000000008
+#define R_PTE_EAA_R 0x0000000000000004
+#define R_PTE_EAA_RW 0x0000000000000002
+#define R_PTE_EAA_X 0x0000000000000001
+#define R_PDE_NLB PRTBE_R_RPDB
+#define R_PDE_NLS PRTBE_R_RPDS
+
+#ifdef TARGET_PPC64
+
+int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
+ int mmu_idx);
+
+static inline int ppc_radix64_get_prot_eaa(uint64_t pte)
+{
+ return (pte & R_PTE_EAA_R ? PAGE_READ : 0) |
+ (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) |
+ (pte & R_PTE_EAA_X ? PAGE_EXEC : 0);
+}
+
+static inline int ppc_radix64_get_prot_amr(PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+ int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */
+ int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */
+
+ return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */
+ (amr & 0x1 ? 0 : PAGE_READ) |
+ (iamr & 0x1 ? 0 : PAGE_EXEC);
+}
+
+#endif /* TARGET_PPC64 */
+
+#endif /* CONFIG_USER_ONLY */
+
+#endif /* MMU_RADIX64_H */
#endif
static TCGv cpu_xer, cpu_so, cpu_ov, cpu_ca, cpu_ov32, cpu_ca32;
static TCGv cpu_reserve;
+static TCGv cpu_reserve_val;
static TCGv cpu_fpscr;
static TCGv_i32 cpu_access_type;
cpu_reserve = tcg_global_mem_new(cpu_env,
offsetof(CPUPPCState, reserve_addr),
"reserve_addr");
+ cpu_reserve_val = tcg_global_mem_new(cpu_env,
+ offsetof(CPUPPCState, reserve_val),
+ "reserve_val");
cpu_fpscr = tcg_global_mem_new(cpu_env,
offsetof(CPUPPCState, fpscr), "fpscr");
bool vsx_enabled;
bool spe_enabled;
bool tm_enabled;
+ bool gtse;
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
int singlestep_enabled;
uint64_t insns_flags;
/* eieio */
static void gen_eieio(DisasContext *ctx)
{
+ tcg_gen_mb(TCG_MO_LD_ST | TCG_BAR_SC);
}
#if !defined(CONFIG_USER_ONLY)
if (!ctx->pr) {
gen_check_tlb_flush(ctx, false);
}
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
gen_stop_exception(ctx);
}
} \
tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop); \
tcg_gen_mov_tl(cpu_reserve, t0); \
- tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); \
+ tcg_gen_mov_tl(cpu_reserve_val, gpr); \
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); \
tcg_temp_free(t0); \
}
static void gen_conditional_store(DisasContext *ctx, TCGv EA,
int reg, int memop)
{
- TCGLabel *l1;
+ TCGLabel *l1 = gen_new_label();
+ TCGLabel *l2 = gen_new_label();
+ TCGv t0;
- tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
- l1 = gen_new_label();
tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1);
- tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
- tcg_gen_qemu_st_tl(cpu_gpr[reg], EA, ctx->mem_idx, memop);
+
+ t0 = tcg_temp_new();
+ tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val,
+ cpu_gpr[reg], ctx->mem_idx,
+ DEF_MEMOP(memop) | MO_ALIGN);
+ tcg_gen_setcond_tl(TCG_COND_EQ, t0, t0, cpu_reserve_val);
+ tcg_gen_shli_tl(t0, t0, CRF_EQ_BIT);
+ tcg_gen_or_tl(t0, t0, cpu_so);
+ tcg_gen_trunc_tl_i32(cpu_crf[0], t0);
+ tcg_temp_free(t0);
+ tcg_gen_br(l2);
+
gen_set_label(l1);
+
+ /* Address mismatch implies failure. But we still need to provide the
+ memory barrier semantics of the instruction. */
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
+
+ gen_set_label(l2);
tcg_gen_movi_tl(cpu_reserve, -1);
}
#endif
if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) {
gen_check_tlb_flush(ctx, true);
}
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
}
/* wait */
GEN_PRIV;
#else
TCGv_i32 t1;
- CHK_HV;
+
+ if (ctx->gtse) {
+ CHK_SV; /* If gtse is set then tblie is supervisor privileged */
+ } else {
+ CHK_HV; /* Else hypervisor privileged */
+ }
if (NARROW_MODE(ctx)) {
TCGv t0 = tcg_temp_new();
* different ISA versions */
GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE),
GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE),
+GEN_HANDLER_E(tlbiel, 0x1F, 0x12, 0x08, 0x00100001, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(tlbie, 0x1F, 0x12, 0x09, 0x00100001, PPC_NONE, PPC2_ISA300),
GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC),
#if defined(TARGET_PPC64)
GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x031FFC01, PPC_SLBI),
ctx.tm_enabled = false;
}
#endif
+ ctx.gtse = !!(env->spr[SPR_LPCR] & LPCR_GTSE);
if ((env->flags & POWERPC_FLAG_SE) && msr_se)
ctx.singlestep_enabled = CPU_SINGLE_STEP;
else
PPC_FLOAT_EXT |
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
PPC_MEM_SYNC | PPC_MEM_EIEIO |
- PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
+ PPC_MEM_TLBSYNC |
PPC_64B | PPC_64BX | PPC_ALTIVEC |
PPC_SEGMENT_64B | PPC_SLBI |
PPC_POPCNTB | PPC_POPCNTWD |
return POWERPC_CPU(cpu_generic_init(TYPE_POWERPC_CPU, cpu_model));
}
+PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc)
+{
+ ObjectClass *oc = OBJECT_CLASS(pcc);
+
+ while (oc && !object_class_is_abstract(oc)) {
+ oc = object_class_get_parent(oc);
+ }
+ assert(oc);
+
+ return POWERPC_CPU_CLASS(oc);
+}
+
/* Sort by PVR, ordering special case "host" last. */
static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b)
{
ObjectClass *oc = data;
CPUListState *s = user_data;
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+ DeviceClass *family = DEVICE_CLASS(ppc_cpu_get_family_class(pcc));
const char *typename = object_class_get_name(oc);
char *name;
int i;
if (alias_oc != oc) {
continue;
}
- (*s->cpu_fprintf)(s->file, "PowerPC %-16s (alias for %s)\n",
- alias->alias, name);
+ /*
+ * If running with KVM, we might update the family alias later, so
+ * avoid printing the wrong alias here and use "preferred" instead
+ */
+ if (strcmp(alias->alias, family->desc) == 0) {
+ (*s->cpu_fprintf)(s->file,
+ "PowerPC %-16s (alias for preferred %s CPU)\n",
+ alias->alias, family->desc);
+ } else {
+ (*s->cpu_fprintf)(s->file, "PowerPC %-16s (alias for %s)\n",
+ alias->alias, name);
+ }
}
g_free(name);
}
return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
}
-static void ppc_cpu_exec_enter(CPUState *cs)
-{
- PowerPCCPU *cpu = POWERPC_CPU(cs);
- CPUPPCState *env = &cpu->env;
-
- env->reserve_addr = -1;
-}
-
/* CPUClass::reset() */
static void ppc_cpu_reset(CPUState *s)
{
cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug;
cc->vmsd = &vmstate_ppc_cpu;
#endif
- cc->cpu_exec_enter = ppc_cpu_exec_enter;
#if defined(CONFIG_SOFTMMU)
cc->write_elf64_note = ppc64_cpu_write_elf64_note;
cc->write_elf32_note = ppc32_cpu_write_elf32_note;
qht-bench
rcutorture
test-aio
+test-aio-multithread
+test-arm-mptimer
test-base64
test-bitops
test-bitcnt
test-crypto-block
test-crypto-cipher
test-crypto-hash
+test-crypto-hmac
test-crypto-ivgen
test-crypto-pbkdf
test-crypto-secret
test-crypto-xts
test-cutils
test-hbitmap
+test-hmp
test-int128
test-iov
test-io-channel-buffer
check-qtest-i386-y += tests/test-filter-redirector$(EXESUF)
check-qtest-i386-y += tests/postcopy-test$(EXESUF)
check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF)
+check-qtest-i386-y += tests/numa-test$(EXESUF)
check-qtest-x86_64-y += $(check-qtest-i386-y)
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
check-qtest-ppc64-y += tests/test-filter-mirror$(EXESUF)
check-qtest-ppc64-y += tests/test-filter-redirector$(EXESUF)
check-qtest-ppc64-y += tests/display-vga-test$(EXESUF)
+check-qtest-ppc64-y += tests/numa-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF)
check-qtest-sh4-y = tests/endianness-test$(EXESUF)
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
gcov-files-arm-y += hw/timer/arm_mptimer.c
+check-qtest-aarch64-y = tests/numa-test$(EXESUF)
+
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
+tests/numa-test$(EXESUF): tests/numa-test.o
tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
data.machine = MACHINE_PC;
data.variant = ".cphp";
test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6"
- " -numa node -numa node",
+ " -numa node -numa node"
+ " -numa dist,src=0,dst=1,val=21",
&data);
free_test_data(&data);
}
data.machine = MACHINE_Q35;
data.variant = ".cphp";
test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6"
- " -numa node -numa node",
+ " -numa node -numa node"
+ " -numa dist,src=0,dst=1,val=21",
&data);
free_test_data(&data);
}
memset(&data, 0, sizeof(data));
data.machine = MACHINE_Q35;
data.variant = ".memhp";
- test_acpi_one(" -m 128,slots=3,maxmem=1G -numa node", &data);
+ test_acpi_one(" -m 128,slots=3,maxmem=1G"
+ " -numa node -numa node"
+ " -numa dist,src=0,dst=1,val=21",
+ &data);
free_test_data(&data);
}
memset(&data, 0, sizeof(data));
data.machine = MACHINE_PC;
data.variant = ".memhp";
- test_acpi_one(" -m 128,slots=3,maxmem=1G -numa node", &data);
+ test_acpi_one(" -m 128,slots=3,maxmem=1G"
+ " -numa node -numa node"
+ " -numa dist,src=0,dst=1,val=21",
+ &data);
free_test_data(&data);
}
static void test_drive_del_device_del(void)
{
/* Start with a drive used by a device that unplugs instantaneously */
- qtest_start("-drive if=none,id=drive0,file=/dev/null,format=raw"
+ qtest_start("-drive if=none,id=drive0,file=null-co://,format=raw"
" -device virtio-scsi-pci"
" -device scsi-hd,drive=drive0,id=dev0");
--- /dev/null
+/*
+ * NUMA configuration test cases
+ *
+ * Copyright (c) 2017 Red Hat Inc.
+ * Authors:
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+static char *make_cli(const char *generic_cli, const char *test_cli)
+{
+ return g_strdup_printf("%s %s", generic_cli ? generic_cli : "", test_cli);
+}
+
+static char *hmp_info_numa(void)
+{
+ QDict *resp;
+ char *s;
+
+ resp = qmp("{ 'execute': 'human-monitor-command', 'arguments': "
+ "{ 'command-line': 'info numa '} }");
+ g_assert(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ s = g_strdup(qdict_get_str(resp, "return"));
+ g_assert(s);
+ QDECREF(resp);
+ return s;
+}
+
+static void test_mon_explicit(const void *data)
+{
+ char *s;
+ char *cli;
+
+ cli = make_cli(data, "-smp 8 "
+ "-numa node,nodeid=0,cpus=0-3 "
+ "-numa node,nodeid=1,cpus=4-7 ");
+ qtest_start(cli);
+
+ s = hmp_info_numa();
+ g_assert(strstr(s, "node 0 cpus: 0 1 2 3"));
+ g_assert(strstr(s, "node 1 cpus: 4 5 6 7"));
+ g_free(s);
+
+ qtest_end();
+ g_free(cli);
+}
+
+static void test_mon_default(const void *data)
+{
+ char *s;
+ char *cli;
+
+ cli = make_cli(data, "-smp 8 -numa node -numa node");
+ qtest_start(cli);
+
+ s = hmp_info_numa();
+ g_assert(strstr(s, "node 0 cpus: 0 2 4 6"));
+ g_assert(strstr(s, "node 1 cpus: 1 3 5 7"));
+ g_free(s);
+
+ qtest_end();
+ g_free(cli);
+}
+
+static void test_mon_partial(const void *data)
+{
+ char *s;
+ char *cli;
+
+ cli = make_cli(data, "-smp 8 "
+ "-numa node,nodeid=0,cpus=0-1 "
+ "-numa node,nodeid=1,cpus=4-5 ");
+ qtest_start(cli);
+
+ s = hmp_info_numa();
+ g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7"));
+ g_assert(strstr(s, "node 1 cpus: 4 5"));
+ g_free(s);
+
+ qtest_end();
+ g_free(cli);
+}
+
+static QList *get_cpus(QDict **resp)
+{
+ *resp = qmp("{ 'execute': 'query-cpus' }");
+ g_assert(*resp);
+ g_assert(qdict_haskey(*resp, "return"));
+ return qdict_get_qlist(*resp, "return");
+}
+
+static void test_query_cpus(const void *data)
+{
+ char *cli;
+ QDict *resp;
+ QList *cpus;
+ const QObject *e;
+
+ cli = make_cli(data, "-smp 8 -numa node,cpus=0-3 -numa node,cpus=4-7");
+ qtest_start(cli);
+ cpus = get_cpus(&resp);
+ g_assert(cpus);
+
+ while ((e = qlist_pop(cpus))) {
+ QDict *cpu, *props;
+ int64_t cpu_idx, node;
+
+ cpu = qobject_to_qdict(e);
+ g_assert(qdict_haskey(cpu, "CPU"));
+ g_assert(qdict_haskey(cpu, "props"));
+
+ cpu_idx = qdict_get_int(cpu, "CPU");
+ props = qdict_get_qdict(cpu, "props");
+ g_assert(qdict_haskey(props, "node-id"));
+ node = qdict_get_int(props, "node-id");
+ if (cpu_idx >= 0 && cpu_idx < 4) {
+ g_assert_cmpint(node, ==, 0);
+ } else {
+ g_assert_cmpint(node, ==, 1);
+ }
+ }
+
+ QDECREF(resp);
+ qtest_end();
+ g_free(cli);
+}
+
+static void pc_numa_cpu(const void *data)
+{
+ char *cli;
+ QDict *resp;
+ QList *cpus;
+ const QObject *e;
+
+ cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 "
+ "-numa node,nodeid=0 -numa node,nodeid=1 "
+ "-numa cpu,node-id=1,socket-id=0 "
+ "-numa cpu,node-id=0,socket-id=1,core-id=0 "
+ "-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 "
+ "-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1");
+ qtest_start(cli);
+ cpus = get_cpus(&resp);
+ g_assert(cpus);
+
+ while ((e = qlist_pop(cpus))) {
+ QDict *cpu, *props;
+ int64_t socket, core, thread, node;
+
+ cpu = qobject_to_qdict(e);
+ g_assert(qdict_haskey(cpu, "props"));
+ props = qdict_get_qdict(cpu, "props");
+
+ g_assert(qdict_haskey(props, "node-id"));
+ node = qdict_get_int(props, "node-id");
+ g_assert(qdict_haskey(props, "socket-id"));
+ socket = qdict_get_int(props, "socket-id");
+ g_assert(qdict_haskey(props, "core-id"));
+ core = qdict_get_int(props, "core-id");
+ g_assert(qdict_haskey(props, "thread-id"));
+ thread = qdict_get_int(props, "thread-id");
+
+ if (socket == 0) {
+ g_assert_cmpint(node, ==, 1);
+ } else if (socket == 1 && core == 0) {
+ g_assert_cmpint(node, ==, 0);
+ } else if (socket == 1 && core == 1 && thread == 0) {
+ g_assert_cmpint(node, ==, 0);
+ } else if (socket == 1 && core == 1 && thread == 1) {
+ g_assert_cmpint(node, ==, 1);
+ } else {
+ g_assert(false);
+ }
+ }
+
+ QDECREF(resp);
+ qtest_end();
+ g_free(cli);
+}
+
+static void spapr_numa_cpu(const void *data)
+{
+ char *cli;
+ QDict *resp;
+ QList *cpus;
+ const QObject *e;
+
+ cli = make_cli(data, "-smp 4,cores=4 "
+ "-numa node,nodeid=0 -numa node,nodeid=1 "
+ "-numa cpu,node-id=0,core-id=0 "
+ "-numa cpu,node-id=0,core-id=1 "
+ "-numa cpu,node-id=0,core-id=2 "
+ "-numa cpu,node-id=1,core-id=3");
+ qtest_start(cli);
+ cpus = get_cpus(&resp);
+ g_assert(cpus);
+
+ while ((e = qlist_pop(cpus))) {
+ QDict *cpu, *props;
+ int64_t core, node;
+
+ cpu = qobject_to_qdict(e);
+ g_assert(qdict_haskey(cpu, "props"));
+ props = qdict_get_qdict(cpu, "props");
+
+ g_assert(qdict_haskey(props, "node-id"));
+ node = qdict_get_int(props, "node-id");
+ g_assert(qdict_haskey(props, "core-id"));
+ core = qdict_get_int(props, "core-id");
+
+ if (core >= 0 && core < 3) {
+ g_assert_cmpint(node, ==, 0);
+ } else if (core == 3) {
+ g_assert_cmpint(node, ==, 1);
+ } else {
+ g_assert(false);
+ }
+ }
+
+ QDECREF(resp);
+ qtest_end();
+ g_free(cli);
+}
+
+static void aarch64_numa_cpu(const void *data)
+{
+ char *cli;
+ QDict *resp;
+ QList *cpus;
+ const QObject *e;
+
+ cli = make_cli(data, "-smp 2 "
+ "-numa node,nodeid=0 -numa node,nodeid=1 "
+ "-numa cpu,node-id=1,thread-id=0 "
+ "-numa cpu,node-id=0,thread-id=1");
+ qtest_start(cli);
+ cpus = get_cpus(&resp);
+ g_assert(cpus);
+
+ while ((e = qlist_pop(cpus))) {
+ QDict *cpu, *props;
+ int64_t thread, node;
+
+ cpu = qobject_to_qdict(e);
+ g_assert(qdict_haskey(cpu, "props"));
+ props = qdict_get_qdict(cpu, "props");
+
+ g_assert(qdict_haskey(props, "node-id"));
+ node = qdict_get_int(props, "node-id");
+ g_assert(qdict_haskey(props, "thread-id"));
+ thread = qdict_get_int(props, "thread-id");
+
+ if (thread == 0) {
+ g_assert_cmpint(node, ==, 1);
+ } else if (thread == 1) {
+ g_assert_cmpint(node, ==, 0);
+ } else {
+ g_assert(false);
+ }
+ }
+
+ QDECREF(resp);
+ qtest_end();
+ g_free(cli);
+}
+
+int main(int argc, char **argv)
+{
+ const char *args = NULL;
+ const char *arch = qtest_get_arch();
+
+ if (strcmp(arch, "aarch64") == 0) {
+ args = "-machine virt";
+ }
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_data_func("/numa/mon/default", args, test_mon_default);
+ qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit);
+ qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial);
+ qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus);
+
+ if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) {
+ qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu);
+ }
+
+ if (!strcmp(arch, "ppc64")) {
+ qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu);
+ }
+
+ if (!strcmp(arch, "aarch64")) {
+ qtest_add_data_func("/numa/aarch64/cpu/explicit", args,
+ aarch64_numa_cpu);
+ }
+
+ return g_test_run();
+}
g_test_init(&argc, &argv, NULL);
qtest_add_func("/nvme/nop", nop);
- qtest_start("-drive id=drv0,if=none,file=/dev/null,format=raw "
+ qtest_start("-drive id=drv0,if=none,file=null-co://,format=raw "
"-device nvme,drive=drv0,serial=foo");
ret = g_test_run();
struct uffdio_api api_struct;
uint64_t ioctl_mask;
- int ufd = ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
+ int ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
if (ufd == -1) {
g_test_message("Skipping test: userfaultfd not available");
Checking if backing clusters are allocated when they shouldn't
-0/128 sectors allocated at offset 1 MiB
-0/128 sectors allocated at offset 4.001 GiB
+0/65536 bytes allocated at offset 1 MiB
+0/65536 bytes allocated at offset 4.001 GiB
Reading
=== IO: pattern 42
Checking if backing clusters are allocated when they shouldn't
-0/128 sectors allocated at offset 1 MiB
-0/128 sectors allocated at offset 4.001 GiB
+0/65536 bytes allocated at offset 1 MiB
+0/65536 bytes allocated at offset 4.001 GiB
Reading
=== IO: pattern 42
def test_stream_intermediate(self):
self.assert_no_active_block_jobs()
- self.assertNotEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
+ self.assertNotEqual(qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img),
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img),
'image file map matches backing file before streaming')
result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid')
self.assert_no_active_block_jobs()
# The image map is empty before the operation
- empty_map = qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img)
+ empty_map = qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', test_img)
# This is a no-op: no data should ever be copied from the base image
result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
# Check that the maps don't match before the streaming operations
for i in range(2, self.num_imgs, 2):
- self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
+ self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]),
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]),
'image file map matches backing file before streaming')
# Create all streaming jobs
def test_stream_base_node_name(self):
self.assert_no_active_block_jobs()
- self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]),
+ self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]),
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]),
'image file map matches backing file before streaming')
# Error: the base node does not exist
if not iotests.supports_quorum():
return
- self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]),
+ self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]),
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]),
'image file map matches backing file before streaming')
self.assert_no_active_block_jobs()
function verify_io()
{
- if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then
+ if ($QEMU_IMG info -U -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then
# For v2 images, discarded clusters are read from the backing file
# Keep the variable empty so that the backing file value can be used as
# the default below
except OSError:
pass
- def do_prepare_drives(self, fmt, args):
+ def do_prepare_drives(self, fmt, args, attach_target):
self.vm = iotests.VM().add_drive(test_img)
qemu_img('create', '-f', fmt, blockdev_target_img,
str(TestDriveCompression.image_len), *args)
- self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
+ if attach_target:
+ self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
self.vm.launch()
- def do_test_compress_complete(self, cmd, format, **args):
- self.do_prepare_drives(format['type'], format['args'])
+ def do_test_compress_complete(self, cmd, format, attach_target, **args):
+ self.do_prepare_drives(format['type'], format['args'], attach_target)
self.assert_no_active_block_jobs()
def test_complete_compress_drive_backup(self):
for format in TestDriveCompression.fmt_supports_compression:
- self.do_test_compress_complete('drive-backup', format,
+ self.do_test_compress_complete('drive-backup', format, False,
target=blockdev_target_img, mode='existing')
def test_complete_compress_blockdev_backup(self):
for format in TestDriveCompression.fmt_supports_compression:
- self.do_test_compress_complete('blockdev-backup', format, target='drive1')
+ self.do_test_compress_complete('blockdev-backup', format, True,
+ target='drive1')
- def do_test_compress_cancel(self, cmd, format, **args):
- self.do_prepare_drives(format['type'], format['args'])
+ def do_test_compress_cancel(self, cmd, format, attach_target, **args):
+ self.do_prepare_drives(format['type'], format['args'], attach_target)
self.assert_no_active_block_jobs()
def test_compress_cancel_drive_backup(self):
for format in TestDriveCompression.fmt_supports_compression:
- self.do_test_compress_cancel('drive-backup', format,
+ self.do_test_compress_cancel('drive-backup', format, False,
target=blockdev_target_img, mode='existing')
def test_compress_cancel_blockdev_backup(self):
for format in TestDriveCompression.fmt_supports_compression:
- self.do_test_compress_cancel('blockdev-backup', format, target='drive1')
+ self.do_test_compress_cancel('blockdev-backup', format, True,
+ target='drive1')
- def do_test_compress_pause(self, cmd, format, **args):
- self.do_prepare_drives(format['type'], format['args'])
+ def do_test_compress_pause(self, cmd, format, attach_target, **args):
+ self.do_prepare_drives(format['type'], format['args'], attach_target)
self.assert_no_active_block_jobs()
def test_compress_pause_drive_backup(self):
for format in TestDriveCompression.fmt_supports_compression:
- self.do_test_compress_pause('drive-backup', format,
+ self.do_test_compress_pause('drive-backup', format, False,
target=blockdev_target_img, mode='existing')
def test_compress_pause_blockdev_backup(self):
for format in TestDriveCompression.fmt_supports_compression:
- self.do_test_compress_pause('blockdev-backup', format, target='drive1')
+ self.do_test_compress_pause('blockdev-backup', format, True,
+ target='drive1')
if __name__ == '__main__':
iotests.main(supported_fmts=['raw', 'qcow2'])
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
+qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed
read failed: Input/output error
=== Testing unaligned pre-allocated zero cluster ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-qcow2: Image is corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed
+qcow2: Image is corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed
read failed: Input/output error
read failed: Input/output error
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed
-qcow2: Marking image as corrupt: Data cluster offset 0x62a00 unaligned (L2 offset: 0x40000, L2 index: 0x1); further corruption events will be suppressed
+qcow2: Marking image as corrupt: Cluster allocation offset 0x62a00 unaligned (L2 offset: 0x40000, L2 index: 0x1); further corruption events will be suppressed
discard 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read failed: Input/output error
#!/bin/bash
#
-# Test case for discarding preallocated zero clusters in qcow2
+# Test case for preallocated zero clusters in qcow2
#
# Copyright (C) 2013 Red Hat, Inc.
#
$QEMU_IO -c "write 0 256k" -c "write -z 0 256k" -c "write 64M 512" \
-c "discard 0 $IMG_SIZE" -c "read -P 0 0 $IMG_SIZE" "$TEST_IMG" \
| _filter_qemu_io
+
# Check the image (there shouldn't be any leaks)
_check_test_img
+# Map the image (we want all clusters to be gone)
+$QEMU_IMG map "$TEST_IMG"
+
+_cleanup_test_img
+
+
+echo
+echo '=== Writing to preallocated zero clusters ==='
+echo
+
+_make_test_img $IMG_SIZE
+
+# Create data clusters (not aligned to an L2 table)
+$QEMU_IO -c 'write -P 42 1M 256k' "$TEST_IMG" | _filter_qemu_io
+orig_map=$($QEMU_IMG map --output=json "$TEST_IMG")
+
+# Convert the data clusters to preallocated zero clusters
+$QEMU_IO -c 'write -z 1M 256k' "$TEST_IMG" | _filter_qemu_io
+
+# Now write to them (with a COW needed for the head and tail)
+$QEMU_IO -c "write -P 23 $(((1024 + 32) * 1024)) 192k" "$TEST_IMG" \
+ | _filter_qemu_io
+
+# Check metadata correctness
+_check_test_img
+
+# Check data correctness
+$QEMU_IO -c "read -P 0 $(( 1024 * 1024)) 32k" \
+ -c "read -P 23 $(((1024 + 32) * 1024)) 192k" \
+ -c "read -P 0 $(((1024 + 32 + 192) * 1024)) 32k" \
+ "$TEST_IMG" \
+ | _filter_qemu_io
+
+# Check that we have actually reused the original area
+new_map=$($QEMU_IMG map --output=json "$TEST_IMG")
+if [ "$new_map" = "$orig_map" ]; then
+ echo 'Successfully reused original clusters.'
+else
+ echo 'Failed to reuse original clusters.'
+ echo 'Original map:'
+ echo "$orig_map"
+ echo 'New map:'
+ echo "$new_map"
+fi
+
+_cleanup_test_img
+
+
+echo
+echo '=== Writing to a snapshotted preallocated zero cluster ==='
+echo
+
+_make_test_img 64k
+
+# Create a preallocated zero cluster
+$QEMU_IO -c 'write -P 42 0 64k' -c 'write -z 0 64k' "$TEST_IMG" \
+ | _filter_qemu_io
+
+# Snapshot it
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+
+# Write to the cluster
+$QEMU_IO -c 'write -P 23 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+# Check metadata correctness
+_check_test_img
+
+# Check data correctness
+$QEMU_IO -c 'read -P 23 0 64k' "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -a foo "$TEST_IMG"
+$QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+_cleanup_test_img
+
+
+echo
+echo '=== Consecutive write to a preallocated zero cluster ==='
+echo
+
+_make_test_img 192k
+
+# Create three normal clusters
+$QEMU_IO -c 'write -P 42 0 192k' "$TEST_IMG" | _filter_qemu_io
+orig_map=$($QEMU_IMG map --output=json "$TEST_IMG")
+
+# Make the middle cluster a preallocated zero cluster
+$QEMU_IO -c 'write -z 64k 64k' "$TEST_IMG" | _filter_qemu_io
+
+# Try to overwrite everything: This should reuse the whole range. To test that
+# this only issues a single continuous write request, use blkdebug.
+$QEMU_IO -c 'write -P 42 0 192k' \
+ "json:{
+ 'driver': '$IMGFMT',
+ 'file': {
+ 'driver': 'blkdebug',
+ 'image.filename': '$TEST_IMG',
+ 'set-state': [{
+ 'event': 'write_aio',
+ 'new_state': 2
+ }],
+ 'inject-error': [{
+ 'event': 'write_aio',
+ 'state': 2
+ }]
+ }
+ }" \
+ | _filter_qemu_io
+
+# Check metadata correctness
+_check_test_img
+
+# Check that we have actually reused the original area
+new_map=$($QEMU_IMG map --output=json "$TEST_IMG")
+if [ "$new_map" = "$orig_map" ]; then
+ echo 'Successfully reused original clusters.'
+else
+ echo 'Failed to reuse original clusters.'
+ echo 'Original map:'
+ echo "$orig_map"
+ echo 'New map:'
+ echo "$new_map"
+fi
+
+_cleanup_test_img
+
# success, all done
echo "*** done"
read 67109376/67109376 bytes at offset 0
64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
+Offset Length Mapped to File
+
+=== Writing to preallocated zero clusters ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67109376
+wrote 262144/262144 bytes at offset 1048576
+256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 262144/262144 bytes at offset 1048576
+256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 196608/196608 bytes at offset 1081344
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 32768/32768 bytes at offset 1048576
+32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 196608/196608 bytes at offset 1081344
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 32768/32768 bytes at offset 1277952
+32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Successfully reused original clusters.
+
+=== Writing to a snapshotted preallocated zero cluster ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Consecutive write to a preallocated zero cluster ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=196608
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 196608/196608 bytes at offset 0
+192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+Successfully reused original clusters.
*** done
rm -f "${TEST_DIR}/${i}-${snapshot_virt0}"
rm -f "${TEST_DIR}/${i}-${snapshot_virt1}"
done
- rm -f "${TEST_IMG}.1" "${TEST_IMG}.2"
+ rm -f "${TEST_IMG}" "${TEST_IMG}.1" "${TEST_IMG}.2" "${TEST_IMG}.base"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
}
# ${1}: unique identifier for the snapshot filename
-# ${2}: true: open backing images; false: don't open them (default)
+# ${2}: extra_params to the blockdev-add command
+# ${3}: filename
+function do_blockdev_add()
+{
+ cmd="{ 'execute': 'blockdev-add', 'arguments':
+ { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${2}
+ 'file':
+ { 'driver': 'file', 'filename': '${3}',
+ 'node-name': 'file_${1}' } } }"
+ _send_qemu_cmd $h "${cmd}" "return"
+}
+
+# ${1}: unique identifier for the snapshot filename
function add_snapshot_image()
{
- if [ "${2}" = "true" ]; then
- extra_params=""
- else
- extra_params="'backing': '', "
- fi
base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}"
snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}"
_make_test_img -b "${base_image}" "$size"
mv "${TEST_IMG}" "${snapshot_file}"
- cmd="{ 'execute': 'blockdev-add', 'arguments':
- { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params}
- 'file':
- { 'driver': 'file', 'filename': '${snapshot_file}',
- 'node-name': 'file_${1}' } } }"
- _send_qemu_cmd $h "${cmd}" "return"
+ do_blockdev_add "$1" "'backing': '', " "${snapshot_file}"
}
# ${1}: unique identifier for the snapshot filename
echo
SNAPSHOTS=$((${SNAPSHOTS}+1))
-add_snapshot_image ${SNAPSHOTS} true
+
+TEST_IMG="$TEST_IMG.base" _make_test_img "$size"
+_make_test_img -b "${TEST_IMG}.base" "$size"
+do_blockdev_add ${SNAPSHOTS} "" "${TEST_IMG}"
blockdev_snapshot ${SNAPSHOTS} error
echo
=== Invalid command - snapshot node has a backing image ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/12-snapshot-v0.IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
{"return": {}}
{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}}
"driver": "$IMGFMT",
"node-name": "disk",
"file": {
- "driver": "file",
- "filename": "$TEST_IMG"
+ "driver": "null-co"
}
}
}
"driver": "$IMGFMT",
"node-name": "test-node",
"file": {
- "driver": "file",
- "filename": "$TEST_IMG"
+ "driver": "null-co"
}
}
}
echo "vm2: flush io, and quit"
_send_qemu_cmd $h2 'qemu-io disk flush' "(qemu)"
_send_qemu_cmd $h2 'quit' ""
+_send_qemu_cmd $h1 'quit' ""
+wait
echo "Check image pattern"
${QEMU_IO} -c "read -P 0x22 0 4M" "${TEST_IMG}" | _filter_testdir | _filter_qemu_io
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Image resized.
-[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
+64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0)
Offset Length Mapped to File
=== Testing map on an image file truncated outside of qemu ===
Image resized.
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qemu-io drv0 map
-[ 0] 128/ 128 sectors allocated at offset 0 bytes (1)
+64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0)
*** done
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 63963136/63963136 bytes at offset 3145728
61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
convert -c -S 0:
read 3145728/3145728 bytes at offset 0
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 33554432/33554432 bytes at offset 33554432
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
convert -c -S 0 with source backing file:
read 3145728/3145728 bytes at offset 0
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 33554432/33554432 bytes at offset 33554432
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
convert -c -S 0 -B ...
read 3145728/3145728 bytes at offset 0
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
convert -S 4k
-[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 8192},
+[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false},
-{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 9216},
+{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
-{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 10240},
+{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
convert -c -S 4k
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
convert -S 8k
-[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": 8192},
+[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
-{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 17408},
+{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
convert -c -S 8k
=== Testing VPC Autodetect ===
-[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
+126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
=== Testing VPC with current_size force ===
-[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
+127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
=== Testing VPC with chs force ===
-[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
+126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
=== Testing Hyper-V Autodetect ===
-[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
+127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
=== Testing Hyper-V with current_size force ===
-[ 0] 266338304/ 266338304 sectors not allocated at offset 0 bytes (0)
+127 GiB (0x1fc0000000) bytes not allocated at offset 0 bytes (0x0)
=== Testing Hyper-V with chs force ===
-[ 0] 266334240/ 266334240 sectors not allocated at offset 0 bytes (0)
+126.998 GiB (0x1fbfe04000) bytes not allocated at offset 0 bytes (0x0)
=== Testing d2v Autodetect ===
-[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
+251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
=== Testing d2v with current_size force ===
-[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
+251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
=== Testing d2v with chs force ===
-[ 0] 514560/ 514560 sectors allocated at offset 0 bytes (1)
+251.250 MiB (0xfb40000) bytes allocated at offset 0 bytes (0x0)
=== Testing Image create, default ===
=== Read created image, default opts ====
-[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
+4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
=== Read created image, force_size_calc=chs ====
-[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
+4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
=== Read created image, force_size_calc=current_size ====
-[ 0] 8389584/ 8389584 sectors not allocated at offset 0 bytes (0)
+4 GiB (0x10007a000) bytes not allocated at offset 0 bytes (0x0)
=== Testing Image create, force_size ===
=== Read created image, default opts ====
-[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
+4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
=== Read created image, force_size_calc=chs ====
-[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
+4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
=== Read created image, force_size_calc=current_size ====
-[ 0] 8388608/ 8388608 sectors not allocated at offset 0 bytes (0)
+4 GiB (0x100000000) bytes not allocated at offset 0 bytes (0x0)
*** done
--- /dev/null
+#!/bin/bash
+#
+# Test image locking
+#
+# Copyright 2016, 2017 Red Hat, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=famz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+ rm -f "${TEST_IMG}.base"
+ rm -f "${TEST_IMG}.convert"
+ rm -f "${TEST_IMG}.a"
+ rm -f "${TEST_IMG}.b"
+ rm -f "${TEST_IMG}.lnk"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+. ./common.qemu
+
+size=32M
+
+_check_ofd()
+{
+ _make_test_img $size >/dev/null
+ if $QEMU_IMG_PROG info --image-opts "driver=file,locking=on,filename=$TEST_IMG" 2>&1 |
+ grep -q 'falling back to POSIX file'; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+_check_ofd || _notrun "OFD lock not available"
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+_run_cmd()
+{
+ echo
+ (echo "$@"; "$@" 2>&1 1>/dev/null) | _filter_testdir
+}
+
+function _do_run_qemu()
+{
+ (
+ if ! test -t 0; then
+ while read cmd; do
+ echo $cmd
+ done
+ fi
+ echo quit
+ ) | $QEMU -nographic -monitor stdio -serial none "$@" 1>/dev/null
+}
+
+function _run_qemu_with_images()
+{
+ _do_run_qemu \
+ $(for i in $@; do echo "-drive if=none,file=$i"; done) 2>&1 \
+ | _filter_testdir | _filter_qemu
+}
+
+echo "== readonly=off,force-share=on should be rejected =="
+_run_qemu_with_images null-co://,readonly=off,force-share=on
+
+for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
+ echo
+ echo "== Creating base image =="
+ TEST_IMG="${TEST_IMG}.base" _make_test_img $size
+
+ echo
+ echo "== Creating test image =="
+ $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" -b ${TEST_IMG}.base | _filter_img_create
+
+ echo
+ echo "== Launching QEMU, opts: '$opts1' =="
+ _launch_qemu -drive file="${TEST_IMG}",if=none,$opts1
+ h=$QEMU_HANDLE
+
+ for opts2 in "" "read-only=on" "read-only=on,force-share=on"; do
+ echo
+ echo "== Launching another QEMU, opts: '$opts2' =="
+ echo "quit" | \
+ $QEMU -nographic -monitor stdio \
+ -drive file="${TEST_IMG}",if=none,$opts2 2>&1 1>/dev/null | \
+ _filter_testdir | _filter_qemu
+ done
+
+ for L in "" "-U"; do
+
+ echo
+ echo "== Running utility commands $L =="
+ _run_cmd $QEMU_IO $L -c "read 0 512" "${TEST_IMG}"
+ _run_cmd $QEMU_IO $L -r -c "read 0 512" "${TEST_IMG}"
+ _run_cmd $QEMU_IO -c "open $L ${TEST_IMG}" -c "read 0 512"
+ _run_cmd $QEMU_IO -c "open -r $L ${TEST_IMG}" -c "read 0 512"
+ _run_cmd $QEMU_IMG info $L "${TEST_IMG}"
+ _run_cmd $QEMU_IMG check $L "${TEST_IMG}"
+ _run_cmd $QEMU_IMG compare $L "${TEST_IMG}" "${TEST_IMG}"
+ _run_cmd $QEMU_IMG map $L "${TEST_IMG}"
+ _run_cmd $QEMU_IMG amend -o "" $L "${TEST_IMG}"
+ _run_cmd $QEMU_IMG commit $L "${TEST_IMG}"
+ _run_cmd $QEMU_IMG resize $L "${TEST_IMG}" $size
+ _run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base"
+ _run_cmd $QEMU_IMG snapshot -l $L "${TEST_IMG}"
+ _run_cmd $QEMU_IMG convert $L "${TEST_IMG}" "${TEST_IMG}.convert"
+ _run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1
+ _run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}"
+ _run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}"
+ done
+ _send_qemu_cmd $h "{ 'execute': 'quit', }" ""
+ echo
+ echo "Round done"
+ _cleanup_qemu
+done
+
+for opt1 in $test_opts; do
+ for opt2 in $test_opts; do
+ echo
+ echo "== Two devices with the same image ($opt1 - $opt2) =="
+ _run_qemu_with_images "${TEST_IMG},$opt1" "${TEST_IMG},$opt2"
+ done
+done
+
+echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir
+(
+ $QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}"
+ $QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}"
+ $QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b"
+) | _filter_img_create
+
+echo
+echo "== Two devices sharing the same file in backing chain =="
+_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG}.b"
+_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG}.c"
+
+echo
+echo "== Backing image also as an active device =="
+_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG}"
+
+echo
+echo "== Backing image also as an active device (ro) =="
+_run_qemu_with_images "${TEST_IMG}.a" "${TEST_IMG},readonly=on"
+
+echo
+echo "== Symbolic link =="
+rm -f "${TEST_IMG}.lnk" &>/dev/null
+ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
+_run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
+
+echo
+echo "== Closing an image should unlock it =="
+_launch_qemu
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'qmp_capabilities' }" \
+ 'return'
+
+echo "Adding drive"
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line': 'drive_add 0 if=none,id=d0,file=${TEST_IMG}' } }" \
+ ""
+
+_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
+
+echo "Closing drive"
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line': 'drive_del d0' } }" \
+ ""
+
+_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
+
+echo "Adding two and closing one"
+for d in d0 d1; do
+ _send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line': 'drive_add 0 if=none,id=$d,file=${TEST_IMG},readonly=on' } }" \
+ ""
+done
+
+_run_cmd $QEMU_IMG info "${TEST_IMG}"
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line': 'drive_del d0' } }" \
+ ""
+
+_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
+
+echo "Closing the other"
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'human-monitor-command',
+ 'arguments': { 'command-line': 'drive_del d1' } }" \
+ ""
+
+_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
+
+_cleanup_qemu
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
--- /dev/null
+QA output created by 153
+== readonly=off,force-share=on should be rejected ==
+QEMU_PROG: -drive if=none,file=null-co://,readonly=off,force-share=on: force-share=on can only be used with read-only images
+
+== Creating base image ==
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
+
+== Creating test image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+
+== Launching QEMU, opts: '' ==
+
+== Launching another QEMU, opts: '' ==
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock
+Is another process using the image?
+
+== Launching another QEMU, opts: 'read-only=on' ==
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,read-only=on: Failed to get shared "write" lock
+Is another process using the image?
+
+== Launching another QEMU, opts: 'read-only=on,force-share=on' ==
+
+== Running utility commands ==
+
+_qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2
+can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+
+_qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
+can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
+can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+
+_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
+can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper info TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper check TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper map TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper commit TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+== Running utility commands -U ==
+
+_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
+can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
+
+_qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
+
+_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
+can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
+
+_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
+
+_qemu_img_wrapper info -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper check -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper map -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper commit -U TEST_DIR/t.qcow2
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper convert -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
+
+_qemu_img_wrapper dd -U if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
+
+_qemu_img_wrapper bench -U -c 1 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images
+
+Round done
+
+== Creating base image ==
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
+
+== Creating test image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+
+== Launching QEMU, opts: 'read-only=on' ==
+
+== Launching another QEMU, opts: '' ==
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock
+Is another process using the image?
+
+== Launching another QEMU, opts: 'read-only=on' ==
+
+== Launching another QEMU, opts: 'read-only=on,force-share=on' ==
+
+== Running utility commands ==
+
+_qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2
+can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+
+_qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
+
+_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
+can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+
+_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
+
+_qemu_img_wrapper info TEST_DIR/t.qcow2
+
+_qemu_img_wrapper check TEST_DIR/t.qcow2
+
+_qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper map TEST_DIR/t.qcow2
+
+_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper commit TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
+
+_qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
+
+_qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
+
+_qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+== Running utility commands -U ==
+
+_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
+can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
+
+_qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
+
+_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
+can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
+
+_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
+
+_qemu_img_wrapper info -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper check -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper map -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper commit -U TEST_DIR/t.qcow2
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
+Is another process using the image?
+
+_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper convert -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
+
+_qemu_img_wrapper dd -U if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
+
+_qemu_img_wrapper bench -U -c 1 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images
+
+Round done
+
+== Creating base image ==
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
+
+== Creating test image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+
+== Launching QEMU, opts: 'read-only=on,force-share=on' ==
+
+== Launching another QEMU, opts: '' ==
+
+== Launching another QEMU, opts: 'read-only=on' ==
+
+== Launching another QEMU, opts: 'read-only=on,force-share=on' ==
+
+== Running utility commands ==
+
+_qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2
+
+_qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
+
+_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
+
+_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
+
+_qemu_img_wrapper info TEST_DIR/t.qcow2
+
+_qemu_img_wrapper check TEST_DIR/t.qcow2
+
+_qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper map TEST_DIR/t.qcow2
+
+_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
+
+_qemu_img_wrapper commit TEST_DIR/t.qcow2
+
+_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
+
+_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+
+_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
+
+_qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
+
+_qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
+
+_qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
+
+== Running utility commands -U ==
+
+_qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2
+can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
+
+_qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
+
+_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
+can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
+
+_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
+
+_qemu_img_wrapper info -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper check -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper map -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper commit -U TEST_DIR/t.qcow2
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M
+qemu-img: unrecognized option '-U'
+Try 'qemu-img --help' for more information
+
+_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+
+_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
+
+_qemu_img_wrapper convert -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert
+
+_qemu_img_wrapper dd -U if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1
+
+_qemu_img_wrapper bench -U -c 1 TEST_DIR/t.qcow2
+
+_qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2
+qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images
+
+Round done
+== Creating TEST_DIR/t.qcow2.[abc] ==
+Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.c', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.b
+
+== Two devices sharing the same file in backing chain ==
+
+== Backing image also as an active device ==
+QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+
+== Backing image also as an active device (ro) ==
+
+== Symbolic link ==
+QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+
+== Closing an image should unlock it ==
+{"return": {}}
+Adding drive
+
+_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
+can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+Closing drive
+
+_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
+Adding two and closing one
+
+_qemu_img_wrapper info TEST_DIR/t.qcow2
+
+_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
+can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
+Is another process using the image?
+Closing the other
+
+_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
+*** done
#
# qcow2 specific bdrv_pwrite_zeroes tests with backing files (complements 034)
#
-# Copyright (C) 2016 Red Hat, Inc.
+# Copyright (C) 2016-2017 Red Hat, Inc.
#
# 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
_supported_os Linux
CLUSTER_SIZE=4k
-size=128M
+size=$((128 * 1024 * 1024))
+
+# This test requires zero clusters, added in v3 images
+_unsupported_imgopts compat=0.10
echo
echo == backing file contains zeros ==
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+echo
+echo == unaligned image tail cluster, no allocation needed ==
+
+# With no backing file, write to all or part of unallocated partial cluster
+# will mark the cluster as zero, but does not allocate.
+# Re-create the image each time to get back to unallocated clusters.
+
+# Write at the front: sector-wise, the request is: 128m... | 00 -- -- --
+_make_test_img $((size + 2048))
+$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write at the back: sector-wise, the request is: 128m... | -- -- -- 00
+_make_test_img $((size + 2048))
+$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write at middle: sector-wise, the request is: 128m... | -- 00 00 --
+_make_test_img $((size + 2048))
+$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write entire cluster: sector-wise, the request is: 128m... | 00 00 00 00
+_make_test_img $((size + 2048))
+$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Repeat with backing file holding unallocated cluster.
+# TODO: Note that this forces an allocation, because we aren't yet able to
+# quickly detect that reads beyond EOF of the backing file are always zero
+CLUSTER_SIZE=2048 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
+
+# Write at the front: sector-wise, the request is:
+# backing: 128m... | -- --
+# active: 128m... | 00 -- -- --
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write at the back: sector-wise, the request is:
+# backing: 128m... | -- --
+# active: 128m... | -- -- -- 00
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write at middle: sector-wise, the request is:
+# backing: 128m... | -- --
+# active: 128m... | -- 00 00 --
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write entire cluster: sector-wise, the request is:
+# backing: 128m... | -- --
+# active: 128m... | 00 00 00 00
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Repeat with backing file holding zero'd cluster
+# TODO: Note that this forces an allocation, because we aren't yet able to
+# quickly detect that reads beyond EOF of the backing file are always zero
+$QEMU_IO -c "write -z $size 512" "$TEST_IMG.base" | _filter_qemu_io
+
+# Write at the front: sector-wise, the request is:
+# backing: 128m... | 00 00
+# active: 128m... | 00 -- -- --
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write at the back: sector-wise, the request is:
+# backing: 128m... | 00 00
+# active: 128m... | -- -- -- 00
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write at middle: sector-wise, the request is:
+# backing: 128m... | 00 00
+# active: 128m... | -- 00 00 --
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Write entire cluster: sector-wise, the request is:
+# backing: 128m... | 00 00
+# active: 128m... | 00 00 00 00
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# A preallocated cluster maintains its allocation, whether it stays as
+# data due to a partial write:
+# Convert 128m... | XX XX => ... | XX 00
+_make_test_img $((size + 1024))
+$QEMU_IO -c "write -P 1 $((size)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# or because it is the entire cluster and can use the zero flag:
+# Convert 128m... | XX XX => ... | 00 00
+$QEMU_IO -c "write -z $((size)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "alloc $size 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $size 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+echo
+echo == unaligned image tail cluster, allocation required ==
+
+# Write beyond backing file must COW
+# Backing file: 128m... | XX --
+# Active layer: 128m... | -- -- 00 --
+CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -P 1 $((size)) 512" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z $((size + 1024)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $((size + 512)) 1536" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Writes at boundaries of (partial) cluster must not lose mid-cluster data
+# Backing file: 128m: ... | -- XX
+# Active layer: 128m: ... | 00 -- -- 00
+CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
+_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+$QEMU_IO -c "write -P 1 $((size + 512)) 512" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z $((size)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 1 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $((size + 1024)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 1 $((size + 512)) 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 $((size + 1024)) 1024" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
# success, all done
echo "*** done"
rm -f $seq.full
read 2048/2048 bytes at offset 67584
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
-{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 36864, "length": 28672, "depth": 1, "zero": true, "data": false},
-{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
+{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 69632, "length": 134148096, "depth": 1, "zero": true, "data": false}]
== backing file contains non-zero data after write_zeroes ==
read 3072/3072 bytes at offset 40960
3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
-{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 36864, "length": 4096, "depth": 1, "zero": true, "data": false},
-{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
+{ "start": 40960, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 45056, "length": 134172672, "depth": 1, "zero": true, "data": false}]
== write_zeroes covers non-zero data ==
read 5120/5120 bytes at offset 68608
5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
-{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 32768, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 36864, "length": 4096, "depth": 0, "zero": true, "data": false},
{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false},
-{ "start": 49152, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
+{ "start": 49152, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 53248, "length": 4096, "depth": 0, "zero": true, "data": false},
{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false},
-{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 28672},
+{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false},
{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}]
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 32768, "depth": 1, "zero": true, "data": false},
{ "start": 32768, "length": 4096, "depth": 0, "zero": true, "data": false},
-{ "start": 36864, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 36864, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 40960, "length": 8192, "depth": 1, "zero": true, "data": false},
{ "start": 49152, "length": 4096, "depth": 0, "zero": true, "data": false},
-{ "start": 53248, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
+{ "start": 53248, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 57344, "length": 8192, "depth": 1, "zero": true, "data": false},
{ "start": 65536, "length": 4096, "depth": 0, "zero": true, "data": false},
-{ "start": 69632, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 28672},
+{ "start": 69632, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 73728, "length": 134144000, "depth": 1, "zero": true, "data": false}]
== spanning two clusters, partially overwriting backing file ==
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 2048/2048 bytes at offset 6144
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": 20480},
+[{ "start": 0, "length": 8192, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 8192, "length": 134209536, "depth": 1, "zero": true, "data": false}]
== spanning multiple clusters, non-zero in first cluster ==
read 10240/10240 bytes at offset 67584
10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
-{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 69632, "length": 8192, "depth": 0, "zero": true, "data": false},
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
{ "start": 65536, "length": 8192, "depth": 0, "zero": true, "data": false},
-{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
== spanning multiple clusters, partially overwriting backing file ==
read 1024/1024 bytes at offset 76800
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 65536, "depth": 1, "zero": true, "data": false},
-{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 20480},
+{ "start": 65536, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 69632, "length": 4096, "depth": 0, "zero": true, "data": false},
-{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": 24576},
+{ "start": 73728, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 77824, "length": 134139904, "depth": 1, "zero": true, "data": false}]
+
+== unaligned image tail cluster, no allocation needed ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
+wrote 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
+wrote 512/512 bytes at offset 134219264
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
+wrote 1024/1024 bytes at offset 134218240
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776
+wrote 2048/2048 bytes at offset 134217728
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 512/512 bytes at offset 134219264
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 1024/1024 bytes at offset 134218240
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 2048/2048 bytes at offset 134217728
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
+wrote 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 512/512 bytes at offset 134219264
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 1024/1024 bytes at offset 134218240
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 2048/2048 bytes at offset 134217728
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2048/2048 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134218752
+wrote 1024/1024 bytes at offset 134217728
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 134218240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134218240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+1024/1024 bytes allocated at offset 128 MiB
+[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false},
+{ "start": 134217728, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+wrote 1024/1024 bytes at offset 134217728
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+1024/1024 bytes allocated at offset 128 MiB
+read 1024/1024 bytes at offset 134217728
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false},
+{ "start": 134217728, "length": 1024, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
+
+== unaligned image tail cluster, allocation required ==
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 134218752
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 134218240
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 512/512 bytes at offset 134218240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134218240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 134218752
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 134219264
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134217728
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 134218240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 134218752
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
+{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
*** done
_cleanup()
{
_cleanup_test_img
+ rm -f "$TEST_IMG.2"
+ rm -f "$TEST_IMG.3"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
_make_test_img $size
+TEST_IMG="$TEST_IMG.2" _make_test_img $size
+TEST_IMG="$TEST_IMG.3" _make_test_img $size
+
# Default drive semantics:
#
# By default you get a single empty floppy drive. You can override it with
check_floppy_qtree -fda "$TEST_IMG"
check_floppy_qtree -fdb "$TEST_IMG"
-check_floppy_qtree -fda "$TEST_IMG" -fdb "$TEST_IMG"
+check_floppy_qtree -fda "$TEST_IMG" -fdb "$TEST_IMG.2"
echo
check_floppy_qtree -drive if=floppy,file="$TEST_IMG"
check_floppy_qtree -drive if=floppy,file="$TEST_IMG",index=1
-check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=floppy,file="$TEST_IMG",index=1
+check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=floppy,file="$TEST_IMG.2",index=1
echo
echo
check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0
check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
echo
check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-device floppy,drive=none0 -device floppy,drive=none1,unit=1
echo
echo === Mixing -fdX and -global ===
# Working
-check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0
-check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0
+check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveB=none0
+check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveA=none0
# Conflicting (-fdX wins)
-check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0
-check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0
+check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveA=none0
+check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -global isa-fdc.driveB=none0
echo
echo
echo === Mixing -fdX and -device ===
# Working
-check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
-check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
+check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0
+check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=1
-check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
-check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0
+check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0
+check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=0
# Conflicting
-check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0
-check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
+check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=0
+check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=1
echo
echo
echo === Mixing -drive and -device ===
# Working
-check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0
-check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1
+check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0
+check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=1
# Conflicting
-check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0
+check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" -device floppy,drive=none0,unit=0
echo
echo
echo === Mixing -global and -device ===
# Working
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveA=none0 -device floppy,drive=none1
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveB=none0 -device floppy,drive=none1
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
# Conflicting
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
-check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \
+check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG.2" \
-global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1
echo
# Working
check_floppy_qtree -drive if=floppy,file="$TEST_IMG" \
- -drive if=none,file="$TEST_IMG" \
- -drive if=none,file="$TEST_IMG" \
+ -drive if=none,file="$TEST_IMG.2" \
+ -drive if=none,file="$TEST_IMG.3" \
-global isa-fdc.driveB=none0 \
-device floppy,drive=none1
QA output created by 172
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=737280
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=737280
+Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=737280
=== Default ===
share-rw = false
drive-type = "288"
-Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
+Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "288"
-Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
+Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2.2,index=1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
=== Mixing -fdX and -global ===
-Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
+Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
+Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
+Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
+Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
=== Mixing -fdX and -device ===
-Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
+Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
+Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
+Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
+Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
+Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=0
QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use
QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed.
-Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
+Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=1
QEMU_PROG: -device floppy,drive=none0,unit=1: Floppy unit 1 is in use
QEMU_PROG: -device floppy,drive=none0,unit=1: Device initialization failed.
=== Mixing -drive and -device ===
-Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
+Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
+Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
+Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device floppy,drive=none0,unit=0
QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use
QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed.
=== Mixing -global and -device ===
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -device floppy,drive=none1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
dev: isa-fdc, id ""
iobase = 1008 (0x3f0)
share-rw = false
drive-type = "144"
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
QEMU_PROG: -device floppy,drive=none1,unit=0: Floppy unit 0 is in use
QEMU_PROG: -device floppy,drive=none1,unit=0: Device initialization failed.
-Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1
+Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1
QEMU_PROG: -device floppy,drive=none1,unit=1: Floppy unit 1 is in use
QEMU_PROG: -device floppy,drive=none1,unit=1: Device initialization failed.
=== Too many floppy drives ===
-Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
+Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -drive if=none,file=TEST_DIR/t.qcow2.3 -global isa-fdc.driveB=none0 -device floppy,drive=none1
QEMU_PROG: -device floppy,drive=none1: Can't create floppy unit 2, bus supports only 2 units
QEMU_PROG: -device floppy,drive=none1: Device initialization failed.
--- /dev/null
+#!/bin/bash
+#
+# Test corner cases with unusual block geometries
+#
+# Copyright (C) 2016-2017 Red Hat, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=eblake@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+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
+
+CLUSTER_SIZE=1M
+size=128M
+options=driver=blkdebug,image.driver=qcow2
+
+echo
+echo "== setting up files =="
+
+TEST_IMG="$TEST_IMG.base" _make_test_img $size
+$QEMU_IO -c "write -P 11 0 $size" "$TEST_IMG.base" | _filter_qemu_io
+_make_test_img -b "$TEST_IMG.base"
+$QEMU_IO -c "write -P 22 0 $size" "$TEST_IMG" | _filter_qemu_io
+
+# Limited to 64k max-transfer
+echo
+echo "== constrained alignment and max-transfer =="
+limits=align=4k,max-transfer=64k
+$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
+ -c "write -P 33 1000 128k" -c "read -P 33 1000 128k" | _filter_qemu_io
+
+echo
+echo "== write zero with constrained max-transfer =="
+limits=align=512,max-transfer=64k,opt-write-zero=$CLUSTER_SIZE
+$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
+ -c "write -z 8003584 2093056" | _filter_qemu_io
+
+# non-power-of-2 write-zero/discard alignments
+echo
+echo "== non-power-of-2 write zeroes limits =="
+
+limits=align=512,opt-write-zero=15M,max-write-zero=15M,opt-discard=15M,max-discard=15M
+$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
+ -c "write -z 32M 32M" | _filter_qemu_io
+
+echo
+echo "== non-power-of-2 discard limits =="
+
+limits=align=512,opt-write-zero=15M,max-write-zero=15M,opt-discard=15M,max-discard=15M
+$QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \
+ -c "discard 80000001 30M" | _filter_qemu_io
+
+echo
+echo "== verify image content =="
+
+function verify_io()
+{
+ if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" |
+ grep "compat: 0.10" > /dev/null); then
+ # For v2 images, discarded clusters are read from the backing file
+ discarded=11
+ else
+ # Discarded clusters are zeroed for v3 or later
+ discarded=0
+ fi
+
+ echo read -P 22 0 1000
+ echo read -P 33 1000 128k
+ echo read -P 22 132072 7871512
+ echo read -P 0 8003584 2093056
+ echo read -P 22 10096640 23457792
+ echo read -P 0 32M 32M
+ echo read -P 22 64M 13M
+ echo read -P $discarded 77M 29M
+ echo read -P 22 106M 22M
+}
+
+verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_io
+
+_check_test_img
+
+# success, all done
+echo "*** done"
+status=0
--- /dev/null
+QA output created by 177
+
+== setting up files ==
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
+wrote 134217728/134217728 bytes at offset 0
+128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 134217728/134217728 bytes at offset 0
+128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== constrained alignment and max-transfer ==
+wrote 131072/131072 bytes at offset 1000
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 131072/131072 bytes at offset 1000
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== write zero with constrained max-transfer ==
+wrote 2093056/2093056 bytes at offset 8003584
+1.996 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== non-power-of-2 write zeroes limits ==
+wrote 33554432/33554432 bytes at offset 33554432
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== non-power-of-2 discard limits ==
+discard 31457280/31457280 bytes at offset 80000001
+30 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify image content ==
+read 1000/1000 bytes at offset 0
+1000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 131072/131072 bytes at offset 1000
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 7871512/7871512 bytes at offset 132072
+7.507 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2093056/2093056 bytes at offset 8003584
+1.996 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 23457792/23457792 bytes at offset 10096640
+22.371 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 33554432/33554432 bytes at offset 33554432
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 13631488/13631488 bytes at offset 67108864
+13 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 30408704/30408704 bytes at offset 80740352
+29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 23068672/23068672 bytes at offset 111149056
+22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+*** done
--- /dev/null
+#!/bin/bash
+#
+# Test case for write zeroes with unmap
+#
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=eblake@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+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
+
+# v2 images can't mark clusters as zero
+_unsupported_imgopts compat=0.10
+
+echo
+echo '=== Testing write zeroes with unmap ==='
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+_make_test_img -b "$TEST_IMG.base"
+
+# Offsets chosen at or near 2M boundaries so test works at all cluster sizes
+# 8k and larger (smaller clusters fail due to non-contiguous allocations)
+
+# Aligned writes to unallocated cluster should not allocate mapping, but must
+# mark cluster as zero, whether or not unmap was requested
+$QEMU_IO -c "write -z -u 2M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z 6M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
+
+# Unaligned writes need not allocate mapping if the cluster already reads
+# as zero, but must mark cluster as zero, whether or not unmap was requested
+$QEMU_IO -c "write -z -u 10485761 2097150" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z 14680065 2097150" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
+
+# Requesting unmap of normal data must deallocate; omitting unmap should
+# preserve the mapping
+$QEMU_IO -c "write 18M 14M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z -u 20M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z 24M 6M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
+
+# Likewise when writing on already-mapped zero data
+$QEMU_IO -c "write -z -u 26M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z 28M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
+
+# Writing on unmapped zeroes does not allocate
+$QEMU_IO -c "write -z 32M 8M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z -u 34M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z 36M 2M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "map" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map
+
+# Writing zero overrides a backing file, regardless of backing cluster type
+$QEMU_IO -c "write -z 40M 8M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write 48M 8M" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "write -z -u 42M 2M" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z 44M 2M" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z -u 50M 2M" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z 52M 2M" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z -u 58M 2M" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write -z 60M 2M" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "map" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+
+# Final check that mappings are correct and images are still sane
+TEST_IMG="$TEST_IMG.base" _check_test_img
+_check_test_img
+
+echo
+echo '=== Testing cache optimization ==='
+echo
+
+BLKDBG_TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG.base"
+
+cat > "$TEST_DIR/blkdebug.conf" <<EOF
+[inject-error]
+event = "l2_update"
+errno = "5"
+immediately = "on"
+once = "off"
+EOF
+
+# None of the following writes should trigger an L2 update, because the
+# cluster already reads as zero, and we don't have to change allocation
+$QEMU_IO -c "w -z -u 20M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "w -z 20M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "w -z 28M 2M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo '*** done'
+status=0
--- /dev/null
+QA output created by 179
+
+=== Testing write zeroes with unmap ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+wrote 2097152/2097152 bytes at offset 2097152
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 6291456
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
+2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
+2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
+2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
+56 MiB (0x3800000) bytes not allocated at offset 8 MiB (0x800000)
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
+wrote 2097150/2097150 bytes at offset 10485761
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097150/2097150 bytes at offset 14680065
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
+2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
+2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
+2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
+2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
+2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
+2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
+2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
+48 MiB (0x3000000) bytes not allocated at offset 16 MiB (0x1000000)
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}]
+wrote 14680064/14680064 bytes at offset 18874368
+14 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 20971520
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 6291456/6291456 bytes at offset 25165824
+6 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
+2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
+2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
+2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
+2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
+2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
+2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
+2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
+2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
+14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000)
+32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000)
+[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
+{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
+{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 6291456, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
+wrote 2097152/2097152 bytes at offset 27262976
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 29360128
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
+2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
+2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
+2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
+2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
+2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
+2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
+2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
+2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
+14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000)
+32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000)
+[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
+{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
+{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 27262976, "length": 2097152, "depth": 0, "zero": true, "data": false},
+{ "start": 29360128, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
+wrote 8388608/8388608 bytes at offset 33554432
+8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 35651584
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 37748736
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+2 MiB (0x200000) bytes not allocated at offset 0 bytes (0x0)
+2 MiB (0x200000) bytes allocated at offset 2 MiB (0x200000)
+2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000)
+2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000)
+2 MiB (0x200000) bytes not allocated at offset 8 MiB (0x800000)
+2 MiB (0x200000) bytes allocated at offset 10 MiB (0xa00000)
+2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000)
+2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000)
+2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000)
+22 MiB (0x1600000) bytes allocated at offset 18 MiB (0x1200000)
+24 MiB (0x1800000) bytes not allocated at offset 40 MiB (0x2800000)
+[{ "start": 0, "length": 18874368, "depth": 0, "zero": true, "data": false},
+{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 0, "zero": true, "data": false},
+{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 27262976, "length": 2097152, "depth": 0, "zero": true, "data": false},
+{ "start": 29360128, "length": 2097152, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 33554432, "depth": 0, "zero": true, "data": false}]
+wrote 8388608/8388608 bytes at offset 41943040
+8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8388608/8388608 bytes at offset 50331648
+8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 44040192
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 46137344
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 52428800
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 54525952
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 60817408
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 62914560
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+42 MiB (0x2a00000) bytes not allocated at offset 0 bytes (0x0)
+4 MiB (0x400000) bytes allocated at offset 42 MiB (0x2a00000)
+4 MiB (0x400000) bytes not allocated at offset 46 MiB (0x2e00000)
+4 MiB (0x400000) bytes allocated at offset 50 MiB (0x3200000)
+4 MiB (0x400000) bytes not allocated at offset 54 MiB (0x3600000)
+4 MiB (0x400000) bytes allocated at offset 58 MiB (0x3a00000)
+2 MiB (0x200000) bytes not allocated at offset 62 MiB (0x3e00000)
+[{ "start": 0, "length": 18874368, "depth": 1, "zero": true, "data": false},
+{ "start": 18874368, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 1, "zero": true, "data": false},
+{ "start": 23068672, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 2097152, "depth": 1, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 27262976, "length": 2097152, "depth": 1, "zero": true, "data": false},
+{ "start": 29360128, "length": 2097152, "depth": 1, "zero": true, "data": false, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 10485760, "depth": 1, "zero": true, "data": false},
+{ "start": 44040192, "length": 4194304, "depth": 0, "zero": true, "data": false},
+{ "start": 48234496, "length": 2097152, "depth": 1, "zero": true, "data": false},
+{ "start": 50331648, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 52428800, "length": 4194304, "depth": 0, "zero": true, "data": false},
+{ "start": 56623104, "length": 2097152, "depth": 1, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 58720256, "length": 2097152, "depth": 1, "zero": true, "data": false},
+{ "start": 60817408, "length": 4194304, "depth": 0, "zero": true, "data": false},
+{ "start": 65011712, "length": 2097152, "depth": 1, "zero": true, "data": false}]
+No errors were found on the image.
+No errors were found on the image.
+
+=== Testing cache optimization ===
+
+wrote 2097152/2097152 bytes at offset 20971520
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 20971520
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 2097152/2097152 bytes at offset 29360128
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
--- /dev/null
+#!/bin/bash
+#
+# Test image locking for POSIX locks
+#
+# Copyright 2017 Red Hat, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=famz@redhat.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
+. ./common.qemu
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+size=32M
+
+_make_test_img $size
+
+echo "Starting QEMU"
+_launch_qemu -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \
+ -device virtio-blk-pci,drive=drive0
+
+echo
+echo "Starting a second QEMU using the same image should fail"
+echo 'quit' | $QEMU -monitor stdio \
+ -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \
+ -device virtio-blk-pci,drive=drive0 2>&1 | _filter_testdir 2>&1 |
+ _filter_qemu |
+ sed -e '/falling back to POSIX file/d' \
+ -e '/locks can be lost unexpectedly/d'
+
+_cleanup_qemu
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
--- /dev/null
+QA output created by 182
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
+Starting QEMU
+
+Starting a second QEMU using the same image should fail
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock
+Is another process using the image?
+*** done
-e "/log_size: [0-9]\\+/d"
}
-# filter out offsets and file names from qemu-img map
+# filter out offsets and file names from qemu-img map; good for both
+# human and json output
_filter_qemu_img_map()
{
sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
+ -e 's/"offset": [0-9]\+/"offset": OFFSET/g' \
-e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt
}
function do_is_allocated() {
local start=$1
- local size=$(( $2 / 512))
+ local size=$2
local step=$3
local count=$4
149 rw auto sudo
150 rw auto quick
152 rw auto quick
+153 rw auto quick
154 rw auto backing quick
155 rw auto
156 rw auto quick
174 auto
175 auto quick
176 rw auto backing
+177 rw auto quick
+179 rw auto quick
181 rw auto migration
+182 rw auto quick
static ssize_t test_block_read_func(QCryptoBlock *block,
- void *opaque,
size_t offset,
uint8_t *buf,
size_t buflen,
+ void *opaque,
Error **errp)
{
Buffer *header = opaque;
static ssize_t test_block_init_func(QCryptoBlock *block,
- void *opaque,
size_t headerlen,
+ void *opaque,
Error **errp)
{
Buffer *header = opaque;
static ssize_t test_block_write_func(QCryptoBlock *block,
- void *opaque,
size_t offset,
const uint8_t *buf,
size_t buflen,
+ void *opaque,
Error **errp)
{
Buffer *header = opaque;
char *cmdline;
cmdline = g_strdup_printf("driver=replication,mode=primary,node-name=xxx,"
- "file.driver=qcow2,file.file.filename=%s"
+ "file.driver=qcow2,file.file.filename=%s,"
+ "file.file.locking=off"
, p_local_disk);
opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
g_free(cmdline);
Error *local_err = NULL;
/* add s_local_disk and forge S_LOCAL_DISK_ID */
- cmdline = g_strdup_printf("file.filename=%s,driver=qcow2", s_local_disk);
+ cmdline = g_strdup_printf("file.filename=%s,driver=qcow2,"
+ "file.locking=off",
+ s_local_disk);
opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false);
g_free(cmdline);
/* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */
cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s,"
"file.driver=qcow2,file.file.filename=%s,"
+ "file.file.locking=off,"
"file.backing.driver=qcow2,"
"file.backing.file.filename=%s,"
+ "file.backing.file.locking=off,"
"file.backing.backing=%s"
, S_ID, s_active_disk, s_hidden_disk
, S_LOCAL_DISK_ID);
{
const char *arch = qtest_get_arch();
const char *cmd = "-device piix3-usb-uhci,id=uhci,addr=1d.0"
- " -drive id=drive0,if=none,file=/dev/null,format=raw"
+ " -drive id=drive0,if=none,file=null-co://,format=raw"
" -device usb-tablet,bus=uhci.0,port=1";
int ret;
qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
qtest_start("-device nec-usb-xhci,id=xhci"
- " -drive id=drive0,if=none,file=/dev/null,format=raw");
+ " -drive id=drive0,if=none,file=null-co://,format=raw");
ret = g_test_run();
qtest_end();
const char *arch = qtest_get_arch();
char *tmp_path;
const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw "
- "-drive if=none,id=drive1,file=/dev/null,format=raw "
+ "-drive if=none,id=drive1,file=null-co://,format=raw "
"-device virtio-blk-pci,id=drv0,drive=drive0,"
"addr=%x.%x";
static QOSState *qvirtio_scsi_start(const char *extra_opts)
{
const char *arch = qtest_get_arch();
- const char *cmd = "-drive id=drv0,if=none,file=/dev/null,format=raw "
+ const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw "
"-device virtio-scsi-pci,id=vs0 "
"-device scsi-hd,bus=vs0.0,drive=drv0 %s";
QDict *response;
QOSState *qs;
- qs = qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw");
+ qs = qvirtio_scsi_start(
+ "-drive id=drv1,if=none,file=null-co://,format=raw");
response = qmp("{\"execute\": \"device_add\","
" \"arguments\": {"
" \"driver\": \"scsi-hd\","
common-obj-y += console-gl.o
common-obj-y += egl-helpers.o
common-obj-y += egl-context.o
+common-obj-y += egl-headless.o
ifeq ($(CONFIG_GTK_GL),y)
common-obj-$(CONFIG_GTK) += gtk-gl-area.o
else
* clicks in the titlebar.
*/
if ([self screenContainsPoint:p]) {
- qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width);
- qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, screen.height);
+ qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, 0, screen.width);
+ qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, 0, screen.height);
}
} else {
qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]);
{
EGLContext ctx;
EGLint ctx_att[] = {
- EGL_CONTEXT_CLIENT_VERSION, params->major_ver,
- EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver,
- EGL_NONE
+ EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
+ EGL_CONTEXT_CLIENT_VERSION, params->major_ver,
+ EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver,
+ EGL_NONE
};
ctx = eglCreateContext(qemu_egl_display, qemu_egl_config,
--- /dev/null
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+#include "ui/egl-helpers.h"
+#include "ui/egl-context.h"
+
+typedef struct egl_dpy {
+ DisplayChangeListener dcl;
+ DisplaySurface *ds;
+ int width, height;
+ GLuint texture;
+ GLuint framebuffer;
+ GLuint blit_texture;
+ GLuint blit_framebuffer;
+ bool y_0_top;
+} egl_dpy;
+
+static void egl_refresh(DisplayChangeListener *dcl)
+{
+ graphic_hw_update(dcl->con);
+}
+
+static void egl_gfx_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+}
+
+static void egl_gfx_switch(DisplayChangeListener *dcl,
+ struct DisplaySurface *new_surface)
+{
+ egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+
+ edpy->ds = new_surface;
+}
+
+static void egl_scanout_disable(DisplayChangeListener *dcl)
+{
+ egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+
+ edpy->texture = 0;
+ /* XXX: delete framebuffers here ??? */
+}
+
+static void egl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+
+ edpy->texture = backing_id;
+ edpy->y_0_top = backing_y_0_top;
+
+ /* source framebuffer */
+ if (!edpy->framebuffer) {
+ glGenFramebuffers(1, &edpy->framebuffer);
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->framebuffer);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, edpy->texture, 0);
+
+ /* dest framebuffer */
+ if (!edpy->blit_framebuffer) {
+ glGenFramebuffers(1, &edpy->blit_framebuffer);
+ glGenTextures(1, &edpy->blit_texture);
+ edpy->width = 0;
+ edpy->height = 0;
+ }
+ if (edpy->width != backing_width || edpy->height != backing_height) {
+ edpy->width = backing_width;
+ edpy->height = backing_height;
+ glBindTexture(GL_TEXTURE_2D, edpy->blit_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+ edpy->width, edpy->height,
+ 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->blit_framebuffer);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, edpy->blit_texture, 0);
+ }
+}
+
+static void egl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+ GLuint y1, y2;
+
+ if (!edpy->texture || !edpy->ds) {
+ return;
+ }
+ assert(surface_width(edpy->ds) == edpy->width);
+ assert(surface_height(edpy->ds) == edpy->height);
+ assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
+
+ /* blit framebuffer, flip if needed */
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->framebuffer);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, edpy->blit_framebuffer);
+ glViewport(0, 0, edpy->width, edpy->height);
+ y1 = edpy->y_0_top ? edpy->height : 0;
+ y2 = edpy->y_0_top ? 0 : edpy->height;
+ glBlitFramebuffer(0, y1, edpy->width, y2,
+ 0, 0, edpy->width, edpy->height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ /* read pixels to surface */
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->blit_framebuffer);
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ glReadPixels(0, 0, edpy->width, edpy->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, surface_data(edpy->ds));
+
+ /* notify about updates */
+ dpy_gfx_update(edpy->dcl.con, x, y, w, h);
+}
+
+static const DisplayChangeListenerOps egl_ops = {
+ .dpy_name = "egl-headless",
+ .dpy_refresh = egl_refresh,
+ .dpy_gfx_update = egl_gfx_update,
+ .dpy_gfx_switch = egl_gfx_switch,
+
+ .dpy_gl_ctx_create = qemu_egl_create_context,
+ .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
+ .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
+ .dpy_gl_ctx_get_current = qemu_egl_get_current_context,
+
+ .dpy_gl_scanout_disable = egl_scanout_disable,
+ .dpy_gl_scanout_texture = egl_scanout_texture,
+ .dpy_gl_update = egl_scanout_flush,
+};
+
+void egl_headless_init(void)
+{
+ QemuConsole *con;
+ egl_dpy *edpy;
+ int idx;
+
+ if (egl_rendernode_init(NULL) < 0) {
+ error_report("egl: render node init failed");
+ exit(1);
+ }
+
+ for (idx = 0;; idx++) {
+ con = qemu_console_lookup_by_index(idx);
+ if (!con || !qemu_console_is_graphic(con)) {
+ break;
+ }
+
+ edpy = g_new0(egl_dpy, 1);
+ edpy->dcl.con = con;
+ edpy->dcl.ops = &egl_ops;
+ register_displaychangelistener(&edpy->dcl);
+ }
+}
/* ---------------------------------------------------------------------- */
-static bool egl_gles;
-static int egl_debug;
-
-#define egl_dbg(_x ...) \
- do { \
- if (egl_debug) { \
- fprintf(stderr, "egl: " _x); \
- } \
- } while (0);
-
-/* ---------------------------------------------------------------------- */
-
#ifdef CONFIG_OPENGL_DMABUF
int qemu_egl_rn_fd;
int egl_rendernode_init(const char *rendernode)
{
qemu_egl_rn_fd = -1;
+ int rc;
qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode);
if (qemu_egl_rn_fd == -1) {
goto err;
}
- qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false);
+ rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev);
+ if (rc != 0) {
+ /* qemu_egl_init_dpy_mesa reports error */
+ goto err;
+ }
if (!epoxy_has_egl_extension(qemu_egl_display,
"EGL_KHR_surfaceless_context")) {
EGLSurface esurface;
EGLBoolean b;
- egl_dbg("eglCreateWindowSurface (x11 win id 0x%lx) ...\n",
- (unsigned long) win);
esurface = eglCreateWindowSurface(qemu_egl_display,
qemu_egl_config,
(EGLNativeWindowType)win, NULL);
* platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
* like mesa will be able to advertise these (even though it can do EGL 1.5).
*/
-static EGLDisplay qemu_egl_get_display(void *native)
+static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
+ EGLenum platform)
{
EGLDisplay dpy = EGL_NO_DISPLAY;
-#ifdef EGL_MESA_platform_gbm
/* In practise any EGL 1.5 implementation would support the EXT extension */
if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
(void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
- if (getPlatformDisplayEXT) {
- dpy = getPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, native, NULL);
+ if (getPlatformDisplayEXT && platform != 0) {
+ dpy = getPlatformDisplayEXT(platform, native, NULL);
}
}
-#endif
if (dpy == EGL_NO_DISPLAY) {
/* fallback */
return dpy;
}
-int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug)
+static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
+ EGLenum platform)
{
static const EGLint conf_att_gl[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_ALPHA_SIZE, 0,
EGL_NONE,
};
- static const EGLint conf_att_gles[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 5,
- EGL_BLUE_SIZE, 5,
- EGL_ALPHA_SIZE, 0,
- EGL_NONE,
- };
EGLint major, minor;
EGLBoolean b;
EGLint n;
- if (debug) {
- egl_debug = 1;
- setenv("EGL_LOG_LEVEL", "debug", true);
- setenv("LIBGL_DEBUG", "verbose", true);
- }
-
- egl_dbg("qemu_egl_get_display (dpy %p) ...\n", dpy);
- qemu_egl_display = qemu_egl_get_display(dpy);
+ qemu_egl_display = qemu_egl_get_display(dpy, platform);
if (qemu_egl_display == EGL_NO_DISPLAY) {
error_report("egl: eglGetDisplay failed");
return -1;
}
- egl_dbg("eglInitialize ...\n");
b = eglInitialize(qemu_egl_display, &major, &minor);
if (b == EGL_FALSE) {
error_report("egl: eglInitialize failed");
return -1;
}
- egl_dbg("eglBindAPI ...\n");
- b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
+ b = eglBindAPI(EGL_OPENGL_API);
if (b == EGL_FALSE) {
error_report("egl: eglBindAPI failed");
return -1;
}
- egl_dbg("eglChooseConfig ...\n");
- b = eglChooseConfig(qemu_egl_display,
- gles ? conf_att_gles : conf_att_gl,
+ b = eglChooseConfig(qemu_egl_display, conf_att_gl,
&qemu_egl_config, 1, &n);
if (b == EGL_FALSE || n != 1) {
error_report("egl: eglChooseConfig failed");
return -1;
}
-
- egl_gles = gles;
return 0;
}
+int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy)
+{
+#ifdef EGL_KHR_platform_x11
+ return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR);
+#else
+ return qemu_egl_init_dpy(dpy, 0);
+#endif
+}
+
+int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy)
+{
+#ifdef EGL_MESA_platform_gbm
+ return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA);
+#else
+ return qemu_egl_init_dpy(dpy, 0);
+#endif
+}
+
EGLContext qemu_egl_init_ctx(void)
{
static const EGLint ctx_att_gl[] = {
+ EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
EGL_NONE
};
- static const EGLint ctx_att_gles[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
-
EGLContext ectx;
EGLBoolean b;
- egl_dbg("eglCreateContext ...\n");
ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
- egl_gles ? ctx_att_gles : ctx_att_gl);
+ ctx_att_gl);
if (ectx == EGL_NO_CONTEXT) {
error_report("egl: eglCreateContext failed");
return NULL;
GdkDisplay *gdk_display = gdk_display_get_default();
Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display);
- if (qemu_egl_init_dpy(x11_display, false, false) < 0) {
+ if (qemu_egl_init_dpy_x11(x11_display) < 0) {
return;
}
return TRUE;
}
qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
- surface_width(vc->gfx.ds));
+ 0, surface_width(vc->gfx.ds));
qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
- surface_height(vc->gfx.ds));
+ 0, surface_height(vc->gfx.ds));
qemu_input_event_sync();
} else if (s->last_set && s->ptr_owner == vc) {
qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
bool has_abs_x;
int num_keys;
int num_btns;
+ int abs_x_min;
+ int abs_x_max;
+ int abs_y_min;
+ int abs_y_max;
struct input_event event;
int read_offset;
break;
}
break;
+ case EV_ABS:
+ switch (event->code) {
+ case ABS_X:
+ qemu_input_queue_abs(NULL, INPUT_AXIS_X, event->value,
+ il->abs_x_min, il->abs_x_max);
+ break;
+ case ABS_Y:
+ qemu_input_queue_abs(NULL, INPUT_AXIS_Y, event->value,
+ il->abs_y_min, il->abs_y_max);
+ break;
+ }
+ break;
case EV_SYN:
qemu_input_event_sync();
if (il->wheel != 0) {
if (il->num_keys) {
input_linux_handle_keyboard(il, &il->event);
}
- if (il->has_rel_x && il->num_btns) {
+ if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
input_linux_handle_mouse(il, &il->event);
}
}
uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
unsigned int i;
int rc, ver;
+ struct input_absinfo absinfo;
if (!il->evdev) {
error_setg(errp, "no input device specified");
rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
if (absmap & (1 << ABS_X)) {
il->has_abs_x = true;
+ rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
+ il->abs_x_min = absinfo.minimum;
+ il->abs_x_max = absinfo.maximum;
+ rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
+ il->abs_y_min = absinfo.minimum;
+ il->abs_y_max = absinfo.maximum;
}
}
qemu_input_event_sync();
}
+static int qemu_input_transform_invert_abs_value(int value)
+{
+ return (int64_t)INPUT_EVENT_ABS_MAX - value + INPUT_EVENT_ABS_MIN;
+}
+
static void qemu_input_transform_abs_rotate(InputEvent *evt)
{
InputMoveEvent *move = evt->u.abs.data;
move->axis = INPUT_AXIS_Y;
} else if (move->axis == INPUT_AXIS_Y) {
move->axis = INPUT_AXIS_X;
- move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
+ move->value = qemu_input_transform_invert_abs_value(move->value);
}
break;
case 180:
- move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
+ move->value = qemu_input_transform_invert_abs_value(move->value);
break;
case 270:
if (move->axis == INPUT_AXIS_X) {
move->axis = INPUT_AXIS_Y;
- move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
+ move->value = qemu_input_transform_invert_abs_value(move->value);
} else if (move->axis == INPUT_AXIS_Y) {
move->axis = INPUT_AXIS_X;
}
return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
}
-int qemu_input_scale_axis(int value, int size_in, int size_out)
+int qemu_input_scale_axis(int value,
+ int min_in, int max_in,
+ int min_out, int max_out)
{
- if (size_in < 2) {
- return size_out / 2;
+ int64_t range_in = (int64_t)max_in - min_in;
+ int64_t range_out = (int64_t)max_out - min_out;
+
+ if (range_in < 1) {
+ return min_out + range_out / 2;
}
- return (int64_t)value * (size_out - 1) / (size_in - 1);
+ return ((int64_t)value - min_in) * range_out / range_in + min_out;
}
InputEvent *qemu_input_event_new_move(InputEventKind kind,
qapi_free_InputEvent(evt);
}
-void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size)
+void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value,
+ int min_in, int max_in)
{
InputEvent *evt;
- int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE);
+ int scaled = qemu_input_scale_axis(value, min_in, max_in,
+ INPUT_EVENT_ABS_MIN,
+ INPUT_EVENT_ABS_MAX);
evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled);
qemu_input_event_send(src, evt);
qapi_free_InputEvent(evt);
if (qemu_input_is_absolute()) {
qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
- real_screen->w);
+ 0, real_screen->w);
qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
- real_screen->h);
+ 0, real_screen->h);
} else {
if (guest_cursor) {
x -= guest_x;
}
}
}
- qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
- qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
+ qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, 0, max_w);
+ qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, 0, max_h);
} else {
if (guest_cursor) {
x -= guest_x;
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
spice_update_buttons(pointer, 0, buttons_state);
- qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width);
- qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->height);
+ qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, 0, pointer->width);
+ qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, 0, pointer->height);
qemu_input_event_sync();
}
if (packed_bytes < estimated_bytes) {
*use_rle = false;
*use_palette = true;
- estimated_bytes = packed_bytes;
}
}
}
}
if (vs->absolute) {
- qemu_input_queue_abs(con, INPUT_AXIS_X, x, width);
- qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height);
+ qemu_input_queue_abs(con, INPUT_AXIS_X, x, 0, width);
+ qemu_input_queue_abs(con, INPUT_AXIS_Y, y, 0, height);
} else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF);
qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF);
}
vs->client_pf.rmax = red_max ? red_max : 0xFF;
- vs->client_pf.rbits = hweight_long(red_max);
+ vs->client_pf.rbits = ctpopl(red_max);
vs->client_pf.rshift = red_shift;
vs->client_pf.rmask = red_max << red_shift;
vs->client_pf.gmax = green_max ? green_max : 0xFF;
- vs->client_pf.gbits = hweight_long(green_max);
+ vs->client_pf.gbits = ctpopl(green_max);
vs->client_pf.gshift = green_shift;
vs->client_pf.gmask = green_max << green_shift;
vs->client_pf.bmax = blue_max ? blue_max : 0xFF;
- vs->client_pf.bbits = hweight_long(blue_max);
+ vs->client_pf.bbits = ctpopl(blue_max);
vs->client_pf.bshift = blue_shift;
vs->client_pf.bmask = blue_max << blue_shift;
vs->client_pf.bits_per_pixel = bits_per_pixel;
+++ /dev/null
-/*
- * GThread coroutine initialization code
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2011 Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.0 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "qemu/coroutine_int.h"
-
-typedef struct {
- Coroutine base;
- GThread *thread;
- bool runnable;
- bool free_on_thread_exit;
- CoroutineAction action;
-} CoroutineGThread;
-
-static CompatGMutex coroutine_lock;
-static CompatGCond coroutine_cond;
-
-/* GLib 2.31 and beyond deprecated various parts of the thread API,
- * but the new interfaces are not available in older GLib versions
- * so we have to cope with both.
- */
-#if GLIB_CHECK_VERSION(2, 31, 0)
-/* Awkwardly, the GPrivate API doesn't provide a way to update the
- * GDestroyNotify handler for the coroutine key dynamically. So instead
- * we track whether or not the CoroutineGThread should be freed on
- * thread exit / coroutine key update using the free_on_thread_exit
- * field.
- */
-static void coroutine_destroy_notify(gpointer data)
-{
- CoroutineGThread *co = data;
- if (co && co->free_on_thread_exit) {
- g_free(co);
- }
-}
-
-static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify);
-
-static inline CoroutineGThread *get_coroutine_key(void)
-{
- return g_private_get(&coroutine_key);
-}
-
-static inline void set_coroutine_key(CoroutineGThread *co,
- bool free_on_thread_exit)
-{
- /* Unlike g_static_private_set() this does not call the GDestroyNotify
- * if the previous value of the key was NULL. Fortunately we only need
- * the GDestroyNotify in the non-NULL key case.
- */
- co->free_on_thread_exit = free_on_thread_exit;
- g_private_replace(&coroutine_key, co);
-}
-
-static inline GThread *create_thread(GThreadFunc func, gpointer data)
-{
- return g_thread_new("coroutine", func, data);
-}
-
-#else
-
-/* Handle older GLib versions */
-
-static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
-
-static inline CoroutineGThread *get_coroutine_key(void)
-{
- return g_static_private_get(&coroutine_key);
-}
-
-static inline void set_coroutine_key(CoroutineGThread *co,
- bool free_on_thread_exit)
-{
- g_static_private_set(&coroutine_key, co,
- free_on_thread_exit ? (GDestroyNotify)g_free : NULL);
-}
-
-static inline GThread *create_thread(GThreadFunc func, gpointer data)
-{
- return g_thread_create_full(func, data, 0, TRUE, TRUE,
- G_THREAD_PRIORITY_NORMAL, NULL);
-}
-
-#endif
-
-
-static void __attribute__((constructor)) coroutine_init(void)
-{
-#if !GLIB_CHECK_VERSION(2, 31, 0)
- if (!g_thread_supported()) {
- g_thread_init(NULL);
- }
-#endif
-}
-
-static void coroutine_wait_runnable_locked(CoroutineGThread *co)
-{
- while (!co->runnable) {
- g_cond_wait(&coroutine_cond, &coroutine_lock);
- }
-}
-
-static void coroutine_wait_runnable(CoroutineGThread *co)
-{
- g_mutex_lock(&coroutine_lock);
- coroutine_wait_runnable_locked(co);
- g_mutex_unlock(&coroutine_lock);
-}
-
-static gpointer coroutine_thread(gpointer opaque)
-{
- CoroutineGThread *co = opaque;
-
- set_coroutine_key(co, false);
- coroutine_wait_runnable(co);
- co->base.entry(co->base.entry_arg);
- qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE);
- return NULL;
-}
-
-Coroutine *qemu_coroutine_new(void)
-{
- CoroutineGThread *co;
-
- co = g_malloc0(sizeof(*co));
- co->thread = create_thread(coroutine_thread, co);
- if (!co->thread) {
- g_free(co);
- return NULL;
- }
- return &co->base;
-}
-
-void qemu_coroutine_delete(Coroutine *co_)
-{
- CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_);
-
- g_thread_join(co->thread);
- g_free(co);
-}
-
-CoroutineAction qemu_coroutine_switch(Coroutine *from_,
- Coroutine *to_,
- CoroutineAction action)
-{
- CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_);
- CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_);
-
- g_mutex_lock(&coroutine_lock);
- from->runnable = false;
- from->action = action;
- to->runnable = true;
- to->action = action;
- g_cond_broadcast(&coroutine_cond);
-
- if (action != COROUTINE_TERMINATE) {
- coroutine_wait_runnable_locked(from);
- }
- g_mutex_unlock(&coroutine_lock);
- return from->action;
-}
-
-Coroutine *qemu_coroutine_self(void)
-{
- CoroutineGThread *co = get_coroutine_key();
- if (!co) {
- co = g_malloc0(sizeof(*co));
- co->runnable = true;
- set_coroutine_key(co, true);
- }
-
- return &co->base;
-}
-
-bool qemu_in_coroutine(void)
-{
- CoroutineGThread *co = get_coroutine_key();
-
- return co && co->base.caller;
-}
const char *env, int (*)(envlist_t *, const char *));
/*
- * Allocates new envlist and returns pointer to that or
- * NULL in case of error.
+ * Allocates new envlist and returns pointer to it.
*/
envlist_t *
envlist_create(void)
{
envlist_t *envlist;
- if ((envlist = malloc(sizeof (*envlist))) == NULL)
- return (NULL);
+ envlist = g_malloc(sizeof(*envlist));
QLIST_INIT(&envlist->el_entries);
envlist->el_count = 0;
entry = envlist->el_entries.lh_first;
QLIST_REMOVE(entry, ev_link);
- free((char *)entry->ev_var);
- free(entry);
+ g_free((char *)entry->ev_var);
+ g_free(entry);
}
- free(envlist);
+ g_free(envlist);
}
/*
if ((envlist == NULL) || (env == NULL))
return (EINVAL);
- if ((tmpenv = strdup(env)) == NULL)
- return (errno);
+ tmpenv = g_strdup(env);
envsave = tmpenv;
do {
tmpenv = envvar + 1;
} while (envvar != NULL);
- free(envsave);
+ g_free(envsave);
return ret;
}
if (entry != NULL) {
QLIST_REMOVE(entry, ev_link);
- free((char *)entry->ev_var);
- free(entry);
+ g_free((char *)entry->ev_var);
+ g_free(entry);
} else {
envlist->el_count++;
}
- if ((entry = malloc(sizeof (*entry))) == NULL)
- return (errno);
- if ((entry->ev_var = strdup(env)) == NULL) {
- free(entry);
- return (errno);
- }
+ entry = g_malloc(sizeof(*entry));
+ entry->ev_var = g_strdup(env);
QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
return (0);
}
if (entry != NULL) {
QLIST_REMOVE(entry, ev_link);
- free((char *)entry->ev_var);
- free(entry);
+ g_free((char *)entry->ev_var);
+ g_free(entry);
envlist->el_count--;
}
/*
* Returns given envlist as array of strings (in same form that
* global variable environ is). Caller must free returned memory
- * by calling free(3) for each element and for the array. Returned
- * array and given envlist are not related (no common references).
+ * by calling g_free for each element and the array.
+ * Returned array and given envlist are not related (no common
+ * references).
*
* If caller provides count pointer, number of items in array is
- * stored there. In case of error, NULL is returned and no memory
- * is allocated.
+ * stored there.
*/
char **
envlist_to_environ(const envlist_t *envlist, size_t *count)
struct envlist_entry *entry;
char **env, **penv;
- penv = env = malloc((envlist->el_count + 1) * sizeof (char *));
- if (env == NULL)
- return (NULL);
+ penv = env = g_malloc((envlist->el_count + 1) * sizeof(char *));
for (entry = envlist->el_entries.lh_first; entry != NULL;
entry = entry->ev_link.le_next) {
- *(penv++) = strdup(entry->ev_var);
+ *(penv++) = g_strdup(entry->ev_var);
}
*penv = NULL; /* NULL terminate the list */
#include "qemu/error-report.h"
#include "monitor/monitor.h"
+#ifdef F_OFD_SETLK
+#define QEMU_SETLK F_OFD_SETLK
+#define QEMU_GETLK F_OFD_GETLK
+#else
+#define QEMU_SETLK F_SETLK
+#define QEMU_GETLK F_GETLK
+#endif
+
static bool fips_enabled = false;
static const char *hw_version = QEMU_HW_VERSION;
{
return qemu_parse_fd(param);
}
+
+static int qemu_lock_fcntl(int fd, int64_t start, int64_t len, int fl_type)
+{
+ int ret;
+ struct flock fl = {
+ .l_whence = SEEK_SET,
+ .l_start = start,
+ .l_len = len,
+ .l_type = fl_type,
+ };
+ ret = fcntl(fd, QEMU_SETLK, &fl);
+ return ret == -1 ? -errno : 0;
+}
+
+int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive)
+{
+ return qemu_lock_fcntl(fd, start, len, exclusive ? F_WRLCK : F_RDLCK);
+}
+
+int qemu_unlock_fd(int fd, int64_t start, int64_t len)
+{
+ return qemu_lock_fcntl(fd, start, len, F_UNLCK);
+}
+
+int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive)
+{
+ int ret;
+ struct flock fl = {
+ .l_whence = SEEK_SET,
+ .l_start = start,
+ .l_len = len,
+ .l_type = exclusive ? F_WRLCK : F_RDLCK,
+ };
+ ret = fcntl(fd, QEMU_GETLK, &fl);
+ if (ret == -1) {
+ return -errno;
+ } else {
+ return fl.l_type == F_UNLCK ? 0 : -EAGAIN;
+ }
+}
#endif
/*
DT_SDL,
DT_COCOA,
DT_GTK,
+ DT_EGL,
DT_NONE,
} DisplayType;
error_report("VNC requires a display argument vnc=<display>");
exit(1);
}
+ } else if (strstart(p, "egl-headless", &opts)) {
+#ifdef CONFIG_OPENGL
+ request_opengl = 1;
+ display_opengl = 1;
+ display = DT_EGL;
+#else
+ fprintf(stderr, "egl support is disabled\n");
+ exit(1);
+#endif
} else if (strstart(p, "curses", &opts)) {
#ifdef CONFIG_CURSES
display = DT_CURSES;
exit(1);
}
fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
+ qemu_opts_id(opts) ?:
qemu_opt_get(opts, "mount_tag"),
1, NULL);
if (!fsdev) {
- error_report("duplicate fsdev id: %s",
+ error_report("duplicate or invalid fsdev id: %s",
qemu_opt_get(opts, "mount_tag"));
exit(1);
}
&error_abort);
qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort);
qemu_opt_set(device, "fsdev",
- qemu_opt_get(opts, "mount_tag"), &error_abort);
+ qemu_opts_id(fsdev), &error_abort);
qemu_opt_set(device, "mount_tag",
qemu_opt_get(opts, "mount_tag"), &error_abort);
break;
default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);
- parse_numa_opts(machine_class);
+ parse_numa_opts(current_machine);
if (qemu_opts_foreach(qemu_find_opts("mon"),
mon_init_func, NULL, NULL)) {
current_machine->boot_order = boot_order;
current_machine->cpu_model = cpu_model;
- machine_class->init(current_machine);
+ machine_run_board_init(current_machine);
realtime_init();
cpu_synchronize_all_post_init();
- numa_post_machine_init();
-
rom_reset_order_override();
/*
qemu_spice_display_init();
}
+#ifdef CONFIG_OPENGL
+ if (display_type == DT_EGL) {
+ egl_headless_init();
+ }
+#endif
+
if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
exit(1);
}