Merge remote-tracking branch 'remotes/kraxel/tags/pull-usb-20140701-1' into staging
usb bugfixes.
# gpg: Signature made Tue 01 Jul 2014 14:51:19 BST using RSA key ID D3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg: aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
* remotes/kraxel/tags/pull-usb-20140701-1:
ccid-card-emulated: use EventNotifier
usb: initialize libusb_device to avoid crash
usb: Fix usb-bt-dongle initialization.
input: fix jumpy mouse cursor with USB mouse emulation
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/block.c b/block.c
index 6856c18..f80e2b2 100644
--- a/block.c
+++ b/block.c
@@ -2508,32 +2508,23 @@
*
* Returns NULL if bs is not found in active's image chain,
* or if active == bs.
+ *
+ * Returns the bottommost base image if bs == NULL.
*/
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
BlockDriverState *bs)
{
- BlockDriverState *overlay = NULL;
- BlockDriverState *intermediate;
-
- assert(active != NULL);
- assert(bs != NULL);
-
- /* if bs is the same as active, then by definition it has no overlay
- */
- if (active == bs) {
- return NULL;
+ while (active && bs != active->backing_hd) {
+ active = active->backing_hd;
}
- intermediate = active;
- while (intermediate->backing_hd) {
- if (intermediate->backing_hd == bs) {
- overlay = intermediate;
- break;
- }
- intermediate = intermediate->backing_hd;
- }
+ return active;
+}
- return overlay;
+/* Given a BDS, searches for the base layer. */
+BlockDriverState *bdrv_find_base(BlockDriverState *bs)
+{
+ return bdrv_find_overlay(bs, NULL);
}
typedef struct BlkIntermediateStates {
@@ -2564,12 +2555,15 @@
*
* base <- active
*
+ * If backing_file_str is non-NULL, it will be used when modifying top's
+ * overlay image metadata.
+ *
* Error conditions:
* if active == top, that is considered an error
*
*/
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
- BlockDriverState *base)
+ BlockDriverState *base, const char *backing_file_str)
{
BlockDriverState *intermediate;
BlockDriverState *base_bs = NULL;
@@ -2621,7 +2615,8 @@
}
/* success - we can delete the intermediate states, and link top->base */
- ret = bdrv_change_backing_file(new_top_bs, base_bs->filename,
+ backing_file_str = backing_file_str ? backing_file_str : base_bs->filename;
+ ret = bdrv_change_backing_file(new_top_bs, backing_file_str,
base_bs->drv ? base_bs->drv->format_name : "");
if (ret) {
goto exit;
@@ -3783,6 +3778,17 @@
return NULL;
}
+/* If 'base' is in the same chain as 'top', return true. Otherwise,
+ * return false. If either argument is NULL, return false. */
+bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base)
+{
+ while (top && top != base) {
+ top = top->backing_hd;
+ }
+
+ return top != NULL;
+}
+
BlockDriverState *bdrv_next(BlockDriverState *bs)
{
if (!bs) {
@@ -4326,22 +4332,6 @@
return 1 + bdrv_get_backing_file_depth(bs->backing_hd);
}
-BlockDriverState *bdrv_find_base(BlockDriverState *bs)
-{
- BlockDriverState *curr_bs = NULL;
-
- if (!bs) {
- return NULL;
- }
-
- curr_bs = bs;
-
- while (curr_bs->backing_hd) {
- curr_bs = curr_bs->backing_hd;
- }
- return curr_bs;
-}
-
/**************************************************************/
/* async I/Os */
diff --git a/block/commit.c b/block/commit.c
index 5c09f44..91517d3 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -37,6 +37,7 @@
BlockdevOnError on_error;
int base_flags;
int orig_overlay_flags;
+ char *backing_file_str;
} CommitBlockJob;
static int coroutine_fn commit_populate(BlockDriverState *bs,
@@ -141,7 +142,7 @@
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
/* success */
- ret = bdrv_drop_intermediate(active, top, base);
+ ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
}
exit_free_buf:
@@ -158,7 +159,7 @@
if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
}
-
+ g_free(s->backing_file_str);
block_job_completed(&s->common, ret);
}
@@ -182,7 +183,7 @@
void commit_start(BlockDriverState *bs, BlockDriverState *base,
BlockDriverState *top, int64_t speed,
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
- void *opaque, Error **errp)
+ void *opaque, const char *backing_file_str, Error **errp)
{
CommitBlockJob *s;
BlockReopenQueue *reopen_queue = NULL;
@@ -244,6 +245,8 @@
s->base_flags = orig_base_flags;
s->orig_overlay_flags = orig_overlay_flags;
+ s->backing_file_str = g_strdup(backing_file_str);
+
s->on_error = on_error;
s->common.co = qemu_coroutine_create(commit_run);
diff --git a/block/cow.c b/block/cow.c
index 8f81ee6..6ee4833 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -332,7 +332,7 @@
char *image_filename = NULL;
Error *local_err = NULL;
int ret;
- BlockDriverState *cow_bs;
+ BlockDriverState *cow_bs = NULL;
/* Read out options */
image_sectors = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / 512;
@@ -344,7 +344,6 @@
goto exit;
}
- cow_bs = NULL;
ret = bdrv_open(&cow_bs, filename, NULL, NULL,
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
if (ret < 0) {
@@ -383,7 +382,9 @@
exit:
g_free(image_filename);
- bdrv_unref(cow_bs);
+ if (cow_bs) {
+ bdrv_unref(cow_bs);
+ }
return ret;
}
diff --git a/block/qed.c b/block/qed.c
index eddae92..b69374b 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -567,7 +567,7 @@
static int qed_create(const char *filename, uint32_t cluster_size,
uint64_t image_size, uint32_t table_size,
const char *backing_file, const char *backing_fmt,
- Error **errp)
+ QemuOpts *opts, Error **errp)
{
QEDHeader header = {
.magic = QED_MAGIC,
@@ -586,7 +586,7 @@
int ret = 0;
BlockDriverState *bs;
- ret = bdrv_create_file(filename, NULL, &local_err);
+ ret = bdrv_create_file(filename, opts, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return ret;
@@ -682,7 +682,7 @@
}
ret = qed_create(filename, cluster_size, image_size, table_size,
- backing_file, backing_fmt, errp);
+ backing_file, backing_fmt, opts, errp);
finish:
g_free(backing_file);
diff --git a/block/raw-posix.c b/block/raw-posix.c
index dacf4fb..825a0c8 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -55,6 +55,9 @@
#include <linux/cdrom.h>
#include <linux/fd.h>
#include <linux/fs.h>
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
+#endif
#endif
#ifdef CONFIG_FIEMAP
#include <linux/fiemap.h>
@@ -1278,12 +1281,14 @@
int fd;
int result = 0;
int64_t total_size = 0;
+ bool nocow = false;
strstart(filename, "file:", &filename);
/* Read out options */
total_size =
qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0) / BDRV_SECTOR_SIZE;
+ nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
0644);
@@ -1291,6 +1296,21 @@
result = -errno;
error_setg_errno(errp, -result, "Could not create file");
} else {
+ if (nocow) {
+#ifdef __linux__
+ /* Set NOCOW flag to solve performance issue on fs like btrfs.
+ * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value
+ * will be ignored since any failure of this operation should not
+ * block the left work.
+ */
+ int attr;
+ if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
+ attr |= FS_NOCOW_FL;
+ ioctl(fd, FS_IOC_SETFLAGS, &attr);
+ }
+#endif
+ }
+
if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
result = -errno;
error_setg_errno(errp, -result, "Could not resize file");
@@ -1477,6 +1497,11 @@
.type = QEMU_OPT_SIZE,
.help = "Virtual disk size"
},
+ {
+ .name = BLOCK_OPT_NOCOW,
+ .type = QEMU_OPT_BOOL,
+ .help = "Turn off copy-on-write (valid only on btrfs)"
+ },
{ /* end of list */ }
}
};
diff --git a/block/stream.c b/block/stream.c
index 0433409..34de8ba 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -32,7 +32,7 @@
RateLimit limit;
BlockDriverState *base;
BlockdevOnError on_error;
- char backing_file_id[1024];
+ char *backing_file_str;
} StreamBlockJob;
static int coroutine_fn stream_populate(BlockDriverState *bs,
@@ -186,7 +186,7 @@
if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
const char *base_id = NULL, *base_fmt = NULL;
if (base) {
- base_id = s->backing_file_id;
+ base_id = s->backing_file_str;
if (base->drv) {
base_fmt = base->drv->format_name;
}
@@ -196,6 +196,7 @@
}
qemu_vfree(buf);
+ g_free(s->backing_file_str);
block_job_completed(&s->common, ret);
}
@@ -217,7 +218,7 @@
};
void stream_start(BlockDriverState *bs, BlockDriverState *base,
- const char *base_id, int64_t speed,
+ const char *backing_file_str, int64_t speed,
BlockdevOnError on_error,
BlockDriverCompletionFunc *cb,
void *opaque, Error **errp)
@@ -237,9 +238,7 @@
}
s->base = base;
- if (base_id) {
- pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
- }
+ s->backing_file_str = g_strdup(backing_file_str);
s->on_error = on_error;
s->common.co = qemu_coroutine_create(stream_run);
diff --git a/block/vdi.c b/block/vdi.c
index 01fe22e..197bd77 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -53,6 +53,13 @@
#include "block/block_int.h"
#include "qemu/module.h"
#include "migration/migration.h"
+#ifdef __linux__
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
+#endif
+#endif
#if defined(CONFIG_UUID)
#include <uuid/uuid.h>
@@ -683,6 +690,7 @@
VdiHeader header;
size_t i;
size_t bmap_size;
+ bool nocow = false;
logout("\n");
@@ -699,6 +707,7 @@
image_type = VDI_TYPE_STATIC;
}
#endif
+ nocow = qemu_opt_get_bool_del(opts, BLOCK_OPT_NOCOW, false);
if (bytes > VDI_DISK_SIZE_MAX) {
result = -ENOTSUP;
@@ -716,6 +725,21 @@
goto exit;
}
+ if (nocow) {
+#ifdef __linux__
+ /* Set NOCOW flag to solve performance issue on fs like btrfs.
+ * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
+ * be ignored since any failure of this operation should not block the
+ * left work.
+ */
+ int attr;
+ if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
+ attr |= FS_NOCOW_FL;
+ ioctl(fd, FS_IOC_SETFLAGS, &attr);
+ }
+#endif
+ }
+
/* We need enough blocks to store the given disk size,
so always round up. */
blocks = (bytes + block_size - 1) / block_size;
@@ -818,6 +842,11 @@
.def_value_str = "off"
},
#endif
+ {
+ .name = BLOCK_OPT_NOCOW,
+ .type = QEMU_OPT_BOOL,
+ .help = "Turn off copy-on-write (valid only on btrfs)"
+ },
/* TODO: An additional option to set UUID values might be useful. */
{ /* end of list */ }
}
diff --git a/block/vmdk.c b/block/vmdk.c
index d0de019..27a78da 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1529,7 +1529,7 @@
static int vmdk_create_extent(const char *filename, int64_t filesize,
bool flat, bool compress, bool zeroed_grain,
- Error **errp)
+ QemuOpts *opts, Error **errp)
{
int ret, i;
BlockDriverState *bs = NULL;
@@ -1539,7 +1539,7 @@
uint32_t *gd_buf = NULL;
int gd_buf_size;
- ret = bdrv_create_file(filename, NULL, &local_err);
+ ret = bdrv_create_file(filename, opts, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto exit;
@@ -1845,7 +1845,7 @@
path, desc_filename);
if (vmdk_create_extent(ext_filename, size,
- flat, compress, zeroed_grain, errp)) {
+ flat, compress, zeroed_grain, opts, errp)) {
ret = -EINVAL;
goto exit;
}
diff --git a/block/vpc.c b/block/vpc.c
index 798d854..8b376a4 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -29,6 +29,13 @@
#if defined(CONFIG_UUID)
#include <uuid/uuid.h>
#endif
+#ifdef __linux__
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
+#endif
+#endif
/**************************************************************/
@@ -751,6 +758,7 @@
int64_t total_size;
int disk_type;
int ret = -EIO;
+ bool nocow = false;
/* Read out options */
total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
@@ -767,6 +775,7 @@
} else {
disk_type = VHD_DYNAMIC;
}
+ nocow = qemu_opt_get_bool_del(opts, BLOCK_OPT_NOCOW, false);
/* Create the file */
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
@@ -775,6 +784,21 @@
goto out;
}
+ if (nocow) {
+#ifdef __linux__
+ /* Set NOCOW flag to solve performance issue on fs like btrfs.
+ * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value will
+ * be ignored since any failure of this operation should not block the
+ * left work.
+ */
+ int attr;
+ if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) {
+ attr |= FS_NOCOW_FL;
+ ioctl(fd, FS_IOC_SETFLAGS, &attr);
+ }
+#endif
+ }
+
/*
* Calculate matching total_size and geometry. Increase the number of
* sectors requested until we get enough (or fail). This ensures that
@@ -884,6 +908,11 @@
"Type of virtual hard disk format. Supported formats are "
"{dynamic (default) | fixed} "
},
+ {
+ .name = BLOCK_OPT_NOCOW,
+ .type = QEMU_OPT_BOOL,
+ .help = "Turn off copy-on-write (valid only on btrfs)"
+ },
{ /* end of list */ }
}
};
diff --git a/blockdev.c b/blockdev.c
index 69b7c2a..48bd9a3 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1871,14 +1871,17 @@
bdrv_put_ref_bh_schedule(bs);
}
-void qmp_block_stream(const char *device, bool has_base,
- const char *base, bool has_speed, int64_t speed,
+void qmp_block_stream(const char *device,
+ bool has_base, const char *base,
+ bool has_backing_file, const char *backing_file,
+ bool has_speed, int64_t speed,
bool has_on_error, BlockdevOnError on_error,
Error **errp)
{
BlockDriverState *bs;
BlockDriverState *base_bs = NULL;
Error *local_err = NULL;
+ const char *base_name = NULL;
if (!has_on_error) {
on_error = BLOCKDEV_ON_ERROR_REPORT;
@@ -1894,15 +1897,27 @@
return;
}
- if (base) {
+ if (has_base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
error_set(errp, QERR_BASE_NOT_FOUND, base);
return;
}
+ base_name = base;
}
- stream_start(bs, base_bs, base, has_speed ? speed : 0,
+ /* if we are streaming the entire chain, the result will have no backing
+ * file, and specifying one is therefore an error */
+ if (base_bs == NULL && has_backing_file) {
+ error_setg(errp, "backing file specified, but streaming the "
+ "entire chain");
+ return;
+ }
+
+ /* backing_file string overrides base bs filename */
+ base_name = has_backing_file ? backing_file : base_name;
+
+ stream_start(bs, base_bs, base_name, has_speed ? speed : 0,
on_error, block_job_cb, bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -1913,7 +1928,9 @@
}
void qmp_block_commit(const char *device,
- bool has_base, const char *base, const char *top,
+ bool has_base, const char *base,
+ bool has_top, const char *top,
+ bool has_backing_file, const char *backing_file,
bool has_speed, int64_t speed,
Error **errp)
{
@@ -1932,6 +1949,11 @@
/* drain all i/o before commits */
bdrv_drain_all();
+ /* Important Note:
+ * libvirt relies on the DeviceNotFound error class in order to probe for
+ * live commit feature versions; for this to work, we must make sure to
+ * perform the device lookup before any generic errors that may occur in a
+ * scenario in which all optional arguments are omitted. */
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
@@ -1945,7 +1967,7 @@
/* default top_bs is the active layer */
top_bs = bs;
- if (top) {
+ if (has_top && top) {
if (strcmp(bs->filename, top) != 0) {
top_bs = bdrv_find_backing_image(bs, top);
}
@@ -1967,12 +1989,23 @@
return;
}
+ /* Do not allow attempts to commit an image into itself */
+ if (top_bs == base_bs) {
+ error_setg(errp, "cannot commit an image into itself");
+ return;
+ }
+
if (top_bs == bs) {
+ if (has_backing_file) {
+ error_setg(errp, "'backing-file' specified,"
+ " but 'top' is the active layer");
+ return;
+ }
commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
bs, &local_err);
} else {
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
- &local_err);
+ has_backing_file ? backing_file : NULL, &local_err);
}
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -2355,6 +2388,85 @@
block_job_complete(job, errp);
}
+void qmp_change_backing_file(const char *device,
+ const char *image_node_name,
+ const char *backing_file,
+ Error **errp)
+{
+ BlockDriverState *bs = NULL;
+ BlockDriverState *image_bs = NULL;
+ Error *local_err = NULL;
+ bool ro;
+ int open_flags;
+ int ret;
+
+ /* find the top layer BDS of the chain */
+ bs = bdrv_find(device);
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
+ }
+
+ image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (!image_bs) {
+ error_setg(errp, "image file not found");
+ return;
+ }
+
+ if (bdrv_find_base(image_bs) == image_bs) {
+ error_setg(errp, "not allowing backing file change on an image "
+ "without a backing file");
+ return;
+ }
+
+ /* even though we are not necessarily operating on bs, we need it to
+ * determine if block ops are currently prohibited on the chain */
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) {
+ return;
+ }
+
+ /* final sanity check */
+ if (!bdrv_chain_contains(bs, image_bs)) {
+ error_setg(errp, "'%s' and image file are not in the same chain",
+ device);
+ return;
+ }
+
+ /* if not r/w, reopen to make r/w */
+ open_flags = image_bs->open_flags;
+ ro = bdrv_is_read_only(image_bs);
+
+ if (ro) {
+ bdrv_reopen(image_bs, open_flags | BDRV_O_RDWR, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ ret = bdrv_change_backing_file(image_bs, backing_file,
+ image_bs->drv ? image_bs->drv->format_name : "");
+
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not change backing file to '%s'",
+ backing_file);
+ /* don't exit here, so we can try to restore open flags if
+ * appropriate */
+ }
+
+ if (ro) {
+ bdrv_reopen(image_bs, open_flags, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err); /* will preserve prior errp */
+ }
+ }
+}
+
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
{
QmpOutputVisitor *ov = qmp_output_visitor_new();
diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt
index 44be891..4a6c2a2 100644
--- a/docs/qmp/qmp-events.txt
+++ b/docs/qmp/qmp-events.txt
@@ -250,14 +250,14 @@
Data:
-- "reference": device name if defined else node name.
-- "sector-num": Number of the first sector of the failed read operation.
-- "sector-count": Failed read operation sector count.
+- "reference": device name if defined else node name.
+- "sector-num": Number of the first sector of the failed read operation.
+- "sectors-count": Failed read operation sector count.
Example:
{ "event": "QUORUM_FAILURE",
- "data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 },
+ "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
QUORUM_REPORT_BAD
@@ -267,19 +267,19 @@
Data:
-- "error": Error message (json-string, optional)
- Only present on failure. This field contains a human-readable
- error message. There are no semantics other than that the
- block layer reported an error and clients should not try to
- interpret the error string.
-- "node-name": The graph node name of the block driver state.
-- "sector-num": Number of the first sector of the failed read operation.
-- "sector-count": Failed read operation sector count.
+- "error": Error message (json-string, optional)
+ Only present on failure. This field contains a human-readable
+ error message. There are no semantics other than that the
+ block layer reported an error and clients should not try to
+ interpret the error string.
+- "node-name": The graph node name of the block driver state.
+- "sector-num": Number of the first sector of the failed read operation.
+- "sectors-count": Failed read operation sector count.
Example:
{ "event": "QUORUM_REPORT_BAD",
- "data": { "node-name": "1.raw", "sector-num": 345435, "sector-count": 5 },
+ "data": { "node-name": "1.raw", "sector-num": 345435, "sectors-count": 5 },
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
RESET
diff --git a/exec.c b/exec.c
index 18d6c35..5a2a25e 100644
--- a/exec.c
+++ b/exec.c
@@ -883,7 +883,7 @@
if (mr->subpage) {
subpage_t *subpage = container_of(mr, subpage_t, iomem);
- memory_region_destroy(&subpage->iomem);
+ object_unref(OBJECT(&subpage->iomem));
g_free(subpage);
}
}
@@ -1768,7 +1768,7 @@
mmio->as = as;
mmio->base = base;
memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio,
- "subpage", TARGET_PAGE_SIZE);
+ NULL, TARGET_PAGE_SIZE);
mmio->iomem.subpage = true;
#if defined(DEBUG_SUBPAGE)
printf("%s: %p base " TARGET_FMT_plx " len %08x\n", __func__,
@@ -1801,13 +1801,13 @@
static void io_mem_init(void)
{
- memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, "rom", UINT64_MAX);
+ memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
- "unassigned", UINT64_MAX);
+ NULL, UINT64_MAX);
memory_region_init_io(&io_mem_notdirty, NULL, ¬dirty_mem_ops, NULL,
- "notdirty", UINT64_MAX);
+ NULL, UINT64_MAX);
memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
- "watch", UINT64_MAX);
+ NULL, UINT64_MAX);
}
static void mem_begin(MemoryListener *listener)
diff --git a/hmp.c b/hmp.c
index 6429e6b..4d1838e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1176,7 +1176,7 @@
const char *base = qdict_get_try_str(qdict, "base");
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
- qmp_block_stream(device, base != NULL, base,
+ qmp_block_stream(device, base != NULL, base, false, NULL,
qdict_haskey(qdict, "speed"), speed,
true, BLOCKDEV_ON_ERROR_REPORT, &error);
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 09bd2c7..4c5ba18 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -127,10 +127,20 @@
VirtIOBlockDataPlane *s;
VirtIOBlock *vblk = VIRTIO_BLK(vdev);
Error *local_err = NULL;
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
*dataplane = NULL;
- if (!blk->data_plane) {
+ if (!blk->data_plane && !blk->iothread) {
+ return;
+ }
+
+ /* Don't try if transport does not support notifiers. */
+ if (!k->set_guest_notifiers || !k->set_host_notifier) {
+ error_setg(errp,
+ "device is incompatible with x-data-plane "
+ "(transport does not support notifiers)");
return;
}
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index e59ebc9..aec3146 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -677,12 +677,6 @@
.resize_cb = virtio_blk_resize,
};
-void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk)
-{
- VirtIOBlock *s = VIRTIO_BLK(dev);
- memcpy(&(s->blk), blk, sizeof(struct VirtIOBlkConf));
-}
-
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
/* Disable dataplane thread during live migration since it does not
* update the dirty memory bitmap yet.
@@ -790,8 +784,27 @@
virtio_cleanup(vdev);
}
+static void virtio_blk_instance_init(Object *obj)
+{
+ VirtIOBlock *s = VIRTIO_BLK(obj);
+
+ object_property_add_link(obj, "iothread", TYPE_IOTHREAD,
+ (Object **)&s->blk.iothread,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
+}
+
static Property virtio_blk_properties[] = {
- DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlock, blk),
+ DEFINE_BLOCK_PROPERTIES(VirtIOBlock, blk.conf),
+ DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, blk.conf),
+ DEFINE_PROP_STRING("serial", VirtIOBlock, blk.serial),
+ DEFINE_PROP_BIT("config-wce", VirtIOBlock, blk.config_wce, 0, true),
+#ifdef __linux__
+ DEFINE_PROP_BIT("scsi", VirtIOBlock, blk.scsi, 0, true),
+#endif
+#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
+ DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, blk.data_plane, 0, false),
+#endif
DEFINE_PROP_END_OF_LIST(),
};
@@ -817,6 +830,7 @@
.name = TYPE_VIRTIO_BLK,
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VirtIOBlock),
+ .instance_init = virtio_blk_instance_init,
.class_init = virtio_blk_class_init,
};
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 52c2f8a..8e140af 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -385,56 +385,6 @@
nd->instantiated = 1;
}
-/* --- iothread --- */
-
-static char *print_iothread(void *ptr)
-{
- return iothread_get_id(ptr);
-}
-
-static int parse_iothread(DeviceState *dev, const char *str, void **ptr)
-{
- IOThread *iothread;
-
- iothread = iothread_find(str);
- if (!iothread) {
- return -ENOENT;
- }
- object_ref(OBJECT(iothread));
- *ptr = iothread;
- return 0;
-}
-
-static void get_iothread(Object *obj, struct Visitor *v, void *opaque,
- const char *name, Error **errp)
-{
- get_pointer(obj, v, opaque, print_iothread, name, errp);
-}
-
-static void set_iothread(Object *obj, struct Visitor *v, void *opaque,
- const char *name, Error **errp)
-{
- set_pointer(obj, v, opaque, parse_iothread, name, errp);
-}
-
-static void release_iothread(Object *obj, const char *name, void *opaque)
-{
- DeviceState *dev = DEVICE(obj);
- Property *prop = opaque;
- IOThread **ptr = qdev_get_prop_ptr(dev, prop);
-
- if (*ptr) {
- object_unref(OBJECT(*ptr));
- }
-}
-
-PropertyInfo qdev_prop_iothread = {
- .name = "iothread",
- .get = get_iothread,
- .set = set_iothread,
- .release = release_iothread,
-};
-
static int qdev_add_one_global(QemuOpts *opts, void *opaque)
{
GlobalProperty *g;
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 371b427..3bdda8e 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -780,6 +780,27 @@
}
}
+/* @qdev_alias_all_properties - Add alias properties to the source object for
+ * all qdev properties on the target DeviceState.
+ */
+void qdev_alias_all_properties(DeviceState *target, Object *source)
+{
+ ObjectClass *class;
+ Property *prop;
+
+ class = object_get_class(OBJECT(target));
+ do {
+ DeviceClass *dc = DEVICE_CLASS(class);
+
+ for (prop = dc->props; prop && prop->name; prop++) {
+ object_property_add_alias(source, prop->name,
+ OBJECT(target), prop->name,
+ &error_abort);
+ }
+ class = object_class_get_parent(class);
+ } while (class != object_class_by_name(TYPE_DEVICE));
+}
+
static bool device_get_realized(Object *obj, Error **errp)
{
DeviceState *dev = DEVICE(obj);
@@ -848,6 +869,7 @@
if (dev->hotplugged && local_err == NULL) {
device_reset(dev);
}
+ dev->pending_deleted_event = false;
} else if (!value && dev->realized) {
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
object_property_set_bool(OBJECT(bus), false, "realized",
@@ -862,6 +884,7 @@
if (dc->unrealize && local_err == NULL) {
dc->unrealize(dev, &local_err);
}
+ dev->pending_deleted_event = true;
}
if (local_err != NULL) {
@@ -972,7 +995,6 @@
{
DeviceState *dev = DEVICE(obj);
BusState *bus;
- bool have_realized = dev->realized;
if (dev->realized) {
object_property_set_bool(obj, false, "realized", NULL);
@@ -988,7 +1010,7 @@
}
/* Only send event if the device had been completely realized */
- if (have_realized) {
+ if (dev->pending_deleted_event) {
gchar *path = object_get_canonical_path(OBJECT(dev));
qapi_event_send_device_deleted(!!dev->id, dev->id, path, &error_abort);
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 7c8c81b..c0dc365 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -167,7 +167,6 @@
{
VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- virtio_blk_set_conf(vdev, &(dev->blk));
qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
if (qdev_init(vdev) < 0) {
return -1;
@@ -180,6 +179,10 @@
VirtIOBlkS390 *dev = VIRTIO_BLK_S390(obj);
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK);
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+ object_unref(OBJECT(&dev->vdev));
+ qdev_alias_all_properties(DEVICE(&dev->vdev), obj);
+ object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
+ &error_abort);
}
static int s390_virtio_serial_init(VirtIOS390Device *s390_dev)
@@ -513,18 +516,11 @@
.class_init = s390_virtio_net_class_init,
};
-static Property s390_virtio_blk_properties[] = {
- DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkS390, blk),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void s390_virtio_blk_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->init = s390_virtio_blk_init;
- dc->props = s390_virtio_blk_properties;
}
static const TypeInfo s390_virtio_blk = {
diff --git a/hw/s390x/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h
index ac81bd8..ffd0df7 100644
--- a/hw/s390x/s390-virtio-bus.h
+++ b/hw/s390x/s390-virtio-bus.h
@@ -124,7 +124,6 @@
typedef struct VirtIOBlkS390 {
VirtIOS390Device parent_obj;
VirtIOBlock vdev;
- VirtIOBlkConf blk;
} VirtIOBlkS390;
/* virtio-scsi-s390 */
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 05656a2..c279968 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -800,7 +800,6 @@
{
VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- virtio_blk_set_conf(vdev, &(dev->blk));
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
if (qdev_init(vdev) < 0) {
return -1;
@@ -814,6 +813,10 @@
VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(obj);
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK);
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+ object_unref(OBJECT(&dev->vdev));
+ qdev_alias_all_properties(DEVICE(&dev->vdev), obj);
+ object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
+ &error_abort);
}
static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev)
@@ -1400,12 +1403,8 @@
static Property virtio_ccw_blk_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_VIRTIO_BLK_FEATURES(VirtioCcwDevice, host_features[0]),
- DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkCcw, blk),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
-#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
- DEFINE_PROP_BIT("x-data-plane", VirtIOBlkCcw, blk.data_plane, 0, false),
-#endif
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index b8b8a8a..5a1f16e 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -144,7 +144,6 @@
typedef struct VirtIOBlkCcw {
VirtioCcwDevice parent_obj;
VirtIOBlock vdev;
- VirtIOBlkConf blk;
} VirtIOBlkCcw;
/* virtio-balloon-ccw */
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 307732c..9d817ca 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -909,6 +909,9 @@
object_property_add(OBJECT(s), "date", "struct tm",
rtc_get_date, NULL, NULL, s, NULL);
+
+ object_property_add_alias(qdev_get_machine(), "rtc-time",
+ OBJECT(s), "date", NULL);
}
ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
@@ -950,11 +953,17 @@
dc->cannot_instantiate_with_device_add_yet = true;
}
+static void rtc_finalize(Object *obj)
+{
+ object_property_del(qdev_get_machine(), "rtc", NULL);
+}
+
static const TypeInfo mc146818rtc_info = {
.name = TYPE_MC146818_RTC,
.parent = TYPE_ISA_DEVICE,
.instance_size = sizeof(RTCState),
.class_init = rtc_class_initfn,
+ .instance_finalize = rtc_finalize,
};
static void mc146818rtc_register_types(void)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 317324f..3c42cda 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1066,11 +1066,7 @@
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
-#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
- DEFINE_PROP_BIT("x-data-plane", VirtIOBlkPCI, blk.data_plane, 0, false),
-#endif
DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),
- DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkPCI, blk),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1078,7 +1074,6 @@
{
VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- virtio_blk_set_conf(vdev, &(dev->blk));
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
if (qdev_init(vdev) < 0) {
return -1;
@@ -1106,6 +1101,10 @@
VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj);
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK);
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+ object_unref(OBJECT(&dev->vdev));
+ qdev_alias_all_properties(DEVICE(&dev->vdev), obj);
+ object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
+ &error_abort);
}
static const TypeInfo virtio_blk_pci_info = {
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index dc332ae..1cea157 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -131,7 +131,6 @@
struct VirtIOBlkPCI {
VirtIOPCIProxy parent_obj;
VirtIOBlock vdev;
- VirtIOBlkConf blk;
};
/*
diff --git a/include/block/block.h b/include/block/block.h
index 7e92f54..baecc26 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -285,7 +285,8 @@
const char *backing_file, const char *backing_fmt);
void bdrv_register(BlockDriver *bdrv);
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
- BlockDriverState *base);
+ BlockDriverState *base,
+ const char *backing_file_str);
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
BlockDriverState *bs);
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
@@ -403,6 +404,7 @@
BlockDriverState *bdrv_lookup_bs(const char *device,
const char *node_name,
Error **errp);
+bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base);
BlockDriverState *bdrv_next(BlockDriverState *bs);
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
void *opaque);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 53e77cf..8f8e65e 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -54,6 +54,7 @@
#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts"
#define BLOCK_OPT_ADAPTER_TYPE "adapter_type"
#define BLOCK_OPT_REDUNDANCY "redundancy"
+#define BLOCK_OPT_NOCOW "nocow"
typedef struct BdrvTrackedRequest {
BlockDriverState *bs;
@@ -462,13 +463,14 @@
* @on_error: The action to take upon error.
* @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb.
+ * @backing_file_str: String to use as the backing file in @top's overlay
* @errp: Error object.
*
*/
void commit_start(BlockDriverState *bs, BlockDriverState *base,
BlockDriverState *top, int64_t speed,
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
- void *opaque, Error **errp);
+ void *opaque, const char *backing_file_str, Error **errp);
/**
* commit_active_start:
* @bs: Active block device to be committed.
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 3d778d7..e2c8e3e 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -32,10 +32,15 @@
#include "qemu/int128.h"
#include "qemu/notify.h"
#include "qapi/error.h"
+#include "qom/object.h"
#define MAX_PHYS_ADDR_SPACE_BITS 62
#define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
+#define TYPE_MEMORY_REGION "qemu:memory-region"
+#define MEMORY_REGION(obj) \
+ OBJECT_CHECK(MemoryRegion, (obj), TYPE_MEMORY_REGION)
+
typedef struct MemoryRegionOps MemoryRegionOps;
typedef struct MemoryRegionMmio MemoryRegionMmio;
@@ -131,11 +136,11 @@
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
struct MemoryRegion {
+ Object parent_obj;
/* All fields are private - violators will be prosecuted */
const MemoryRegionOps *ops;
const MemoryRegionIOMMUOps *iommu_ops;
void *opaque;
- struct Object *owner;
MemoryRegion *container;
Int128 size;
hwaddr addr;
@@ -152,7 +157,7 @@
bool flush_coalesced_mmio;
MemoryRegion *alias;
hwaddr alias_offset;
- int priority;
+ int32_t priority;
bool may_overlap;
QTAILQ_HEAD(subregions, MemoryRegion) subregions;
QTAILQ_ENTRY(MemoryRegion) subregions_link;
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 9221cfc..0799ff2 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -156,6 +156,7 @@
const char *id;
bool realized;
+ bool pending_deleted_event;
QemuOpts *opts;
int hotplugged;
BusState *parent_bus;
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index c962b6b..77fe3a1 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -22,7 +22,6 @@
extern PropertyInfo qdev_prop_drive;
extern PropertyInfo qdev_prop_netdev;
extern PropertyInfo qdev_prop_vlan;
-extern PropertyInfo qdev_prop_iothread;
extern PropertyInfo qdev_prop_pci_devfn;
extern PropertyInfo qdev_prop_blocksize;
extern PropertyInfo qdev_prop_pci_host_devaddr;
@@ -143,8 +142,6 @@
DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers)
#define DEFINE_PROP_DRIVE(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *)
-#define DEFINE_PROP_IOTHREAD(_n, _s, _f) \
- DEFINE_PROP(_n, _s, _f, qdev_prop_iothread, IOThread *)
#define DEFINE_PROP_MACADDR(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr)
#define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
@@ -193,6 +190,8 @@
*/
void qdev_property_add_static(DeviceState *dev, Property *prop, Error **errp);
+void qdev_alias_all_properties(DeviceState *target, Object *source);
+
/**
* @qdev_prop_set_after_realize:
* @dev: device
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
index d0fb26f..223530e 100644
--- a/include/hw/virtio/virtio-blk.h
+++ b/include/hw/virtio/virtio-blk.h
@@ -155,25 +155,6 @@
#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
DEFINE_VIRTIO_COMMON_FEATURES(_state, _field)
-#ifdef __linux__
-#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \
- DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \
- DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \
- DEFINE_PROP_STRING("serial", _state, _field.serial), \
- DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \
- DEFINE_PROP_BIT("scsi", _state, _field.scsi, 0, true), \
- DEFINE_PROP_IOTHREAD("x-iothread", _state, _field.iothread)
-#else
-#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \
- DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \
- DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \
- DEFINE_PROP_STRING("serial", _state, _field.serial), \
- DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \
- DEFINE_PROP_IOTHREAD("x-iothread", _state, _field.iothread)
-#endif /* __linux__ */
-
-void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk);
-
int virtio_blk_handle_scsi_req(VirtIOBlock *blk,
VirtQueueElement *elem);
diff --git a/include/qom/object.h b/include/qom/object.h
index b882ccc..8a05a81 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -304,6 +304,25 @@
Error **errp);
/**
+ * ObjectPropertyResolve:
+ * @obj: the object that owns the property
+ * @opaque: the opaque registered with the property
+ * @part: the name of the property
+ *
+ * Resolves the #Object corresponding to property @part.
+ *
+ * The returned object can also be used as a starting point
+ * to resolve a relative path starting with "@part".
+ *
+ * Returns: If @path is the path that led to @obj, the function
+ * returns the #Object corresponding to "@path/@part".
+ * If "@path/@part" is not a valid object path, it returns #NULL.
+ */
+typedef Object *(ObjectPropertyResolve)(Object *obj,
+ void *opaque,
+ const char *part);
+
+/**
* ObjectPropertyRelease:
* @obj: the object that owns the property
* @name: the name of the property
@@ -321,6 +340,7 @@
gchar *type;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
+ ObjectPropertyResolve *resolve;
ObjectPropertyRelease *release;
void *opaque;
@@ -787,12 +807,16 @@
* destruction. This may be NULL.
* @opaque: an opaque pointer to pass to the callbacks for the property
* @errp: returns an error if this function fails
+ *
+ * Returns: The #ObjectProperty; this can be used to set the @resolve
+ * callback for child and link properties.
*/
-void object_property_add(Object *obj, const char *name, const char *type,
- ObjectPropertyAccessor *get,
- ObjectPropertyAccessor *set,
- ObjectPropertyRelease *release,
- void *opaque, Error **errp);
+ObjectProperty *object_property_add(Object *obj, const char *name,
+ const char *type,
+ ObjectPropertyAccessor *get,
+ ObjectPropertyAccessor *set,
+ ObjectPropertyRelease *release,
+ void *opaque, Error **errp);
void object_property_del(Object *obj, const char *name, Error **errp);
@@ -1231,6 +1255,26 @@
const uint64_t *v, Error **Errp);
/**
+ * object_property_add_alias:
+ * @obj: the object to add a property to
+ * @name: the name of the property
+ * @target_obj: the object to forward property access to
+ * @target_name: the name of the property on the forwarded object
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Add an alias for a property on an object. This function will add a property
+ * of the same type as the forwarded property.
+ *
+ * The caller must ensure that <code>@target_obj</code> stays alive as long as
+ * this property exists. In the case of a child object or an alias on the same
+ * object this will be the case. For aliases to other objects the caller is
+ * responsible for taking a reference.
+ */
+void object_property_add_alias(Object *obj, const char *name,
+ Object *target_obj, const char *target_name,
+ Error **errp);
+
+/**
* object_child_foreach:
* @obj: the object whose children will be navigated
* @fn: the iterator function to be called
diff --git a/memory.c b/memory.c
index b91a60a..64d7176 100644
--- a/memory.c
+++ b/memory.c
@@ -16,6 +16,7 @@
#include "exec/memory.h"
#include "exec/address-spaces.h"
#include "exec/ioport.h"
+#include "qapi/visitor.h"
#include "qemu/bitops.h"
#include "qom/object.h"
#include "trace.h"
@@ -842,40 +843,178 @@
qemu_ram_free(mr->ram_addr & TARGET_PAGE_MASK);
}
+static bool memory_region_need_escape(char c)
+{
+ return c == '/' || c == '[' || c == '\\' || c == ']';
+}
+
+static char *memory_region_escape_name(const char *name)
+{
+ const char *p;
+ char *escaped, *q;
+ uint8_t c;
+ size_t bytes = 0;
+
+ for (p = name; *p; p++) {
+ bytes += memory_region_need_escape(*p) ? 4 : 1;
+ }
+ if (bytes == p - name) {
+ return g_memdup(name, bytes + 1);
+ }
+
+ escaped = g_malloc(bytes + 1);
+ for (p = name, q = escaped; *p; p++) {
+ c = *p;
+ if (unlikely(memory_region_need_escape(c))) {
+ *q++ = '\\';
+ *q++ = 'x';
+ *q++ = "0123456789abcdef"[c >> 4];
+ c = "0123456789abcdef"[c & 15];
+ }
+ *q++ = c;
+ }
+ *q = 0;
+ return escaped;
+}
+
+static void object_property_add_child_array(Object *owner,
+ const char *name,
+ Object *child)
+{
+ int i;
+ char *base_name = memory_region_escape_name(name);
+
+ for (i = 0; ; i++) {
+ char *full_name = g_strdup_printf("%s[%d]", base_name, i);
+ Error *local_err = NULL;
+
+ object_property_add_child(owner, full_name, child, &local_err);
+ g_free(full_name);
+ if (!local_err) {
+ break;
+ }
+
+ error_free(local_err);
+ }
+
+ g_free(base_name);
+}
+
+
void memory_region_init(MemoryRegion *mr,
Object *owner,
const char *name,
uint64_t size)
{
- mr->ops = &unassigned_mem_ops;
- mr->opaque = NULL;
- mr->owner = owner;
- mr->iommu_ops = NULL;
- mr->container = NULL;
+ if (!owner) {
+ owner = qdev_get_machine();
+ }
+
+ object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);
mr->size = int128_make64(size);
if (size == UINT64_MAX) {
mr->size = int128_2_64();
}
- mr->addr = 0;
- mr->subpage = false;
- mr->enabled = true;
- mr->terminates = false;
- mr->ram = false;
- mr->romd_mode = true;
- mr->readonly = false;
- mr->rom_device = false;
- mr->destructor = memory_region_destructor_none;
- mr->priority = 0;
- mr->may_overlap = false;
- mr->alias = NULL;
- QTAILQ_INIT(&mr->subregions);
- memset(&mr->subregions_link, 0, sizeof mr->subregions_link);
- QTAILQ_INIT(&mr->coalesced);
mr->name = g_strdup(name);
- mr->dirty_log_mask = 0;
- mr->ioeventfd_nb = 0;
- mr->ioeventfds = NULL;
- mr->flush_coalesced_mmio = false;
+
+ if (name) {
+ object_property_add_child_array(owner, name, OBJECT(mr));
+ object_unref(OBJECT(mr));
+ }
+}
+
+static void memory_region_get_addr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ uint64_t value = mr->addr;
+
+ visit_type_uint64(v, &value, name, errp);
+}
+
+static void memory_region_get_container(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ gchar *path = (gchar *)"";
+
+ if (mr->container) {
+ path = object_get_canonical_path(OBJECT(mr->container));
+ }
+ visit_type_str(v, &path, name, errp);
+ if (mr->container) {
+ g_free(path);
+ }
+}
+
+static Object *memory_region_resolve_container(Object *obj, void *opaque,
+ const char *part)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+
+ return OBJECT(mr->container);
+}
+
+static void memory_region_get_priority(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ int32_t value = mr->priority;
+
+ visit_type_int32(v, &value, name, errp);
+}
+
+static bool memory_region_get_may_overlap(Object *obj, Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+
+ return mr->may_overlap;
+}
+
+static void memory_region_get_size(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ uint64_t value = memory_region_size(mr);
+
+ visit_type_uint64(v, &value, name, errp);
+}
+
+static void memory_region_initfn(Object *obj)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ ObjectProperty *op;
+
+ mr->ops = &unassigned_mem_ops;
+ mr->enabled = true;
+ mr->romd_mode = true;
+ mr->destructor = memory_region_destructor_none;
+ QTAILQ_INIT(&mr->subregions);
+ QTAILQ_INIT(&mr->coalesced);
+
+ op = object_property_add(OBJECT(mr), "container",
+ "link<" TYPE_MEMORY_REGION ">",
+ memory_region_get_container,
+ NULL, /* memory_region_set_container */
+ NULL, NULL, &error_abort);
+ op->resolve = memory_region_resolve_container;
+
+ object_property_add(OBJECT(mr), "addr", "uint64",
+ memory_region_get_addr,
+ NULL, /* memory_region_set_addr */
+ NULL, NULL, &error_abort);
+ object_property_add(OBJECT(mr), "priority", "uint32",
+ memory_region_get_priority,
+ NULL, /* memory_region_set_priority */
+ NULL, NULL, &error_abort);
+ object_property_add_bool(OBJECT(mr), "may-overlap",
+ memory_region_get_may_overlap,
+ NULL, /* memory_region_set_may_overlap */
+ &error_abort);
+ object_property_add(OBJECT(mr), "size", "uint64",
+ memory_region_get_size,
+ NULL, /* memory_region_set_size, */
+ NULL, NULL, &error_abort);
}
static uint64_t unassigned_mem_read(void *opaque, hwaddr addr,
@@ -1113,8 +1252,10 @@
memory_region_init_io(mr, owner, &unassigned_mem_ops, mr, name, size);
}
-void memory_region_destroy(MemoryRegion *mr)
+static void memory_region_finalize(Object *obj)
{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+
assert(QTAILQ_EMPTY(&mr->subregions));
assert(memory_region_transaction_depth == 0);
mr->destructor(mr);
@@ -1123,22 +1264,45 @@
g_free(mr->ioeventfds);
}
+void memory_region_destroy(MemoryRegion *mr)
+{
+ object_unparent(OBJECT(mr));
+}
+
+
Object *memory_region_owner(MemoryRegion *mr)
{
- return mr->owner;
+ Object *obj = OBJECT(mr);
+ return obj->parent;
}
void memory_region_ref(MemoryRegion *mr)
{
- if (mr && mr->owner) {
- object_ref(mr->owner);
+ /* MMIO callbacks most likely will access data that belongs
+ * to the owner, hence the need to ref/unref the owner whenever
+ * the memory region is in use.
+ *
+ * The memory region is a child of its owner. As long as the
+ * owner doesn't call unparent itself on the memory region,
+ * ref-ing the owner will also keep the memory region alive.
+ * Memory regions without an owner are supposed to never go away,
+ * but we still ref/unref them for debugging purposes.
+ */
+ Object *obj = OBJECT(mr);
+ if (obj && obj->parent) {
+ object_ref(obj->parent);
+ } else {
+ object_ref(obj);
}
}
void memory_region_unref(MemoryRegion *mr)
{
- if (mr && mr->owner) {
- object_unref(mr->owner);
+ Object *obj = OBJECT(mr);
+ if (obj && obj->parent) {
+ object_unref(obj->parent);
+ } else {
+ object_unref(obj);
}
}
@@ -1946,3 +2110,18 @@
g_free(ml);
}
}
+
+static const TypeInfo memory_region_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_MEMORY_REGION,
+ .instance_size = sizeof(MemoryRegion),
+ .instance_init = memory_region_initfn,
+ .instance_finalize = memory_region_finalize,
+};
+
+static void memory_register_types(void)
+{
+ type_register_static(&memory_region_info);
+}
+
+type_init(memory_register_types)
diff --git a/qapi/block-core.json b/qapi/block-core.json
index faf394c..e378653 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -680,6 +680,32 @@
'data': 'BlockdevSnapshot' }
##
+# @change-backing-file
+#
+# Change the backing file in the image file metadata. This does not
+# cause QEMU to reopen the image file to reparse the backing filename
+# (it may, however, perform a reopen to change permissions from
+# r/o -> r/w -> r/o, if needed). The new backing file string is written
+# into the image file metadata, and the QEMU internal strings are
+# updated.
+#
+# @image-node-name: The name of the block driver state node of the
+# image to modify.
+#
+# @device: The name of the device that owns image-node-name.
+#
+# @backing-file: The string to write as the backing file. This
+# string is not validated, so care should be taken
+# when specifying the string or the image chain may
+# not be able to be reopened again.
+#
+# Since: 2.1
+##
+{ 'command': 'change-backing-file',
+ 'data': { 'device': 'str', 'image-node-name': 'str',
+ 'backing-file': 'str' } }
+
+##
# @block-commit
#
# Live commit of data from overlay image nodes into backing nodes - i.e.,
@@ -690,8 +716,26 @@
# @base: #optional The file name of the backing image to write data into.
# If not specified, this is the deepest backing image
#
-# @top: The file name of the backing image within the image chain,
-# which contains the topmost data to be committed down.
+# @top: #optional The file name of the backing image within the image chain,
+# which contains the topmost data to be committed down. If
+# not specified, this is the active layer.
+#
+# @backing-file: #optional The backing file string to write into the overlay
+# image of 'top'. If 'top' is the active layer,
+# specifying a backing file string is an error. This
+# filename is not validated.
+#
+# If a pathname string is such that it cannot be
+# resolved by QEMU, that means that subsequent QMP or
+# HMP commands must use node-names for the image in
+# question, as filename lookup methods will fail.
+#
+# If not specified, QEMU will automatically determine
+# the backing file string to use, or error out if
+# there is no obvious choice. Care should be taken
+# when specifying the string, to specify a valid
+# filename or protocol.
+# (Since 2.1)
#
# If top == base, that is an error.
# If top == active, the job will not be completed by itself,
@@ -705,7 +749,6 @@
# size of the smaller top, you can safely truncate it
# yourself once the commit operation successfully completes.
#
-#
# @speed: #optional the maximum speed, in bytes per second
#
# Returns: Nothing on success
@@ -719,8 +762,8 @@
#
##
{ 'command': 'block-commit',
- 'data': { 'device': 'str', '*base': 'str', 'top': 'str',
- '*speed': 'int' } }
+ 'data': { 'device': 'str', '*base': 'str', '*top': 'str',
+ '*backing-file': 'str', '*speed': 'int' } }
##
# @drive-backup
@@ -879,6 +922,21 @@
#
# @base: #optional the common backing file name
#
+# @backing-file: #optional The backing file string to write into the active
+# layer. This filename is not validated.
+#
+# If a pathname string is such that it cannot be
+# resolved by QEMU, that means that subsequent QMP or
+# HMP commands must use node-names for the image in
+# question, as filename lookup methods will fail.
+#
+# If not specified, QEMU will automatically determine
+# the backing file string to use, or error out if there
+# is no obvious choice. Care should be taken when
+# specifying the string, to specify a valid filename or
+# protocol.
+# (Since 2.1)
+#
# @speed: #optional the maximum speed, in bytes per second
#
# @on-error: #optional the action to take on an error (default report).
@@ -891,8 +949,8 @@
# Since: 1.1
##
{ 'command': 'block-stream',
- 'data': { 'device': 'str', '*base': 'str', '*speed': 'int',
- '*on-error': 'BlockdevOnError' } }
+ 'data': { 'device': 'str', '*base': 'str', '*backing-file': 'str',
+ '*speed': 'int', '*on-error': 'BlockdevOnError' } }
##
# @block-job-set-speed:
diff --git a/qapi/event.json b/qapi/event.json
index ff97aeb..c51dc49 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -288,12 +288,12 @@
#
# @sector-num: number of the first sector of the failed read operation
#
-# @sector-count: failed read operation sector count
+# @sectors-count: failed read operation sector count
#
# Since: 2.0
##
{ 'event': 'QUORUM_FAILURE',
- 'data': { 'reference': 'str', 'sector-num': 'int', 'sector-count': 'int' } }
+ 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
##
# @QUORUM_REPORT_BAD
@@ -309,13 +309,13 @@
#
# @sector-num: number of the first sector of the failed read operation
#
-# @sector-count: failed read operation sector count
+# @sectors-count: failed read operation sector count
#
# Since: 2.0
##
{ 'event': 'QUORUM_REPORT_BAD',
'data': { '*error': 'str', 'node-name': 'str',
- 'sector-num': 'int', 'sector-count': 'int' } }
+ 'sector-num': 'int', 'sectors-count': 'int' } }
##
# @VSERPORT_CHANGE
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 88ec9bb..ad92c85 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -589,6 +589,22 @@
This option can only be enabled if @code{compat=1.1} is specified.
+@item nocow
+If this option is set to @code{on}, it will trun off COW of the file. It's only
+valid on btrfs, no effect on other file systems.
+
+Btrfs has low performance when hosting a VM image file, even more when the guest
+on the VM also using btrfs as file system. Turning off COW is a way to mitigate
+this bad performance. Generally there are two ways to turn off COW on btrfs:
+a) Disable it by mounting with nodatacow, then all newly created files will be
+NOCOW. b) For an empty file, add the NOCOW file attribute. That's what this option
+does.
+
+Note: this option is only valid to new or empty files. If there is an existing
+file which is COW and has data blocks already, it couldn't be changed to NOCOW
+by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if
+the NOCOW flag is set or not (Capitabl 'C' is NOCOW flag).
+
@end table
@item qed
diff --git a/qemu-img.texi b/qemu-img.texi
index c68b541..8496f3b 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -474,6 +474,22 @@
This option can only be enabled if @code{compat=1.1} is specified.
+@item nocow
+If this option is set to @code{on}, it will trun off COW of the file. It's only
+valid on btrfs, no effect on other file systems.
+
+Btrfs has low performance when hosting a VM image file, even more when the guest
+on the VM also using btrfs as file system. Turning off COW is a way to mitigate
+this bad performance. Generally there are two ways to turn off COW on btrfs:
+a) Disable it by mounting with nodatacow, then all newly created files will be
+NOCOW. b) For an empty file, add the NOCOW file attribute. That's what this option
+does.
+
+Note: this option is only valid to new or empty files. If there is an existing
+file which is COW and has data blocks already, it couldn't be changed to NOCOW
+by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if
+the NOCOW flag is set or not (Capitabl 'C' is NOCOW flag).
+
@end table
@item Other
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 1ea18b2..4be4765 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -979,13 +979,13 @@
{
.name = "block-stream",
- .args_type = "device:B,base:s?,speed:o?,on-error:s?",
+ .args_type = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?",
.mhandler.cmd_new = qmp_marshal_input_block_stream,
},
{
.name = "block-commit",
- .args_type = "device:B,base:s?,top:s,speed:o?",
+ .args_type = "device:B,base:s?,top:s?,backing-file:s?,speed:o?",
.mhandler.cmd_new = qmp_marshal_input_block_commit,
},
@@ -1003,7 +1003,25 @@
If not specified, this is the deepest backing image
(json-string, optional)
- "top": The file name of the backing image within the image chain,
- which contains the topmost data to be committed down.
+ which contains the topmost data to be committed down. If
+ not specified, this is the active layer. (json-string, optional)
+
+- backing-file: The backing file string to write into the overlay
+ image of 'top'. If 'top' is the active layer,
+ specifying a backing file string is an error. This
+ filename is not validated.
+
+ If a pathname string is such that it cannot be
+ resolved by QEMU, that means that subsequent QMP or
+ HMP commands must use node-names for the image in
+ question, as filename lookup methods will fail.
+
+ If not specified, QEMU will automatically determine
+ the backing file string to use, or error out if
+ there is no obvious choice. Care should be taken
+ when specifying the string, to specify a valid
+ filename or protocol.
+ (json-string, optional) (Since 2.1)
If top == base, that is an error.
If top == active, the job will not be completed by itself,
@@ -1352,6 +1370,45 @@
EQMP
{
+ .name = "change-backing-file",
+ .args_type = "device:s,image-node-name:s,backing-file:s",
+ .mhandler.cmd_new = qmp_marshal_input_change_backing_file,
+ },
+
+SQMP
+change-backing-file
+-------------------
+Since: 2.1
+
+Change the backing file in the image file metadata. This does not cause
+QEMU to reopen the image file to reparse the backing filename (it may,
+however, perform a reopen to change permissions from r/o -> r/w -> r/o,
+if needed). The new backing file string is written into the image file
+metadata, and the QEMU internal strings are updated.
+
+Arguments:
+
+- "image-node-name": The name of the block driver state node of the
+ image to modify. The "device" is argument is used to
+ verify "image-node-name" is in the chain described by
+ "device".
+ (json-string, optional)
+
+- "device": The name of the device.
+ (json-string)
+
+- "backing-file": The string to write as the backing file. This string is
+ not validated, so care should be taken when specifying
+ the string or the image chain may not be able to be
+ reopened again.
+ (json-string)
+
+Returns: Nothing on success
+ If "device" does not exist or cannot be determined, DeviceNotFound
+
+EQMP
+
+ {
.name = "balloon",
.args_type = "value:M",
.mhandler.cmd_new = qmp_marshal_input_balloon,
diff --git a/qom/object.c b/qom/object.c
index 7cefdf2..0e8267b 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -356,11 +356,6 @@
return strstart(prop->type, "child<", NULL);
}
-static inline bool object_property_is_link(ObjectProperty *prop)
-{
- return strstart(prop->type, "link<", NULL);
-}
-
static void object_property_del_all(Object *obj)
{
while (!QTAILQ_EMPTY(&obj->properties)) {
@@ -716,11 +711,17 @@
void object_ref(Object *obj)
{
+ if (!obj) {
+ return;
+ }
atomic_inc(&obj->ref);
}
void object_unref(Object *obj)
{
+ if (!obj) {
+ return;
+ }
g_assert(obj->ref > 0);
/* parent always holds a reference to its children */
@@ -729,11 +730,12 @@
}
}
-void object_property_add(Object *obj, const char *name, const char *type,
- ObjectPropertyAccessor *get,
- ObjectPropertyAccessor *set,
- ObjectPropertyRelease *release,
- void *opaque, Error **errp)
+ObjectProperty *
+object_property_add(Object *obj, const char *name, const char *type,
+ ObjectPropertyAccessor *get,
+ ObjectPropertyAccessor *set,
+ ObjectPropertyRelease *release,
+ void *opaque, Error **errp)
{
ObjectProperty *prop;
@@ -742,7 +744,7 @@
error_setg(errp, "attempt to add duplicate property '%s'"
" to object (type '%s')", name,
object_get_typename(obj));
- return;
+ return NULL;
}
}
@@ -757,6 +759,7 @@
prop->opaque = opaque;
QTAILQ_INSERT_TAIL(&obj->properties, prop, node);
+ return prop;
}
ObjectProperty *object_property_find(Object *obj, const char *name,
@@ -1029,6 +1032,11 @@
g_free(path);
}
+static Object *object_resolve_child_property(Object *parent, void *opaque, const gchar *part)
+{
+ return opaque;
+}
+
static void object_finalize_child_property(Object *obj, const char *name,
void *opaque)
{
@@ -1042,15 +1050,18 @@
{
Error *local_err = NULL;
gchar *type;
+ ObjectProperty *op;
type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child)));
- object_property_add(obj, name, type, object_get_child_property, NULL,
- object_finalize_child_property, child, &local_err);
+ op = object_property_add(obj, name, type, object_get_child_property, NULL,
+ object_finalize_child_property, child, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
+
+ op->resolve = object_resolve_child_property;
object_ref(child);
g_assert(child->parent == NULL);
child->parent = obj;
@@ -1155,13 +1166,16 @@
return;
}
- if (new_target) {
- object_ref(new_target);
- }
+ object_ref(new_target);
*child = new_target;
- if (old_target != NULL) {
- object_unref(old_target);
- }
+ object_unref(old_target);
+}
+
+static Object *object_resolve_link_property(Object *parent, void *opaque, const gchar *part)
+{
+ LinkProperty *lprop = opaque;
+
+ return *lprop->child;
}
static void object_release_link_property(Object *obj, const char *name,
@@ -1185,6 +1199,7 @@
Error *local_err = NULL;
LinkProperty *prop = g_malloc(sizeof(*prop));
gchar *full_type;
+ ObjectProperty *op;
prop->child = child;
prop->check = check;
@@ -1192,17 +1207,21 @@
full_type = g_strdup_printf("link<%s>", type);
- object_property_add(obj, name, full_type,
- object_get_link_property,
- check ? object_set_link_property : NULL,
- object_release_link_property,
- prop,
- &local_err);
+ op = object_property_add(obj, name, full_type,
+ object_get_link_property,
+ check ? object_set_link_property : NULL,
+ object_release_link_property,
+ prop,
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
g_free(prop);
+ goto out;
}
+ op->resolve = object_resolve_link_property;
+
+out:
g_free(full_type);
}
@@ -1261,11 +1280,8 @@
return NULL;
}
- if (object_property_is_link(prop)) {
- LinkProperty *lprop = prop->opaque;
- return *lprop->child;
- } else if (object_property_is_child(prop)) {
- return prop->opaque;
+ if (prop->resolve) {
+ return prop->resolve(parent, prop->opaque, part);
} else {
return NULL;
}
@@ -1551,6 +1567,77 @@
NULL, NULL, (void *)v, errp);
}
+typedef struct {
+ Object *target_obj;
+ const char *target_name;
+} AliasProperty;
+
+static void property_get_alias(Object *obj, struct Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ AliasProperty *prop = opaque;
+
+ object_property_get(prop->target_obj, v, prop->target_name, errp);
+}
+
+static void property_set_alias(Object *obj, struct Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ AliasProperty *prop = opaque;
+
+ object_property_set(prop->target_obj, v, prop->target_name, errp);
+}
+
+static Object *property_resolve_alias(Object *obj, void *opaque,
+ const gchar *part)
+{
+ AliasProperty *prop = opaque;
+
+ return object_resolve_path_component(prop->target_obj, prop->target_name);
+}
+
+static void property_release_alias(Object *obj, const char *name, void *opaque)
+{
+ AliasProperty *prop = opaque;
+
+ g_free(prop);
+}
+
+void object_property_add_alias(Object *obj, const char *name,
+ Object *target_obj, const char *target_name,
+ Error **errp)
+{
+ AliasProperty *prop;
+ ObjectProperty *op;
+ ObjectProperty *target_prop;
+ gchar *prop_type;
+
+ target_prop = object_property_find(target_obj, target_name, errp);
+ if (!target_prop) {
+ return;
+ }
+
+ if (object_property_is_child(target_prop)) {
+ prop_type = g_strdup_printf("link%s",
+ target_prop->type + strlen("child"));
+ } else {
+ prop_type = g_strdup(target_prop->type);
+ }
+
+ prop = g_malloc(sizeof(*prop));
+ prop->target_obj = target_obj;
+ prop->target_name = target_name;
+
+ op = object_property_add(obj, name, prop_type,
+ property_get_alias,
+ property_set_alias,
+ property_release_alias,
+ prop, errp);
+ op->resolve = property_resolve_alias;
+
+ g_free(prop_type);
+}
+
static void object_instance_init(Object *obj)
{
object_property_add_str(obj, "type", qdev_get_type, NULL, NULL);
diff --git a/tests/fdc-test.c b/tests/fdc-test.c
index 37096dc..c8e1e7b 100644
--- a/tests/fdc-test.c
+++ b/tests/fdc-test.c
@@ -291,7 +291,7 @@
/* Insert media in drive. DSKCHK should not be reset until a step pulse
* is sent. */
qmp_discard_response("{'execute':'change', 'arguments':{"
- " 'device':'floppy0', 'target': '%s' }}",
+ " 'device':'floppy0', 'target': %s }}",
test_image);
qmp_discard_response(""); /* ignore event
(FIXME open -> open transition?!) */
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 71468ac..98e8f4b 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -30,8 +30,9 @@
#include "qemu/compiler.h"
#include "qemu/osdep.h"
-#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
+#include "qapi/qmp/json-streamer.h"
+#include "qapi/qmp/qjson.h"
#define MAX_IRQ 256
#define SOCKET_TIMEOUT 5
@@ -220,19 +221,15 @@
g_free(s);
}
-static void socket_sendf(int fd, const char *fmt, va_list ap)
+static void socket_send(int fd, const char *buf, size_t size)
{
- gchar *str;
- size_t size, offset;
-
- str = g_strdup_vprintf(fmt, ap);
- size = strlen(str);
+ size_t offset;
offset = 0;
while (offset < size) {
ssize_t len;
- len = write(fd, str + offset, size - offset);
+ len = write(fd, buf + offset, size - offset);
if (len == -1 && errno == EINTR) {
continue;
}
@@ -244,6 +241,15 @@
}
}
+static void socket_sendf(int fd, const char *fmt, va_list ap)
+{
+ gchar *str = g_strdup_vprintf(fmt, ap);
+ size_t size = strlen(str);
+
+ socket_send(fd, str, size);
+ g_free(str);
+}
+
static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
{
va_list ap;
@@ -378,8 +384,29 @@
QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
{
- /* Send QMP request */
- socket_sendf(s->qmp_fd, fmt, ap);
+ va_list ap_copy;
+ QObject *qobj;
+
+ /* Going through qobject ensures we escape strings properly.
+ * This seemingly unnecessary copy is required in case va_list
+ * is an array type.
+ */
+ va_copy(ap_copy, ap);
+ qobj = qobject_from_jsonv(fmt, &ap_copy);
+ va_end(ap_copy);
+
+ /* No need to send anything for an empty QObject. */
+ if (qobj) {
+ QString *qstr = qobject_to_json(qobj);
+ const char *str = qstring_get_str(qstr);
+ size_t size = qstring_get_length(qstr);
+
+ /* Send QMP request */
+ socket_send(s->qmp_fd, str, size);
+
+ QDECREF(qstr);
+ qobject_decref(qobj);
+ }
/* Receive reply */
return qtest_qmp_receive(s);
diff --git a/tests/qemu-iotests-quick.sh b/tests/qemu-iotests-quick.sh
index c449e8a..8a9a4c6 100755
--- a/tests/qemu-iotests-quick.sh
+++ b/tests/qemu-iotests-quick.sh
@@ -1,16 +1,6 @@
#!/bin/sh
-# We don't know which of the system emulator binaries there is (or if there is
-# any at all), so the 'quick' group doesn't contain any tests that require
-# running qemu proper. Assign a fake binary name so that qemu-iotests doesn't
-# complain about the missing binary.
-export QEMU_PROG="this_should_be_unused"
-
-export QEMU_IMG_PROG="$(pwd)/qemu-img"
-export QEMU_IO_PROG="$(pwd)/qemu-io"
-export QEMU_NBD_PROG="$(pwd)/qemu-nbd"
-
-cd $SRC_PATH/tests/qemu-iotests
+cd tests/qemu-iotests
ret=0
./check -T -nocache -qcow2 -g quick || ret=1
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index d166810..f1e16c1 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -35,11 +35,7 @@
class ImageCommitTestCase(iotests.QMPTestCase):
'''Abstract base class for image commit test cases'''
- def run_commit_test(self, top, base, need_ready=False):
- self.assert_no_active_block_jobs()
- result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
- self.assert_qmp(result, 'return', {})
-
+ def wait_for_complete(self, need_ready=False):
completed = False
ready = False
while not completed:
@@ -62,6 +58,18 @@
self.assert_no_active_block_jobs()
self.vm.shutdown()
+ def run_commit_test(self, top, base, need_ready=False):
+ self.assert_no_active_block_jobs()
+ result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
+ self.assert_qmp(result, 'return', {})
+ self.wait_for_complete(need_ready)
+
+ def run_default_commit_test(self):
+ self.assert_no_active_block_jobs()
+ result = self.vm.qmp('block-commit', device='drive0')
+ self.assert_qmp(result, 'return', {})
+ self.wait_for_complete()
+
class TestSingleDrive(ImageCommitTestCase):
image_len = 1 * 1024 * 1024
test_len = 1 * 1024 * 256
@@ -113,17 +121,17 @@
self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
+ def test_top_is_default_active(self):
+ self.run_default_commit_test()
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
+
def test_top_and_base_reversed(self):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img)
- def test_top_omitted(self):
- self.assert_no_active_block_jobs()
- result = self.vm.qmp('block-commit', device='drive0')
- self.assert_qmp(result, 'error/class', 'GenericError')
- self.assert_qmp(result, 'error/desc', "Parameter 'top' is missing")
class TestRelativePaths(ImageCommitTestCase):
image_len = 1 * 1024 * 1024
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
index 28309a0..413e7ef 100644
--- a/tests/qemu-iotests/082.out
+++ b/tests/qemu-iotests/082.out
@@ -66,6 +66,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M
Supported options:
@@ -77,6 +78,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M
Supported options:
@@ -88,6 +90,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M
Supported options:
@@ -99,6 +102,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M
Supported options:
@@ -110,6 +114,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M
Supported options:
@@ -121,6 +126,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M
Supported options:
@@ -132,6 +138,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M
Supported options:
@@ -143,6 +150,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off
@@ -247,6 +255,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -258,6 +267,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -269,6 +279,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -280,6 +291,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -291,6 +303,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -302,6 +315,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -313,6 +327,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
Supported options:
@@ -324,6 +339,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,help': No such file or directory
@@ -417,6 +433,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
Supported options:
@@ -428,6 +445,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
Supported options:
@@ -439,6 +457,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
Supported options:
@@ -450,6 +469,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
Supported options:
@@ -461,6 +481,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
Supported options:
@@ -472,6 +493,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
Supported options:
@@ -483,6 +505,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
Supported options:
@@ -494,6 +517,7 @@
cluster_size qcow2 cluster size
preallocation Preallocation mode (allowed values: off, metadata)
lazy_refcounts Postpone refcount updates
+nocow Turn off copy-on-write (valid only on btrfs)
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index e3dc4e8..6e67f61 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -7,16 +7,16 @@
#
# test-group association ... one line per test
#
-001 rw auto
+001 rw auto quick
002 rw auto quick
003 rw auto
004 rw auto quick
-005 img auto
+005 img auto quick
006 img auto
007 snapshot auto
-008 rw auto
-009 rw auto
-010 rw auto
+008 rw auto quick
+009 rw auto quick
+010 rw auto quick
011 rw auto quick
012 auto quick
013 rw auto
@@ -24,36 +24,36 @@
015 rw snapshot auto
016 rw auto quick
017 rw backing auto quick
-018 rw backing auto
+018 rw backing auto quick
019 rw backing auto quick
020 rw backing auto quick
-021 io auto
+021 io auto quick
022 rw snapshot auto
023 rw auto
024 rw backing auto quick
025 rw auto quick
026 rw blkdbg auto
027 rw auto quick
-028 rw backing auto
+028 rw backing auto quick
029 rw auto quick
030 rw auto backing
031 rw auto quick
-032 rw auto
+032 rw auto quick
033 rw auto quick
-034 rw auto backing
+034 rw auto backing quick
035 rw auto quick
036 rw auto quick
-037 rw auto backing
-038 rw auto backing
-039 rw auto
+037 rw auto backing quick
+038 rw auto backing quick
+039 rw auto quick
040 rw auto
041 rw auto backing
042 rw auto quick
043 rw auto backing
044 rw auto
-045 rw auto
-046 rw auto aio
-047 rw auto
+045 rw auto quick
+046 rw auto aio quick
+047 rw auto quick
048 img auto quick
049 rw auto
050 rw auto backing quick
@@ -71,32 +71,32 @@
062 rw auto quick
063 rw auto quick
064 rw auto quick
-065 rw auto
+065 rw auto quick
066 rw auto quick
-067 rw auto
-068 rw auto
+067 rw auto quick
+068 rw auto quick
069 rw auto quick
070 rw auto quick
-071 rw auto
+071 rw auto quick
072 rw auto quick
073 rw auto quick
074 rw auto quick
-075 rw auto
+075 rw auto quick
076 auto
077 rw auto quick
-078 rw auto
+078 rw auto quick
079 rw auto
080 rw auto
-081 rw auto
+081 rw auto quick
082 rw auto quick
083 rw auto
-084 img auto
+084 img auto quick
085 rw auto
086 rw auto quick
-087 rw auto
-088 rw auto
+087 rw auto quick
+088 rw auto quick
089 rw auto quick
090 rw auto quick
-091 rw auto
+091 rw auto quick
092 rw auto quick
-095 rw auto
+095 rw auto quick
diff --git a/tests/qom-test.c b/tests/qom-test.c
index d8d1d8d..4246382 100644
--- a/tests/qom-test.c
+++ b/tests/qom-test.c
@@ -53,7 +53,7 @@
g_test_message("Obtaining properties of %s", path);
response = qmp("{ 'execute': 'qom-list',"
- " 'arguments': { 'path': '%s' } }", path);
+ " 'arguments': { 'path': %s } }", path);
g_assert(response);
if (!recurse) {
@@ -76,8 +76,8 @@
const char *prop = qdict_get_str(tuple, "name");
g_test_message("Testing property %s.%s", path, prop);
response = qmp("{ 'execute': 'qom-get',"
- " 'arguments': { 'path': '%s',"
- " 'property': '%s' } }",
+ " 'arguments': { 'path': %s,"
+ " 'property': %s } }",
path, prop);
/* qom-get may fail but should not, e.g., segfault. */
g_assert(response);
diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c
index 15ddaf3..99db538 100644
--- a/tests/tmp105-test.c
+++ b/tests/tmp105-test.c
@@ -69,7 +69,7 @@
QDict *response;
int ret;
- response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': '%s', "
+ response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, "
"'property': 'temperature' } }", id);
g_assert(qdict_haskey(response, "return"));
ret = qdict_get_int(response, "return");
@@ -81,7 +81,7 @@
{
QDict *response;
- response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': '%s', "
+ response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, "
"'property': 'temperature', 'value': %d } }", id, value);
g_assert(qdict_haskey(response, "return"));
QDECREF(response);
diff --git a/trace-events b/trace-events
index d071b97..11a17a8 100644
--- a/trace-events
+++ b/trace-events
@@ -511,6 +511,8 @@
g_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p"
g_free(void *ptr) "ptr %p"
system_wakeup_request(int reason) "reason=%d"
+qemu_system_shutdown_request(void) ""
+qemu_system_powerdown_request(void) ""
# block/qcow2.c
qcow2_writev_start_req(void *co, int64_t sector, int nb_sectors) "co %p sector %" PRIx64 " nb_sectors %d"
diff --git a/ui/vnc.c b/ui/vnc.c
index 14a86c3..548588a 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -432,14 +432,10 @@
static void vnc_refresh(DisplayChangeListener *dcl);
static int vnc_refresh_server_surface(VncDisplay *vd);
-static void vnc_dpy_update(DisplayChangeListener *dcl,
- int x, int y, int w, int h)
-{
- VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
- struct VncSurface *s = &vd->guest;
- int width = surface_width(vd->ds);
- int height = surface_height(vd->ds);
-
+static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
+ VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT),
+ int width, int height,
+ int x, int y, int w, int h) {
/* this is needed this to ensure we updated all affected
* blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
w += (x % VNC_DIRTY_PIXELS_PER_BIT);
@@ -451,11 +447,22 @@
h = MIN(y + h, height);
for (; y < h; y++) {
- bitmap_set(s->dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
+ bitmap_set(dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));
}
}
+static void vnc_dpy_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
+ struct VncSurface *s = &vd->guest;
+ int width = pixman_image_get_width(vd->server);
+ int height = pixman_image_get_height(vd->server);
+
+ vnc_set_area_dirty(s->dirty, width, height, x, y, w, h);
+}
+
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
int32_t encoding)
{
@@ -517,17 +524,15 @@
static void vnc_desktop_resize(VncState *vs)
{
- DisplaySurface *ds = vs->vd->ds;
-
if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
return;
}
- if (vs->client_width == surface_width(ds) &&
- vs->client_height == surface_height(ds)) {
+ if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
+ vs->client_height == pixman_image_get_height(vs->vd->server)) {
return;
}
- vs->client_width = surface_width(ds);
- vs->client_height = surface_height(ds);
+ vs->client_width = pixman_image_get_width(vs->vd->server);
+ vs->client_height = pixman_image_get_height(vs->vd->server);
vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
@@ -571,31 +576,24 @@
ptr += x * VNC_SERVER_FB_BYTES;
return ptr;
}
-/* this sets only the visible pixels of a dirty bitmap */
-#define VNC_SET_VISIBLE_PIXELS_DIRTY(bitmap, w, h) {\
- int y;\
- memset(bitmap, 0x00, sizeof(bitmap));\
- for (y = 0; y < h; y++) {\
- bitmap_set(bitmap[y], 0,\
- DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));\
- } \
- }
static void vnc_dpy_switch(DisplayChangeListener *dcl,
DisplaySurface *surface)
{
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
VncState *vs;
+ int width, height;
vnc_abort_display_jobs(vd);
/* server surface */
qemu_pixman_image_unref(vd->server);
vd->ds = surface;
+ width = MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
+ VNC_DIRTY_PIXELS_PER_BIT));
+ height = MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
- surface_width(vd->ds),
- surface_height(vd->ds),
- NULL, 0);
+ width, height, NULL, 0);
/* guest surface */
#if 0 /* FIXME */
@@ -605,9 +603,9 @@
qemu_pixman_image_unref(vd->guest.fb);
vd->guest.fb = pixman_image_ref(surface->image);
vd->guest.format = surface->format;
- VNC_SET_VISIBLE_PIXELS_DIRTY(vd->guest.dirty,
- surface_width(vd->ds),
- surface_height(vd->ds));
+ memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
+ vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0,
+ width, height);
QTAILQ_FOREACH(vs, &vd->clients, next) {
vnc_colordepth(vs);
@@ -615,9 +613,9 @@
if (vs->vd->cursor) {
vnc_cursor_define(vs);
}
- VNC_SET_VISIBLE_PIXELS_DIRTY(vs->dirty,
- surface_width(vd->ds),
- surface_height(vd->ds));
+ memset(vs->dirty, 0x00, sizeof(vs->dirty));
+ vnc_set_area_dirty(vs->dirty, width, height, 0, 0,
+ width, height);
}
}
@@ -911,8 +909,8 @@
*/
job = vnc_job_new(vs);
- height = MIN(pixman_image_get_height(vd->server), vs->client_height);
- width = MIN(pixman_image_get_width(vd->server), vs->client_width);
+ height = pixman_image_get_height(vd->server);
+ width = pixman_image_get_width(vd->server);
y = 0;
for (;;) {
@@ -1501,8 +1499,8 @@
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, absolute, 0,
- surface_width(vs->vd->ds),
- surface_height(vs->vd->ds),
+ pixman_image_get_width(vs->vd->server),
+ pixman_image_get_height(vs->vd->server),
VNC_ENCODING_POINTER_TYPE_CHANGE);
vnc_unlock_output(vs);
vnc_flush(vs);
@@ -1520,8 +1518,8 @@
[INPUT_BUTTON_WHEEL_DOWN] = 0x10,
};
QemuConsole *con = vs->vd->dcl.con;
- int width = surface_width(vs->vd->ds);
- int height = surface_height(vs->vd->ds);
+ int width = pixman_image_get_width(vs->vd->server);
+ int height = pixman_image_get_height(vs->vd->server);
if (vs->last_bmask != button_mask) {
qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask);
@@ -1869,29 +1867,18 @@
}
static void framebuffer_update_request(VncState *vs, int incremental,
- int x_position, int y_position,
- int w, int h)
+ int x, int y, int w, int h)
{
- int i;
- const size_t width = surface_width(vs->vd->ds) / VNC_DIRTY_PIXELS_PER_BIT;
- const size_t height = surface_height(vs->vd->ds);
-
- if (y_position > height) {
- y_position = height;
- }
- if (y_position + h >= height) {
- h = height - y_position;
- }
+ int width = pixman_image_get_width(vs->vd->server);
+ int height = pixman_image_get_height(vs->vd->server);
vs->need_update = 1;
- if (!incremental) {
- vs->force_update = 1;
- for (i = 0; i < h; i++) {
- bitmap_set(vs->dirty[y_position + i], 0, width);
- bitmap_clear(vs->dirty[y_position + i], width,
- VNC_DIRTY_BITS - width);
- }
+
+ if (incremental) {
+ return;
}
+
+ vnc_set_area_dirty(vs->dirty, width, height, x, y, w, h);
}
static void send_ext_key_event_ack(VncState *vs)
@@ -1901,8 +1888,8 @@
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0,
- surface_width(vs->vd->ds),
- surface_height(vs->vd->ds),
+ pixman_image_get_width(vs->vd->server),
+ pixman_image_get_height(vs->vd->server),
VNC_ENCODING_EXT_KEY_EVENT);
vnc_unlock_output(vs);
vnc_flush(vs);
@@ -1915,8 +1902,8 @@
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0,
- surface_width(vs->vd->ds),
- surface_height(vs->vd->ds),
+ pixman_image_get_width(vs->vd->server),
+ pixman_image_get_height(vs->vd->server),
VNC_ENCODING_AUDIO);
vnc_unlock_output(vs);
vnc_flush(vs);
@@ -2094,8 +2081,8 @@
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0,
- surface_width(vs->vd->ds),
- surface_height(vs->vd->ds),
+ pixman_image_get_width(vs->vd->server),
+ pixman_image_get_height(vs->vd->server),
VNC_ENCODING_WMVi);
pixel_format_message(vs);
vnc_unlock_output(vs);
@@ -2165,13 +2152,20 @@
pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
break;
case VNC_MSG_CLIENT_CUT_TEXT:
- if (len == 1)
+ if (len == 1) {
return 8;
-
+ }
if (len == 8) {
uint32_t dlen = read_u32(data, 4);
- if (dlen > 0)
+ if (dlen > (1 << 20)) {
+ error_report("vnc: client_cut_text msg payload has %u bytes"
+ " which exceeds our limit of 1MB.", dlen);
+ vnc_client_error(vs);
+ break;
+ }
+ if (dlen > 0) {
return 8 + dlen;
+ }
}
client_cut_text(vs, read_u32(data, 4), data + 8);
@@ -2310,8 +2304,8 @@
}
vnc_set_share_mode(vs, mode);
- vs->client_width = surface_width(vs->vd->ds);
- vs->client_height = surface_height(vs->vd->ds);
+ vs->client_width = pixman_image_get_width(vs->vd->server);
+ vs->client_height = pixman_image_get_height(vs->vd->server);
vnc_write_u16(vs, vs->client_width);
vnc_write_u16(vs, vs->client_height);
@@ -2678,12 +2672,12 @@
static int vnc_refresh_server_surface(VncDisplay *vd)
{
- int width = pixman_image_get_width(vd->guest.fb);
- int height = pixman_image_get_height(vd->guest.fb);
- int y;
+ int width = MIN(pixman_image_get_width(vd->guest.fb),
+ pixman_image_get_width(vd->server));
+ int height = MIN(pixman_image_get_height(vd->guest.fb),
+ pixman_image_get_height(vd->server));
+ int cmp_bytes, server_stride, min_stride, guest_stride, y = 0;
uint8_t *guest_row0 = NULL, *server_row0;
- int guest_stride = 0, server_stride;
- int cmp_bytes;
VncState *vs;
int has_dirty = 0;
pixman_image_t *tmpbuf = NULL;
@@ -2700,10 +2694,10 @@
* Check and copy modified bits from guest to server surface.
* Update server dirty map.
*/
- cmp_bytes = VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES;
- if (cmp_bytes > vnc_server_fb_stride(vd)) {
- cmp_bytes = vnc_server_fb_stride(vd);
- }
+ server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
+ server_stride = guest_stride = pixman_image_get_stride(vd->server);
+ cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
+ server_stride);
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
int width = pixman_image_get_width(vd->server);
tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
@@ -2711,10 +2705,8 @@
guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
guest_stride = pixman_image_get_stride(vd->guest.fb);
}
- server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
- server_stride = pixman_image_get_stride(vd->server);
+ min_stride = MIN(server_stride, guest_stride);
- y = 0;
for (;;) {
int x;
uint8_t *guest_ptr, *server_ptr;
@@ -2740,13 +2732,17 @@
for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
+ int _cmp_bytes = cmp_bytes;
if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
continue;
}
- if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) {
+ if ((x + 1) * cmp_bytes > min_stride) {
+ _cmp_bytes = min_stride - x * cmp_bytes;
+ }
+ if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
continue;
}
- memcpy(server_ptr, guest_ptr, cmp_bytes);
+ memcpy(server_ptr, guest_ptr, _cmp_bytes);
if (!vd->non_adaptive) {
vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
y, &tv);
diff --git a/ui/vnc.h b/ui/vnc.h
index 07af9f7..8f582fd 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -77,14 +77,15 @@
void *last_fg,
int *has_bg, int *has_fg);
-/* VNC_MAX_WIDTH must be a multiple of 16. */
-#define VNC_MAX_WIDTH 2560
-#define VNC_MAX_HEIGHT 2048
-
/* VNC_DIRTY_PIXELS_PER_BIT is the number of dirty pixels represented
- * by one bit in the dirty bitmap */
+ * by one bit in the dirty bitmap, should be a power of 2 */
#define VNC_DIRTY_PIXELS_PER_BIT 16
+/* VNC_MAX_WIDTH must be a multiple of VNC_DIRTY_PIXELS_PER_BIT. */
+
+#define VNC_MAX_WIDTH ROUND_UP(2560, VNC_DIRTY_PIXELS_PER_BIT)
+#define VNC_MAX_HEIGHT 2048
+
/* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */
#define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT)
@@ -126,7 +127,8 @@
struct VncSurface
{
struct timeval last_freq_check;
- DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_MAX_WIDTH / 16);
+ DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
+ VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT);
VncRectStat stats[VNC_STAT_ROWS][VNC_STAT_COLS];
pixman_image_t *fb;
pixman_format_code_t format;
diff --git a/vl.c b/vl.c
index 41ddcd26..6e084c2 100644
--- a/vl.c
+++ b/vl.c
@@ -1921,6 +1921,7 @@
void qemu_system_shutdown_request(void)
{
+ trace_qemu_system_shutdown_request();
shutdown_requested = 1;
qemu_notify_event();
}
@@ -1933,6 +1934,7 @@
void qemu_system_powerdown_request(void)
{
+ trace_qemu_system_powerdown_request();
powerdown_requested = 1;
qemu_notify_event();
}
@@ -3986,12 +3988,11 @@
exit(1);
}
- cpu_exec_init_all();
-
current_machine = MACHINE(object_new(object_class_get_name(
OBJECT_CLASS(machine_class))));
object_property_add_child(object_get_root(), "machine",
OBJECT(current_machine), &error_abort);
+ cpu_exec_init_all();
if (machine_class->hw_version) {
qemu_set_version(machine_class->hw_version);