aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README (renamed from README.md)41
-rw-r--r--block.c2
-rwxr-xr-xblock/blkreplay.c8
-rw-r--r--block/block-backend.c22
-rw-r--r--block/dirty-bitmap.c6
-rw-r--r--block/gluster.c642
-rw-r--r--block/io.c242
-rw-r--r--block/iscsi.c32
-rw-r--r--block/mirror.c157
-rw-r--r--block/nbd-client.c107
-rw-r--r--block/nbd-client.h11
-rw-r--r--block/nbd.c30
-rw-r--r--block/qcow2-refcount.c4
-rw-r--r--block/qcow2.c10
-rw-r--r--block/raw-posix.c24
-rw-r--r--block/raw-win32.c19
-rw-r--r--block/raw_bsd.c59
-rw-r--r--block/rbd.c29
-rw-r--r--block/sheepdog.c17
-rw-r--r--block/trace-events4
-rw-r--r--blockdev.c189
-rw-r--r--crypto/Makefile.objs4
-rw-r--r--crypto/hash-gcrypt.c3
-rw-r--r--crypto/hash-glib.c97
-rw-r--r--crypto/hash-nettle.c3
-rw-r--r--crypto/hash-stub.c41
-rw-r--r--crypto/random-platform.c (renamed from crypto/random-stub.c)37
-rw-r--r--docs/qapi-code-gen.txt27
-rw-r--r--hmp.c70
-rw-r--r--hw/arm/musicpal.c2
-rw-r--r--hw/block/xen_disk.c7
-rw-r--r--hw/core/qdev-properties-system.c2
-rw-r--r--hw/display/qxl.c11
-rw-r--r--hw/display/trace-events2
-rw-r--r--hw/i386/kvm/apic.c9
-rw-r--r--hw/i386/pc.c279
-rw-r--r--hw/ide/core.c6
-rw-r--r--hw/intc/apic.c26
-rw-r--r--hw/intc/apic_common.c33
-rw-r--r--hw/intc/arm_gicv3_redist.c4
-rw-r--r--hw/net/allwinner_emac.c2
-rw-r--r--hw/net/cadence_gem.c2
-rw-r--r--hw/net/dp8393x.c2
-rw-r--r--hw/net/e1000.c2
-rw-r--r--hw/net/e1000e.c2
-rw-r--r--hw/net/eepro100.c2
-rw-r--r--hw/net/etraxfs_eth.c2
-rw-r--r--hw/net/fsl_etsec/etsec.c2
-rw-r--r--hw/net/imx_fec.c2
-rw-r--r--hw/net/lan9118.c2
-rw-r--r--hw/net/lance.c2
-rw-r--r--hw/net/mcf_fec.c2
-rw-r--r--hw/net/milkymist-minimac2.c2
-rw-r--r--hw/net/mipsnet.c2
-rw-r--r--hw/net/ne2000-isa.c2
-rw-r--r--hw/net/ne2000.c2
-rw-r--r--hw/net/opencores_eth.c2
-rw-r--r--hw/net/pcnet-pci.c2
-rw-r--r--hw/net/rocker/rocker_fp.c2
-rw-r--r--hw/net/rtl8139.c2
-rw-r--r--hw/net/smc91c111.c2
-rw-r--r--hw/net/spapr_llan.c2
-rw-r--r--hw/net/stellaris_enet.c2
-rw-r--r--hw/net/vhost_net.c20
-rw-r--r--hw/net/virtio-net.c10
-rw-r--r--hw/net/vmxnet3.c2
-rw-r--r--hw/net/xen_nic.c2
-rw-r--r--hw/net/xgmac.c2
-rw-r--r--hw/net/xilinx_axienet.c2
-rw-r--r--hw/net/xilinx_ethlite.c2
-rw-r--r--hw/s390x/css-bridge.c26
-rw-r--r--hw/s390x/css.c5
-rw-r--r--hw/s390x/s390-virtio-ccw.c4
-rw-r--r--hw/scsi/scsi-disk.c8
-rw-r--r--hw/usb/dev-network.c4
-rw-r--r--hw/usb/hcd-xhci.c4
-rw-r--r--hw/usb/redirect.c6
-rw-r--r--include/block/block.h10
-rw-r--r--include/block/block_int.h10
-rw-r--r--include/block/dirty-bitmap.h4
-rw-r--r--include/block/nbd.h2
-rw-r--r--include/hw/i386/apic_internal.h5
-rw-r--r--include/hw/i386/pc.h10
-rw-r--r--include/hw/i386/topology.h15
-rw-r--r--include/hw/s390x/css-bridge.h7
-rw-r--r--include/net/net.h6
-rw-r--r--include/sysemu/block-backend.h9
-rw-r--r--linux-user/aarch64/syscall_nr.h3
-rw-r--r--linux-user/ioctls.h41
-rw-r--r--linux-user/linux_loop.h11
-rw-r--r--linux-user/signal.c8
-rw-r--r--linux-user/sparc/syscall_nr.h3
-rw-r--r--linux-user/syscall.c454
-rw-r--r--linux-user/syscall_defs.h15
-rw-r--r--linux-user/syscall_types.h9
-rw-r--r--linux-user/x86_64/termbits.h12
-rw-r--r--monitor.c14
-rw-r--r--nbd/common.c5
-rw-r--r--nbd/nbd-internal.h4
-rw-r--r--nbd/server.c19
-rw-r--r--net/clients.h20
-rw-r--r--net/dump.c8
-rw-r--r--net/filter.c2
-rw-r--r--net/hub.c24
-rw-r--r--net/l2tpv3.c8
-rw-r--r--net/net.c155
-rw-r--r--net/netmap.c6
-rw-r--r--net/slirp.c8
-rw-r--r--net/socket.c10
-rw-r--r--net/tap-win32.c8
-rw-r--r--net/tap.c28
-rw-r--r--net/vde.c8
-rw-r--r--net/vhost-user.c22
-rw-r--r--qapi-schema.json49
-rw-r--r--qapi/block-core.json108
-rw-r--r--qapi/qmp-input-visitor.c59
-rw-r--r--qapi/qmp-output-visitor.c24
-rw-r--r--qemu-io-cmds.c3
-rw-r--r--qmp-commands.hx15
-rw-r--r--scripts/qapi-commands.py31
-rw-r--r--scripts/qapi-event.py48
-rw-r--r--scripts/qapi-introspect.py4
-rw-r--r--scripts/qapi-types.py2
-rw-r--r--scripts/qapi-visit.py8
-rw-r--r--scripts/qapi.py127
-rw-r--r--target-arm/machine.c3
-rw-r--r--target-i386/cpu.c198
-rw-r--r--target-i386/cpu.h28
-rw-r--r--target-i386/kvm.c39
-rw-r--r--tests/Makefile.include6
-rw-r--r--tests/docker/Makefile.include14
-rwxr-xr-xtests/docker/docker.py159
-rw-r--r--tests/docker/dockerfiles/debian-bootstrap.docker21
-rwxr-xr-xtests/docker/dockerfiles/debian-bootstrap.pre87
-rwxr-xr-xtests/docker/run16
-rw-r--r--tests/qapi-schema/args-bad-boxed.err1
-rw-r--r--tests/qapi-schema/args-bad-boxed.exit1
-rw-r--r--tests/qapi-schema/args-bad-boxed.json2
-rw-r--r--tests/qapi-schema/args-bad-boxed.out0
-rw-r--r--tests/qapi-schema/args-boxed-anon.err1
-rw-r--r--tests/qapi-schema/args-boxed-anon.exit1
-rw-r--r--tests/qapi-schema/args-boxed-anon.json2
-rw-r--r--tests/qapi-schema/args-boxed-anon.out0
-rw-r--r--tests/qapi-schema/args-boxed-empty.err1
-rw-r--r--tests/qapi-schema/args-boxed-empty.exit1
-rw-r--r--tests/qapi-schema/args-boxed-empty.json3
-rw-r--r--tests/qapi-schema/args-boxed-empty.out0
-rw-r--r--tests/qapi-schema/args-boxed-string.err1
-rw-r--r--tests/qapi-schema/args-boxed-string.exit1
-rw-r--r--tests/qapi-schema/args-boxed-string.json2
-rw-r--r--tests/qapi-schema/args-boxed-string.out0
-rw-r--r--tests/qapi-schema/args-union.err2
-rw-r--r--tests/qapi-schema/args-union.json3
-rw-r--r--tests/qapi-schema/event-boxed-empty.err1
-rw-r--r--tests/qapi-schema/event-boxed-empty.exit1
-rw-r--r--tests/qapi-schema/event-boxed-empty.json2
-rw-r--r--tests/qapi-schema/event-boxed-empty.out0
-rw-r--r--tests/qapi-schema/event-case.out1
-rw-r--r--tests/qapi-schema/flat-union-incomplete-branch.err1
-rw-r--r--tests/qapi-schema/flat-union-incomplete-branch.exit1
-rw-r--r--tests/qapi-schema/flat-union-incomplete-branch.json9
-rw-r--r--tests/qapi-schema/flat-union-incomplete-branch.out0
-rw-r--r--tests/qapi-schema/ident-with-escape.out2
-rw-r--r--tests/qapi-schema/indented-expr.out4
-rw-r--r--tests/qapi-schema/qapi-schema-test.json4
-rw-r--r--tests/qapi-schema/qapi-schema-test.out37
-rw-r--r--tests/qapi-schema/test-qapi.py11
-rw-r--r--tests/test-qmp-commands.c8
168 files changed, 3247 insertions, 1350 deletions
diff --git a/README.md b/README
index 99da481fa1..f38193fc67 100644
--- a/README.md
+++ b/README
@@ -1,5 +1,5 @@
-QEMU
----
+ QEMU README
+ ===========
QEMU is a generic and open source machine & userspace emulator and
virtualizer.
@@ -31,31 +31,31 @@ version 2. For full licensing details, consult the LICENSE file.
Building
----
+========
QEMU is multi-platform software intended to be buildable on all modern
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
of other UNIX targets. The simple steps to build QEMU are:
- mkdir build
- cd build
- ../configure
- make
+ mkdir build
+ cd build
+ ../configure
+ make
Complete details of the process for building and configuring QEMU for
all supported host platforms can be found in the qemu-tech.html file.
Additional information can also be found online via the QEMU website:
- http://qemu-project.org/Hosts/Linux
- http://qemu-project.org/Hosts/W32
+ http://qemu-project.org/Hosts/Linux
+ http://qemu-project.org/Hosts/W32
Submitting patches
----
+==================
The QEMU source code is maintained under the GIT version control system.
- git clone git://git.qemu-project.org/qemu.git
+ git clone git://git.qemu-project.org/qemu.git
When submitting patches, the preferred approach is to use 'git
format-patch' and/or 'git send-email' to format & send the mail to the
@@ -66,18 +66,18 @@ guidelines set out in the HACKING and CODING_STYLE files.
Additional information on submitting patches can be found online via
the QEMU website
- http://qemu-project.org/Contribute/SubmitAPatch
- http://qemu-project.org/Contribute/TrivialPatches
+ http://qemu-project.org/Contribute/SubmitAPatch
+ http://qemu-project.org/Contribute/TrivialPatches
Bug reporting
----
+=============
The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs
found when running code built from QEMU git or upstream released sources
should be reported via:
- https://bugs.launchpad.net/qemu/
+ https://bugs.launchpad.net/qemu/
If using QEMU via an operating system vendor pre-built binary package, it
is preferable to report bugs to the vendor's own bug tracker first. If
@@ -86,21 +86,22 @@ reported via launchpad.
For additional information on bug reporting consult:
- http://qemu-project.org/Contribute/ReportABug
+ http://qemu-project.org/Contribute/ReportABug
Contact
----
+=======
The QEMU community can be contacted in a number of ways, with the two
main methods being email and IRC
- - Mailing List: qemu-devel@nongnu.org
- - Archives: http://lists.nongnu.org/mailman/listinfo/qemu-devel
- - IRC: #qemu on irc.oftc.net
+ - qemu-devel@nongnu.org
+ http://lists.nongnu.org/mailman/listinfo/qemu-devel
+ - #qemu on irc.oftc.net
Information on additional methods of contacting the community can be
found online via the QEMU website:
http://qemu-project.org/Contribute/StartHere
+-- End
diff --git a/block.c b/block.c
index d2dac3dce9..30d64e6ca5 100644
--- a/block.c
+++ b/block.c
@@ -2837,7 +2837,7 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs)
{
BlockDriverInfo bdi;
- if (bs->backing || !(bs->open_flags & BDRV_O_UNMAP)) {
+ if (!(bs->open_flags & BDRV_O_UNMAP)) {
return false;
}
diff --git a/block/blkreplay.c b/block/blkreplay.c
index 3368c8c98d..30f9d5ff6c 100755
--- a/block/blkreplay.c
+++ b/block/blkreplay.c
@@ -114,11 +114,11 @@ static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs,
return ret;
}
-static int coroutine_fn blkreplay_co_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors)
+static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count)
{
uint64_t reqid = request_id++;
- int ret = bdrv_co_discard(bs->file->bs, sector_num, nb_sectors);
+ int ret = bdrv_co_pdiscard(bs->file->bs, offset, count);
block_request_create(reqid, bs, qemu_coroutine_self());
qemu_coroutine_yield();
@@ -148,7 +148,7 @@ static BlockDriver bdrv_blkreplay = {
.bdrv_co_pwritev = blkreplay_co_pwritev,
.bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes,
- .bdrv_co_discard = blkreplay_co_discard,
+ .bdrv_co_pdiscard = blkreplay_co_pdiscard,
.bdrv_co_flush = blkreplay_co_flush,
};
diff --git a/block/block-backend.c b/block/block-backend.c
index f9cea1b304..effa038924 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1065,16 +1065,16 @@ BlockAIOCB *blk_aio_flush(BlockBackend *blk,
return bdrv_aio_flush(blk_bs(blk), cb, opaque);
}
-BlockAIOCB *blk_aio_discard(BlockBackend *blk,
- int64_t sector_num, int nb_sectors,
- BlockCompletionFunc *cb, void *opaque)
+BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
+ int64_t offset, int count,
+ BlockCompletionFunc *cb, void *opaque)
{
- int ret = blk_check_request(blk, sector_num, nb_sectors);
+ int ret = blk_check_byte_request(blk, offset, count);
if (ret < 0) {
return blk_abort_aio_request(blk, cb, opaque, ret);
}
- return bdrv_aio_discard(blk_bs(blk), sector_num, nb_sectors, cb, opaque);
+ return bdrv_aio_pdiscard(blk_bs(blk), offset, count, cb, opaque);
}
void blk_aio_cancel(BlockAIOCB *acb)
@@ -1106,14 +1106,14 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
return bdrv_aio_ioctl(blk_bs(blk), req, buf, cb, opaque);
}
-int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors)
+int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int count)
{
- int ret = blk_check_request(blk, sector_num, nb_sectors);
+ int ret = blk_check_byte_request(blk, offset, count);
if (ret < 0) {
return ret;
}
- return bdrv_co_discard(blk_bs(blk), sector_num, nb_sectors);
+ return bdrv_co_pdiscard(blk_bs(blk), offset, count);
}
int blk_co_flush(BlockBackend *blk)
@@ -1504,14 +1504,14 @@ int blk_truncate(BlockBackend *blk, int64_t offset)
return bdrv_truncate(blk_bs(blk), offset);
}
-int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors)
+int blk_pdiscard(BlockBackend *blk, int64_t offset, int count)
{
- int ret = blk_check_request(blk, sector_num, nb_sectors);
+ int ret = blk_check_byte_request(blk, offset, count);
if (ret < 0) {
return ret;
}
- return bdrv_discard(blk_bs(blk), sector_num, nb_sectors);
+ return bdrv_pdiscard(blk_bs(blk), offset, count);
}
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index 4902ca557f..f2bfdcfdea 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -326,14 +326,14 @@ void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
}
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
- int64_t cur_sector, int nr_sectors)
+ int64_t cur_sector, int64_t nr_sectors)
{
assert(bdrv_dirty_bitmap_enabled(bitmap));
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
}
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
- int64_t cur_sector, int nr_sectors)
+ int64_t cur_sector, int64_t nr_sectors)
{
assert(bdrv_dirty_bitmap_enabled(bitmap));
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
@@ -361,7 +361,7 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
}
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
- int nr_sectors)
+ int64_t nr_sectors)
{
BdrvDirtyBitmap *bitmap;
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
diff --git a/block/gluster.c b/block/gluster.c
index 406c1e6357..01b479fbb9 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -11,7 +11,27 @@
#include <glusterfs/api/glfs.h>
#include "block/block_int.h"
#include "qapi/error.h"
+#include "qapi/qmp/qerror.h"
#include "qemu/uri.h"
+#include "qemu/error-report.h"
+
+#define GLUSTER_OPT_FILENAME "filename"
+#define GLUSTER_OPT_VOLUME "volume"
+#define GLUSTER_OPT_PATH "path"
+#define GLUSTER_OPT_TYPE "type"
+#define GLUSTER_OPT_SERVER_PATTERN "server."
+#define GLUSTER_OPT_HOST "host"
+#define GLUSTER_OPT_PORT "port"
+#define GLUSTER_OPT_TO "to"
+#define GLUSTER_OPT_IPV4 "ipv4"
+#define GLUSTER_OPT_IPV6 "ipv6"
+#define GLUSTER_OPT_SOCKET "socket"
+#define GLUSTER_OPT_DEBUG "debug"
+#define GLUSTER_DEFAULT_PORT 24007
+#define GLUSTER_DEBUG_DEFAULT 4
+#define GLUSTER_DEBUG_MAX 9
+
+#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
typedef struct GlusterAIOCB {
int64_t size;
@@ -28,27 +48,141 @@ typedef struct BDRVGlusterState {
int debug_level;
} BDRVGlusterState;
-typedef struct GlusterConf {
- char *server;
- int port;
- char *volname;
- char *image;
- char *transport;
- int debug_level;
-} GlusterConf;
+typedef struct BDRVGlusterReopenState {
+ struct glfs *glfs;
+ struct glfs_fd *fd;
+} BDRVGlusterReopenState;
-static void qemu_gluster_gconf_free(GlusterConf *gconf)
-{
- if (gconf) {
- g_free(gconf->server);
- g_free(gconf->volname);
- g_free(gconf->image);
- g_free(gconf->transport);
- g_free(gconf);
+
+static QemuOptsList qemu_gluster_create_opts = {
+ .name = "qemu-gluster-create-opts",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head),
+ .desc = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_PREALLOC,
+ .type = QEMU_OPT_STRING,
+ .help = "Preallocation mode (allowed values: off, full)"
+ },
+ {
+ .name = GLUSTER_OPT_DEBUG,
+ .type = QEMU_OPT_NUMBER,
+ .help = "Gluster log level, valid range is 0-9",
+ },
+ { /* end of list */ }
}
-}
+};
+
+static QemuOptsList runtime_opts = {
+ .name = "gluster",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = GLUSTER_OPT_FILENAME,
+ .type = QEMU_OPT_STRING,
+ .help = "URL to the gluster image",
+ },
+ {
+ .name = GLUSTER_OPT_DEBUG,
+ .type = QEMU_OPT_NUMBER,
+ .help = "Gluster log level, valid range is 0-9",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList runtime_json_opts = {
+ .name = "gluster_json",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_json_opts.head),
+ .desc = {
+ {
+ .name = GLUSTER_OPT_VOLUME,
+ .type = QEMU_OPT_STRING,
+ .help = "name of gluster volume where VM image resides",
+ },
+ {
+ .name = GLUSTER_OPT_PATH,
+ .type = QEMU_OPT_STRING,
+ .help = "absolute path to image file in gluster volume",
+ },
+ {
+ .name = GLUSTER_OPT_DEBUG,
+ .type = QEMU_OPT_NUMBER,
+ .help = "Gluster log level, valid range is 0-9",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList runtime_type_opts = {
+ .name = "gluster_type",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_type_opts.head),
+ .desc = {
+ {
+ .name = GLUSTER_OPT_TYPE,
+ .type = QEMU_OPT_STRING,
+ .help = "tcp|unix",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList runtime_unix_opts = {
+ .name = "gluster_unix",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_unix_opts.head),
+ .desc = {
+ {
+ .name = GLUSTER_OPT_SOCKET,
+ .type = QEMU_OPT_STRING,
+ .help = "socket file path)",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList runtime_tcp_opts = {
+ .name = "gluster_tcp",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
+ .desc = {
+ {
+ .name = GLUSTER_OPT_TYPE,
+ .type = QEMU_OPT_STRING,
+ .help = "tcp|unix",
+ },
+ {
+ .name = GLUSTER_OPT_HOST,
+ .type = QEMU_OPT_STRING,
+ .help = "host address (hostname/ipv4/ipv6 addresses)",
+ },
+ {
+ .name = GLUSTER_OPT_PORT,
+ .type = QEMU_OPT_NUMBER,
+ .help = "port number on which glusterd is listening (default 24007)",
+ },
+ {
+ .name = "to",
+ .type = QEMU_OPT_NUMBER,
+ .help = "max port number, not supported by gluster",
+ },
+ {
+ .name = "ipv4",
+ .type = QEMU_OPT_BOOL,
+ .help = "ipv4 bool value, not supported by gluster",
+ },
+ {
+ .name = "ipv6",
+ .type = QEMU_OPT_BOOL,
+ .help = "ipv6 bool value, not supported by gluster",
+ },
+ { /* end of list */ }
+ },
+};
-static int parse_volume_options(GlusterConf *gconf, char *path)
+static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
{
char *p, *q;
@@ -62,31 +196,29 @@ static int parse_volume_options(GlusterConf *gconf, char *path)
if (*p == '\0') {
return -EINVAL;
}
- gconf->volname = g_strndup(q, p - q);
+ gconf->volume = g_strndup(q, p - q);
- /* image */
+ /* path */
p += strspn(p, "/");
if (*p == '\0') {
return -EINVAL;
}
- gconf->image = g_strdup(p);
+ gconf->path = g_strdup(p);
return 0;
}
/*
- * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...]
+ * file=gluster[+transport]://[host[:port]]/volume/path[?socket=...]
*
* 'gluster' is the protocol.
*
* 'transport' specifies the transport type used to connect to gluster
* management daemon (glusterd). Valid transport types are
- * tcp, unix and rdma. If a transport type isn't specified, then tcp
- * type is assumed.
+ * tcp or unix. If a transport type isn't specified, then tcp type is assumed.
*
- * 'server' specifies the server where the volume file specification for
- * the given volume resides. This can be either hostname, ipv4 address
- * or ipv6 address. ipv6 address needs to be within square brackets [ ].
- * If transport type is 'unix', then 'server' field should not be specified.
+ * 'host' specifies the host where the volume file specification for
+ * the given volume resides. This can be either hostname or ipv4 address.
+ * If transport type is 'unix', then 'host' field should not be specified.
* The 'socket' field needs to be populated with the path to unix domain
* socket.
*
@@ -95,23 +227,22 @@ static int parse_volume_options(GlusterConf *gconf, char *path)
* default port. If the transport type is unix, then 'port' should not be
* specified.
*
- * 'volname' is the name of the gluster volume which contains the VM image.
+ * 'volume' is the name of the gluster volume which contains the VM image.
*
- * 'image' is the path to the actual VM image that resides on gluster volume.
+ * 'path' is the path to the actual VM image that resides on gluster volume.
*
* Examples:
*
* file=gluster://1.2.3.4/testvol/a.img
* file=gluster+tcp://1.2.3.4/testvol/a.img
* file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img
- * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img
- * file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img
- * file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img
+ * file=gluster+tcp://host.domain.com:24007/testvol/dir/a.img
* file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket
- * file=gluster+rdma://1.2.3.4:24007/testvol/a.img
*/
-static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
+static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
+ const char *filename)
{
+ GlusterServer *gsconf;
URI *uri;
QueryParams *qp = NULL;
bool is_unix = false;
@@ -122,16 +253,21 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
return -EINVAL;
}
+ gconf->server = g_new0(GlusterServerList, 1);
+ gconf->server->value = gsconf = g_new0(GlusterServer, 1);
+
/* transport */
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
- gconf->transport = g_strdup("tcp");
+ gsconf->type = GLUSTER_TRANSPORT_TCP;
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
- gconf->transport = g_strdup("tcp");
+ gsconf->type = GLUSTER_TRANSPORT_TCP;
} else if (!strcmp(uri->scheme, "gluster+unix")) {
- gconf->transport = g_strdup("unix");
+ gsconf->type = GLUSTER_TRANSPORT_UNIX;
is_unix = true;
} else if (!strcmp(uri->scheme, "gluster+rdma")) {
- gconf->transport = g_strdup("rdma");
+ gsconf->type = GLUSTER_TRANSPORT_TCP;
+ error_report("Warning: rdma feature is not supported, falling "
+ "back to tcp");
} else {
ret = -EINVAL;
goto out;
@@ -157,10 +293,14 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
ret = -EINVAL;
goto out;
}
- gconf->server = g_strdup(qp->p[0].value);
+ gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
} else {
- gconf->server = g_strdup(uri->server ? uri->server : "localhost");
- gconf->port = uri->port;
+ gsconf->u.tcp.host = g_strdup(uri->server ? uri->server : "localhost");
+ if (uri->port) {
+ gsconf->u.tcp.port = g_strdup_printf("%d", uri->port);
+ } else {
+ gsconf->u.tcp.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
+ }
}
out:
@@ -171,30 +311,34 @@ out:
return ret;
}
-static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
- Error **errp)
+static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
+ Error **errp)
{
- struct glfs *glfs = NULL;
+ struct glfs *glfs;
int ret;
int old_errno;
+ GlusterServerList *server;
- ret = qemu_gluster_parseuri(gconf, filename);
- if (ret < 0) {
- error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/"
- "volname/image[?socket=...]");
- errno = -ret;
- goto out;
- }
-
- glfs = glfs_new(gconf->volname);
+ glfs = glfs_new(gconf->volume);
if (!glfs) {
goto out;
}
- ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->server,
- gconf->port);
- if (ret < 0) {
- goto out;
+ for (server = gconf->server; server; server = server->next) {
+ if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
+ ret = glfs_set_volfile_server(glfs,
+ GlusterTransport_lookup[server->value->type],
+ server->value->u.q_unix.path, 0);
+ } else {
+ ret = glfs_set_volfile_server(glfs,
+ GlusterTransport_lookup[server->value->type],
+ server->value->u.tcp.host,
+ atoi(server->value->u.tcp.port));
+ }
+
+ if (ret < 0) {
+ goto out;
+ }
}
ret = glfs_set_logging(glfs, "-", gconf->debug_level);
@@ -204,15 +348,25 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
ret = glfs_init(glfs);
if (ret) {
- error_setg_errno(errp, errno,
- "Gluster connection failed for server=%s port=%d "
- "volume=%s image=%s transport=%s", gconf->server,
- gconf->port, gconf->volname, gconf->image,
- gconf->transport);
+ error_setg(errp, "Gluster connection for volume %s, path %s failed"
+ " to connect", gconf->volume, gconf->path);
+ for (server = gconf->server; server; server = server->next) {
+ if (server->value->type == GLUSTER_TRANSPORT_UNIX) {
+ error_append_hint(errp, "hint: failed on socket %s ",
+ server->value->u.q_unix.path);
+ } else {
+ error_append_hint(errp, "hint: failed on host %s and port %s ",
+ server->value->u.tcp.host,
+ server->value->u.tcp.port);
+ }
+ }
+
+ error_append_hint(errp, "Please refer to gluster logs for more info\n");
/* glfs_init sometimes doesn't set errno although docs suggest that */
- if (errno == 0)
+ if (errno == 0) {
errno = EINVAL;
+ }
goto out;
}
@@ -227,6 +381,226 @@ out:
return NULL;
}
+static int qapi_enum_parse(const char *opt)
+{
+ int i;
+
+ if (!opt) {
+ return GLUSTER_TRANSPORT__MAX;
+ }
+
+ for (i = 0; i < GLUSTER_TRANSPORT__MAX; i++) {
+ if (!strcmp(opt, GlusterTransport_lookup[i])) {
+ return i;
+ }
+ }
+
+ return i;
+}
+
+/*
+ * Convert the json formatted command line into qapi.
+*/
+static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
+ QDict *options, Error **errp)
+{
+ QemuOpts *opts;
+ GlusterServer *gsconf;
+ GlusterServerList *curr = NULL;
+ QDict *backing_options = NULL;
+ Error *local_err = NULL;
+ char *str = NULL;
+ const char *ptr;
+ size_t num_servers;
+ int i;
+
+ /* create opts info from runtime_json_opts list */
+ opts = qemu_opts_create(&runtime_json_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ num_servers = qdict_array_entries(options, GLUSTER_OPT_SERVER_PATTERN);
+ if (num_servers < 1) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER, "server");
+ goto out;
+ }
+
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_VOLUME);
+ if (!ptr) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_VOLUME);
+ goto out;
+ }
+ gconf->volume = g_strdup(ptr);
+
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH);
+ if (!ptr) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_PATH);
+ goto out;
+ }
+ gconf->path = g_strdup(ptr);
+ qemu_opts_del(opts);
+
+ for (i = 0; i < num_servers; i++) {
+ str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", i);
+ qdict_extract_subqdict(options, &backing_options, str);
+
+ /* create opts info from runtime_type_opts list */
+ opts = qemu_opts_create(&runtime_type_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, backing_options, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_TYPE);
+ gsconf = g_new0(GlusterServer, 1);
+ gsconf->type = qapi_enum_parse(ptr);
+ if (!ptr) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER, GLUSTER_OPT_TYPE);
+ error_append_hint(&local_err, GERR_INDEX_HINT, i);
+ goto out;
+
+ }
+ if (gsconf->type == GLUSTER_TRANSPORT__MAX) {
+ error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE,
+ GLUSTER_OPT_TYPE, "tcp or unix");
+ error_append_hint(&local_err, GERR_INDEX_HINT, i);
+ goto out;
+ }
+ qemu_opts_del(opts);
+
+ if (gsconf->type == GLUSTER_TRANSPORT_TCP) {
+ /* create opts info from runtime_tcp_opts list */
+ opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, backing_options, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_HOST);
+ if (!ptr) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER,
+ GLUSTER_OPT_HOST);
+ error_append_hint(&local_err, GERR_INDEX_HINT, i);
+ goto out;
+ }
+ gsconf->u.tcp.host = g_strdup(ptr);
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_PORT);
+ if (!ptr) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER,
+ GLUSTER_OPT_PORT);
+ error_append_hint(&local_err, GERR_INDEX_HINT, i);
+ goto out;
+ }
+ gsconf->u.tcp.port = g_strdup(ptr);
+
+ /* defend for unsupported fields in InetSocketAddress,
+ * i.e. @ipv4, @ipv6 and @to
+ */
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_TO);
+ if (ptr) {
+ gsconf->u.tcp.has_to = true;
+ }
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV4);
+ if (ptr) {
+ gsconf->u.tcp.has_ipv4 = true;
+ }
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_IPV6);
+ if (ptr) {
+ gsconf->u.tcp.has_ipv6 = true;
+ }
+ if (gsconf->u.tcp.has_to) {
+ error_setg(&local_err, "Parameter 'to' not supported");
+ goto out;
+ }
+ if (gsconf->u.tcp.has_ipv4 || gsconf->u.tcp.has_ipv6) {
+ error_setg(&local_err, "Parameters 'ipv4/ipv6' not supported");
+ goto out;
+ }
+ qemu_opts_del(opts);
+ } else {
+ /* create opts info from runtime_unix_opts list */
+ opts = qemu_opts_create(&runtime_unix_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, backing_options, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET);
+ if (!ptr) {
+ error_setg(&local_err, QERR_MISSING_PARAMETER,
+ GLUSTER_OPT_SOCKET);
+ error_append_hint(&local_err, GERR_INDEX_HINT, i);
+ goto out;
+ }
+ gsconf->u.q_unix.path = g_strdup(ptr);
+ qemu_opts_del(opts);
+ }
+
+ if (gconf->server == NULL) {
+ gconf->server = g_new0(GlusterServerList, 1);
+ gconf->server->value = gsconf;
+ curr = gconf->server;
+ } else {
+ curr->next = g_new0(GlusterServerList, 1);
+ curr->next->value = gsconf;
+ curr = curr->next;
+ }
+
+ qdict_del(backing_options, str);
+ g_free(str);
+ str = NULL;
+ }
+
+ return 0;
+
+out:
+ error_propagate(errp, local_err);
+ qemu_opts_del(opts);
+ if (str) {
+ qdict_del(backing_options, str);
+ g_free(str);
+ }
+ errno = EINVAL;
+ return -errno;
+}
+
+static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
+ const char *filename,
+ QDict *options, Error **errp)
+{
+ int ret;
+ if (filename) {
+ ret = qemu_gluster_parse_uri(gconf, filename);
+ if (ret < 0) {
+ error_setg(errp, "invalid URI");
+ error_append_hint(errp, "Usage: file=gluster[+transport]://"
+ "[host[:port]]/volume/path[?socket=...]\n");
+ errno = -ret;
+ return NULL;
+ }
+ } else {
+ ret = qemu_gluster_parse_json(gconf, options, errp);
+ if (ret < 0) {
+ error_append_hint(errp, "Usage: "
+ "-drive driver=qcow2,file.driver=gluster,"
+ "file.volume=testvol,file.path=/path/a.qcow2"
+ "[,file.debug=9],file.server.0.type=tcp,"
+ "file.server.0.host=1.2.3.4,"
+ "file.server.0.port=24007,"
+ "file.server.1.transport=unix,"
+ "file.server.1.socket=/var/run/glusterd.socket ..."
+ "\n");
+ errno = -ret;
+ return NULL;
+ }
+
+ }
+
+ return qemu_gluster_glfs_init(gconf, errp);
+}
+
static void qemu_gluster_complete_aio(void *opaque)
{
GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
@@ -255,30 +629,6 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
qemu_bh_schedule(acb->bh);
}
-#define GLUSTER_OPT_FILENAME "filename"
-#define GLUSTER_OPT_DEBUG "debug"
-#define GLUSTER_DEBUG_DEFAULT 4
-#define GLUSTER_DEBUG_MAX 9
-
-/* TODO Convert to fine grained options */
-static QemuOptsList runtime_opts = {
- .name = "gluster",
- .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
- .desc = {
- {
- .name = GLUSTER_OPT_FILENAME,
- .type = QEMU_OPT_STRING,
- .help = "URL to the gluster image",
- },
- {
- .name = GLUSTER_OPT_DEBUG,
- .type = QEMU_OPT_NUMBER,
- .help = "Gluster log level, valid range is 0-9",
- },
- { /* end of list */ }
- },
-};
-
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
{
assert(open_flags != NULL);
@@ -324,7 +674,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
BDRVGlusterState *s = bs->opaque;
int open_flags = 0;
int ret = 0;
- GlusterConf *gconf = g_new0(GlusterConf, 1);
+ BlockdevOptionsGluster *gconf = NULL;
QemuOpts *opts;
Error *local_err = NULL;
const char *filename;
@@ -347,8 +697,10 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
s->debug_level = GLUSTER_DEBUG_MAX;
}
+ gconf = g_new0(BlockdevOptionsGluster, 1);
gconf->debug_level = s->debug_level;
- s->glfs = qemu_gluster_init(gconf, filename, errp);
+ gconf->has_debug_level = true;
+ s->glfs = qemu_gluster_init(gconf, filename, options, errp);
if (!s->glfs) {
ret = -errno;
goto out;
@@ -373,7 +725,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
- s->fd = glfs_open(s->glfs, gconf->image, open_flags);
+ s->fd = glfs_open(s->glfs, gconf->path, open_flags);
if (!s->fd) {
ret = -errno;
}
@@ -382,7 +734,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
out:
qemu_opts_del(opts);
- qemu_gluster_gconf_free(gconf);
+ qapi_free_BlockdevOptionsGluster(gconf);
if (!ret) {
return ret;
}
@@ -395,19 +747,13 @@ out:
return ret;
}
-typedef struct BDRVGlusterReopenState {
- struct glfs *glfs;
- struct glfs_fd *fd;
-} BDRVGlusterReopenState;
-
-
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
int ret = 0;
BDRVGlusterState *s;
BDRVGlusterReopenState *reop_s;
- GlusterConf *gconf = NULL;
+ BlockdevOptionsGluster *gconf;
int open_flags = 0;
assert(state != NULL);
@@ -420,10 +766,10 @@ static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
qemu_gluster_parse_flags(state->flags, &open_flags);
- gconf = g_new0(GlusterConf, 1);
-
+ gconf = g_new0(BlockdevOptionsGluster, 1);
gconf->debug_level = s->debug_level;
- reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, errp);
+ gconf->has_debug_level = true;
+ reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, NULL, errp);
if (reop_s->glfs == NULL) {
ret = -errno;
goto exit;
@@ -439,7 +785,7 @@ static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
}
#endif
- reop_s->fd = glfs_open(reop_s->glfs, gconf->image, open_flags);
+ reop_s->fd = glfs_open(reop_s->glfs, gconf->path, open_flags);
if (reop_s->fd == NULL) {
/* reops->glfs will be cleaned up in _abort */
ret = -errno;
@@ -448,7 +794,7 @@ static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
exit:
/* state->opaque will be freed in either the _abort or _commit */
- qemu_gluster_gconf_free(gconf);
+ qapi_free_BlockdevOptionsGluster(gconf);
return ret;
}
@@ -501,7 +847,9 @@ static void qemu_gluster_reopen_abort(BDRVReopenState *state)
#ifdef CONFIG_GLUSTERFS_ZEROFILL
static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
- int64_t offset, int size, BdrvRequestFlags flags)
+ int64_t offset,
+ int size,
+ BdrvRequestFlags flags)
{
int ret;
GlusterAIOCB acb;
@@ -527,7 +875,7 @@ static inline bool gluster_supports_zerofill(void)
}
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
- int64_t size)
+ int64_t size)
{
return glfs_zerofill(fd, offset, size);
}
@@ -539,7 +887,7 @@ static inline bool gluster_supports_zerofill(void)
}
static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
- int64_t size)
+ int64_t size)
{
return 0;
}
@@ -548,14 +896,15 @@ static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
static int qemu_gluster_create(const char *filename,
QemuOpts *opts, Error **errp)
{
+ BlockdevOptionsGluster *gconf;
struct glfs *glfs;
struct glfs_fd *fd;
int ret = 0;
int prealloc = 0;
int64_t total_size = 0;
char *tmp = NULL;
- GlusterConf *gconf = g_new0(GlusterConf, 1);
+ gconf = g_new0(BlockdevOptionsGluster, 1);
gconf->debug_level = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
GLUSTER_DEBUG_DEFAULT);
if (gconf->debug_level < 0) {
@@ -563,8 +912,9 @@ static int qemu_gluster_create(const char *filename,
} else if (gconf->debug_level > GLUSTER_DEBUG_MAX) {
gconf->debug_level = GLUSTER_DEBUG_MAX;
}
+ gconf->has_debug_level = true;
- glfs = qemu_gluster_init(gconf, filename, errp);
+ glfs = qemu_gluster_init(gconf, filename, NULL, errp);
if (!glfs) {
ret = -errno;
goto out;
@@ -576,19 +926,17 @@ static int qemu_gluster_create(const char *filename,
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
if (!tmp || !strcmp(tmp, "off")) {
prealloc = 0;
- } else if (!strcmp(tmp, "full") &&
- gluster_supports_zerofill()) {
+ } else if (!strcmp(tmp, "full") && gluster_supports_zerofill()) {
prealloc = 1;
} else {
error_setg(errp, "Invalid preallocation mode: '%s'"
- " or GlusterFS doesn't support zerofill API",
- tmp);
+ " or GlusterFS doesn't support zerofill API", tmp);
ret = -EINVAL;
goto out;
}
- fd = glfs_creat(glfs, gconf->image,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
+ fd = glfs_creat(glfs, gconf->path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
if (!fd) {
ret = -errno;
} else {
@@ -606,7 +954,7 @@ static int qemu_gluster_create(const char *filename,
}
out:
g_free(tmp);
- qemu_gluster_gconf_free(gconf);
+ qapi_free_BlockdevOptionsGluster(gconf);
if (glfs) {
glfs_fini(glfs);
}
@@ -614,7 +962,8 @@ out:
}
static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write)
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *qiov, int write)
{
int ret;
GlusterAIOCB acb;
@@ -629,10 +978,10 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
if (write) {
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
- gluster_finish_aiocb, &acb);
+ gluster_finish_aiocb, &acb);
} else {
ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
- gluster_finish_aiocb, &acb);
+ gluster_finish_aiocb, &acb);
}
if (ret < 0) {
@@ -657,13 +1006,17 @@ static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
}
static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
+ int64_t sector_num,
+ int nb_sectors,
+ QEMUIOVector *qiov)
{
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0);
}
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
+ int64_t sector_num,
+ int nb_sectors,
+ QEMUIOVector *qiov)
{
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
}
@@ -724,14 +1077,12 @@ error:
}
#ifdef CONFIG_GLUSTERFS_DISCARD
-static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors)
+static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int size)
{
int ret;
GlusterAIOCB acb;
BDRVGlusterState *s = bs->opaque;
- size_t size = nb_sectors * BDRV_SECTOR_SIZE;
- off_t offset = sector_num * BDRV_SECTOR_SIZE;
acb.size = 0;
acb.ret = 0;
@@ -934,34 +1285,11 @@ static int64_t coroutine_fn qemu_gluster_co_get_block_status(
}
-static QemuOptsList qemu_gluster_create_opts = {
- .name = "qemu-gluster-create-opts",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_gluster_create_opts.head),
- .desc = {
- {
- .name = BLOCK_OPT_SIZE,
- .type = QEMU_OPT_SIZE,
- .help = "Virtual disk size"
- },
- {
- .name = BLOCK_OPT_PREALLOC,
- .type = QEMU_OPT_STRING,
- .help = "Preallocation mode (allowed values: off, full)"
- },
- {
- .name = GLUSTER_OPT_DEBUG,
- .type = QEMU_OPT_NUMBER,
- .help = "Gluster log level, valid range is 0-9",
- },
- { /* end of list */ }
- }
-};
-
static BlockDriver bdrv_gluster = {
.format_name = "gluster",
.protocol_name = "gluster",
.instance_size = sizeof(BDRVGlusterState),
- .bdrv_needs_filename = true,
+ .bdrv_needs_filename = false,
.bdrv_file_open = qemu_gluster_open,
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
@@ -976,7 +1304,7 @@ static BlockDriver bdrv_gluster = {
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_co_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
@@ -989,7 +1317,7 @@ static BlockDriver bdrv_gluster_tcp = {
.format_name = "gluster",
.protocol_name = "gluster+tcp",
.instance_size = sizeof(BDRVGlusterState),
- .bdrv_needs_filename = true,
+ .bdrv_needs_filename = false,
.bdrv_file_open = qemu_gluster_open,
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
@@ -1004,7 +1332,7 @@ static BlockDriver bdrv_gluster_tcp = {
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_co_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
@@ -1032,7 +1360,7 @@ static BlockDriver bdrv_gluster_unix = {
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_co_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
@@ -1041,6 +1369,12 @@ static BlockDriver bdrv_gluster_unix = {
.create_opts = &qemu_gluster_create_opts,
};
+/* rdma is deprecated (actually never supported for volfile fetch).
+ * Let's maintain it for the protocol compatibility, to make sure things
+ * won't break immediately. For now, gluster+rdma will fall back to gluster+tcp
+ * protocol with a warning.
+ * TODO: remove gluster+rdma interface support
+ */
static BlockDriver bdrv_gluster_rdma = {
.format_name = "gluster",
.protocol_name = "gluster+rdma",
@@ -1060,7 +1394,7 @@ static BlockDriver bdrv_gluster_rdma = {
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_co_discard = qemu_gluster_co_discard,
+ .bdrv_co_pdiscard = qemu_gluster_co_pdiscard,
#endif
#ifdef CONFIG_GLUSTERFS_ZEROFILL
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
diff --git a/block/io.c b/block/io.c
index cfda7148d8..7323f0fb7b 100644
--- a/block/io.c
+++ b/block/io.c
@@ -33,14 +33,13 @@
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
-static BlockAIOCB *bdrv_co_aio_rw_vector(BdrvChild *child,
- int64_t sector_num,
- QEMUIOVector *qiov,
- int nb_sectors,
- BdrvRequestFlags flags,
- BlockCompletionFunc *cb,
- void *opaque,
- bool is_write);
+static BlockAIOCB *bdrv_co_aio_prw_vector(BdrvChild *child,
+ int64_t offset,
+ QEMUIOVector *qiov,
+ BdrvRequestFlags flags,
+ BlockCompletionFunc *cb,
+ void *opaque,
+ bool is_write);
static void coroutine_fn bdrv_co_do_rw(void *opaque);
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int count, BdrvRequestFlags flags);
@@ -971,21 +970,25 @@ err:
/*
* Forwards an already correctly aligned request to the BlockDriver. This
- * handles copy on read and zeroing after EOF; any other features must be
- * implemented by the caller.
+ * handles copy on read, zeroing after EOF, and fragmentation of large
+ * reads; any other features must be implemented by the caller.
*/
static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
int64_t align, QEMUIOVector *qiov, int flags)
{
int64_t total_bytes, max_bytes;
- int ret;
+ int ret = 0;
+ uint64_t bytes_remaining = bytes;
+ int max_transfer;
assert(is_power_of_2(align));
assert((offset & (align - 1)) == 0);
assert((bytes & (align - 1)) == 0);
assert(!qiov || bytes == qiov->size);
assert((bs->open_flags & BDRV_O_NO_IO) == 0);
+ max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX),
+ align);
/* TODO: We would need a per-BDS .supported_read_flags and
* potential fallback support, if we ever implement any read flags
@@ -1024,7 +1027,7 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
}
}
- /* Forward the request to the BlockDriver */
+ /* Forward the request to the BlockDriver, possibly fragmenting it */
total_bytes = bdrv_getlength(bs);
if (total_bytes < 0) {
ret = total_bytes;
@@ -1032,30 +1035,39 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
}
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
- if (bytes <= max_bytes) {
+ if (bytes <= max_bytes && bytes <= max_transfer) {
ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0);
- } else if (max_bytes > 0) {
- QEMUIOVector local_qiov;
+ goto out;
+ }
- qemu_iovec_init(&local_qiov, qiov->niov);
- qemu_iovec_concat(&local_qiov, qiov, 0, max_bytes);
+ while (bytes_remaining) {
+ int num;
- ret = bdrv_driver_preadv(bs, offset, max_bytes, &local_qiov, 0);
+ if (max_bytes) {
+ QEMUIOVector local_qiov;
- qemu_iovec_destroy(&local_qiov);
- } else {
- ret = 0;
- }
+ num = MIN(bytes_remaining, MIN(max_bytes, max_transfer));
+ assert(num);
+ qemu_iovec_init(&local_qiov, qiov->niov);
+ qemu_iovec_concat(&local_qiov, qiov, bytes - bytes_remaining, num);
- /* Reading beyond end of file is supposed to produce zeroes */
- if (ret == 0 && total_bytes < offset + bytes) {
- uint64_t zero_offset = MAX(0, total_bytes - offset);
- uint64_t zero_bytes = offset + bytes - zero_offset;
- qemu_iovec_memset(qiov, zero_offset, 0, zero_bytes);
+ ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
+ num, &local_qiov, 0);
+ max_bytes -= num;
+ qemu_iovec_destroy(&local_qiov);
+ } else {
+ num = bytes_remaining;
+ ret = qemu_iovec_memset(qiov, bytes - bytes_remaining, 0,
+ bytes_remaining);
+ }
+ if (ret < 0) {
+ goto out;
+ }
+ bytes_remaining -= num;
}
out:
- return ret;
+ return ret < 0 ? ret : 0;
}
/*
@@ -1256,7 +1268,8 @@ fail:
}
/*
- * Forwards an already correctly aligned write request to the BlockDriver.
+ * Forwards an already correctly aligned write request to the BlockDriver,
+ * after possibly fragmenting it.
*/
static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
@@ -1268,6 +1281,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
int64_t start_sector = offset >> BDRV_SECTOR_BITS;
int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE);
+ uint64_t bytes_remaining = bytes;
+ int max_transfer;
assert(is_power_of_2(align));
assert((offset & (align - 1)) == 0);
@@ -1275,6 +1290,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
assert(!qiov || bytes == qiov->size);
assert((bs->open_flags & BDRV_O_NO_IO) == 0);
assert(!(flags & ~BDRV_REQ_MASK));
+ max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX),
+ align);
waited = wait_serialising_requests(req);
assert(!waited || !req->serialising);
@@ -1297,9 +1314,34 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
} else if (flags & BDRV_REQ_ZERO_WRITE) {
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags);
- } else {
+ } else if (bytes <= max_transfer) {
bdrv_debug_event(bs, BLKDBG_PWRITEV);
ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags);
+ } else {
+ bdrv_debug_event(bs, BLKDBG_PWRITEV);
+ while (bytes_remaining) {
+ int num = MIN(bytes_remaining, max_transfer);
+ QEMUIOVector local_qiov;
+ int local_flags = flags;
+
+ assert(num);
+ if (num < bytes_remaining && (flags & BDRV_REQ_FUA) &&
+ !(bs->supported_write_flags & BDRV_REQ_FUA)) {
+ /* If FUA is going to be emulated by flush, we only
+ * need to flush on the last iteration */
+ local_flags &= ~BDRV_REQ_FUA;
+ }
+ qemu_iovec_init(&local_qiov, qiov->niov);
+ qemu_iovec_concat(&local_qiov, qiov, bytes - bytes_remaining, num);
+
+ ret = bdrv_driver_pwritev(bs, offset + bytes - bytes_remaining,
+ num, &local_qiov, local_flags);
+ qemu_iovec_destroy(&local_qiov);
+ if (ret < 0) {
+ break;
+ }
+ bytes_remaining -= num;
+ }
}
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
@@ -1312,6 +1354,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
if (ret >= 0) {
bs->total_sectors = MAX(bs->total_sectors, end_sector);
+ ret = 0;
}
return ret;
@@ -1971,8 +2014,9 @@ BlockAIOCB *bdrv_aio_readv(BdrvChild *child, int64_t sector_num,
{
trace_bdrv_aio_readv(child->bs, sector_num, nb_sectors, opaque);
- return bdrv_co_aio_rw_vector(child, sector_num, qiov, nb_sectors, 0,
- cb, opaque, false);
+ assert(nb_sectors << BDRV_SECTOR_BITS == qiov->size);
+ return bdrv_co_aio_prw_vector(child, sector_num << BDRV_SECTOR_BITS, qiov,
+ 0, cb, opaque, false);
}
BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num,
@@ -1981,8 +2025,9 @@ BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num,
{
trace_bdrv_aio_writev(child->bs, sector_num, nb_sectors, opaque);
- return bdrv_co_aio_rw_vector(child, sector_num, qiov, nb_sectors, 0,
- cb, opaque, true);
+ assert(nb_sectors << BDRV_SECTOR_BITS == qiov->size);
+ return bdrv_co_aio_prw_vector(child, sector_num << BDRV_SECTOR_BITS, qiov,
+ 0, cb, opaque, true);
}
void bdrv_aio_cancel(BlockAIOCB *acb)
@@ -2018,8 +2063,8 @@ typedef struct BlockRequest {
union {
/* Used during read, write, trim */
struct {
- int64_t sector;
- int nb_sectors;
+ int64_t offset;
+ int bytes;
int flags;
QEMUIOVector *qiov;
};
@@ -2083,24 +2128,23 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque)
BlockAIOCBCoroutine *acb = opaque;
if (!acb->is_write) {
- acb->req.error = bdrv_co_do_readv(acb->child, acb->req.sector,
- acb->req.nb_sectors, acb->req.qiov, acb->req.flags);
+ acb->req.error = bdrv_co_preadv(acb->child, acb->req.offset,
+ acb->req.qiov->size, acb->req.qiov, acb->req.flags);
} else {
- acb->req.error = bdrv_co_do_writev(acb->child, acb->req.sector,
- acb->req.nb_sectors, acb->req.qiov, acb->req.flags);
+ acb->req.error = bdrv_co_pwritev(acb->child, acb->req.offset,
+ acb->req.qiov->size, acb->req.qiov, acb->req.flags);
}
bdrv_co_complete(acb);
}
-static BlockAIOCB *bdrv_co_aio_rw_vector(BdrvChild *child,
- int64_t sector_num,
- QEMUIOVector *qiov,
- int nb_sectors,
- BdrvRequestFlags flags,
- BlockCompletionFunc *cb,
- void *opaque,
- bool is_write)
+static BlockAIOCB *bdrv_co_aio_prw_vector(BdrvChild *child,
+ int64_t offset,
+ QEMUIOVector *qiov,
+ BdrvRequestFlags flags,
+ BlockCompletionFunc *cb,
+ void *opaque,
+ bool is_write)
{
Coroutine *co;
BlockAIOCBCoroutine *acb;
@@ -2109,8 +2153,7 @@ static BlockAIOCB *bdrv_co_aio_rw_vector(BdrvChild *child,
acb->child = child;
acb->need_bh = true;
acb->req.error = -EINPROGRESS;
- acb->req.sector = sector_num;
- acb->req.nb_sectors = nb_sectors;
+ acb->req.offset = offset;
acb->req.qiov = qiov;
acb->req.flags = flags;
acb->is_write = is_write;
@@ -2150,30 +2193,29 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
return &acb->common;
}
-static void coroutine_fn bdrv_aio_discard_co_entry(void *opaque)
+static void coroutine_fn bdrv_aio_pdiscard_co_entry(void *opaque)
{
BlockAIOCBCoroutine *acb = opaque;
BlockDriverState *bs = acb->common.bs;
- acb->req.error = bdrv_co_discard(bs, acb->req.sector, acb->req.nb_sectors);
+ acb->req.error = bdrv_co_pdiscard(bs, acb->req.offset, acb->req.bytes);
bdrv_co_complete(acb);
}
-BlockAIOCB *bdrv_aio_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors,
- BlockCompletionFunc *cb, void *opaque)
+BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs, int64_t offset, int count,
+ BlockCompletionFunc *cb, void *opaque)
{
Coroutine *co;
BlockAIOCBCoroutine *acb;
- trace_bdrv_aio_discard(bs, sector_num, nb_sectors, opaque);
+ trace_bdrv_aio_pdiscard(bs, offset, count, opaque);
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
acb->need_bh = true;
acb->req.error = -EINPROGRESS;
- acb->req.sector = sector_num;
- acb->req.nb_sectors = nb_sectors;
- co = qemu_coroutine_create(bdrv_aio_discard_co_entry, acb);
+ acb->req.offset = offset;
+ acb->req.bytes = count;
+ co = qemu_coroutine_create(bdrv_aio_pdiscard_co_entry, acb);
qemu_coroutine_enter(co);
bdrv_co_maybe_schedule_bh(acb);
@@ -2346,28 +2388,29 @@ int bdrv_flush(BlockDriverState *bs)
typedef struct DiscardCo {
BlockDriverState *bs;
- int64_t sector_num;
- int nb_sectors;
+ int64_t offset;
+ int count;
int ret;
} DiscardCo;
-static void coroutine_fn bdrv_discard_co_entry(void *opaque)
+static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque)
{
DiscardCo *rwco = opaque;
- rwco->ret = bdrv_co_discard(rwco->bs, rwco->sector_num, rwco->nb_sectors);
+ rwco->ret = bdrv_co_pdiscard(rwco->bs, rwco->offset, rwco->count);
}
-int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
+int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset,
+ int count)
{
BdrvTrackedRequest req;
- int max_discard, ret;
+ int max_pdiscard, ret;
+ int head, align;
if (!bs->drv) {
return -ENOMEDIUM;
}
- ret = bdrv_check_request(bs, sector_num, nb_sectors);
+ ret = bdrv_check_byte_request(bs, offset, count);
if (ret < 0) {
return ret;
} else if (bs->read_only) {
@@ -2380,50 +2423,47 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
return 0;
}
- if (!bs->drv->bdrv_co_discard && !bs->drv->bdrv_aio_discard) {
+ if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) {
+ return 0;
+ }
+
+ /* Discard is advisory, so ignore any unaligned head or tail */
+ align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment);
+ assert(is_power_of_2(align));
+ head = MIN(count, -offset & (align - 1));
+ if (head) {
+ count -= head;
+ offset += head;
+ }
+ count = QEMU_ALIGN_DOWN(count, align);
+ if (!count) {
return 0;
}
- tracked_request_begin(&req, bs, sector_num << BDRV_SECTOR_BITS,
- nb_sectors << BDRV_SECTOR_BITS, BDRV_TRACKED_DISCARD);
+ tracked_request_begin(&req, bs, offset, count, BDRV_TRACKED_DISCARD);
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
if (ret < 0) {
goto out;
}
- max_discard = MIN_NON_ZERO(bs->bl.max_pdiscard >> BDRV_SECTOR_BITS,
- BDRV_REQUEST_MAX_SECTORS);
- while (nb_sectors > 0) {
- int ret;
- int num = nb_sectors;
- int discard_alignment = bs->bl.pdiscard_alignment >> BDRV_SECTOR_BITS;
-
- /* align request */
- if (discard_alignment &&
- num >= discard_alignment &&
- sector_num % discard_alignment) {
- if (num > discard_alignment) {
- num = discard_alignment;
- }
- num -= sector_num % discard_alignment;
- }
+ max_pdiscard = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_pdiscard, INT_MAX),
+ align);
- /* limit request size */
- if (num > max_discard) {
- num = max_discard;
- }
+ while (count > 0) {
+ int ret;
+ int num = MIN(count, max_pdiscard);
- if (bs->drv->bdrv_co_discard) {
- ret = bs->drv->bdrv_co_discard(bs, sector_num, num);
+ if (bs->drv->bdrv_co_pdiscard) {
+ ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
} else {
BlockAIOCB *acb;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
- acb = bs->drv->bdrv_aio_discard(bs, sector_num, nb_sectors,
- bdrv_co_io_em_complete, &co);
+ acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num,
+ bdrv_co_io_em_complete, &co);
if (acb == NULL) {
ret = -EIO;
goto out;
@@ -2436,8 +2476,8 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
goto out;
}
- sector_num += num;
- nb_sectors -= num;
+ offset += num;
+ count -= num;
}
ret = 0;
out:
@@ -2448,23 +2488,23 @@ out:
return ret;
}
-int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count)
{
Coroutine *co;
DiscardCo rwco = {
.bs = bs,
- .sector_num = sector_num,
- .nb_sectors = nb_sectors,
+ .offset = offset,
+ .count = count,
.ret = NOT_DONE,
};
if (qemu_in_coroutine()) {
/* Fast-path if already in coroutine context */
- bdrv_discard_co_entry(&rwco);
+ bdrv_pdiscard_co_entry(&rwco);
} else {
AioContext *aio_context = bdrv_get_aio_context(bs);
- co = qemu_coroutine_create(bdrv_discard_co_entry, &rwco);
+ co = qemu_coroutine_create(bdrv_pdiscard_co_entry, &rwco);
qemu_coroutine_enter(co);
while (rwco.ret == NOT_DONE) {
aio_poll(aio_context, true);
diff --git a/block/iscsi.c b/block/iscsi.c
index 129c3afa68..95ce9e139e 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -586,11 +586,8 @@ iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
return -EINVAL;
}
- if (bs->bl.max_transfer &&
- nb_sectors << BDRV_SECTOR_BITS > bs->bl.max_transfer) {
- error_report("iSCSI Error: Write of %d sectors exceeds max_xfer_len "
- "of %" PRIu32 " bytes", nb_sectors, bs->bl.max_transfer);
- return -EINVAL;
+ if (bs->bl.max_transfer) {
+ assert(nb_sectors << BDRV_SECTOR_BITS <= bs->bl.max_transfer);
}
lba = sector_qemu2lun(sector_num, iscsilun);
@@ -754,11 +751,8 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
return -EINVAL;
}
- if (bs->bl.max_transfer &&
- nb_sectors << BDRV_SECTOR_BITS > bs->bl.max_transfer) {
- error_report("iSCSI Error: Read of %d sectors exceeds max_xfer_len "
- "of %" PRIu32 " bytes", nb_sectors, bs->bl.max_transfer);
- return -EINVAL;
+ if (bs->bl.max_transfer) {
+ assert(nb_sectors << BDRV_SECTOR_BITS <= bs->bl.max_transfer);
}
/* if cache.direct is off and we have a valid entry in our allocation map
@@ -1048,29 +1042,26 @@ iscsi_getlength(BlockDriverState *bs)
}
static int
-coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
+coroutine_fn iscsi_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
{
IscsiLun *iscsilun = bs->opaque;
struct IscsiTask iTask;
struct unmap_list list;
- if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
- return -EINVAL;
- }
+ assert(is_byte_request_lun_aligned(offset, count, iscsilun));
if (!iscsilun->lbp.lbpu) {
/* UNMAP is not supported by the target */
return 0;
}
- list.lba = sector_qemu2lun(sector_num, iscsilun);
- list.num = sector_qemu2lun(nb_sectors, iscsilun);
+ list.lba = offset / iscsilun->block_size;
+ list.num = count / iscsilun->block_size;
iscsi_co_init_iscsitask(iscsilun, &iTask);
retry:
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
- iscsi_co_generic_cb, &iTask) == NULL) {
+ iscsi_co_generic_cb, &iTask) == NULL) {
return -ENOMEM;
}
@@ -1100,7 +1091,8 @@ retry:
return iTask.err_code;
}
- iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
+ iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
return 0;
}
@@ -2004,7 +1996,7 @@ static BlockDriver bdrv_iscsi = {
.bdrv_refresh_limits = iscsi_refresh_limits,
.bdrv_co_get_block_status = iscsi_co_get_block_status,
- .bdrv_co_discard = iscsi_co_discard,
+ .bdrv_co_pdiscard = iscsi_co_pdiscard,
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
.bdrv_co_readv = iscsi_co_readv,
.bdrv_co_writev_flags = iscsi_co_writev_flags,
diff --git a/block/mirror.c b/block/mirror.c
index 9ae11e5276..69a1a7cc96 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -58,9 +58,10 @@ typedef struct MirrorBlockJob {
QSIMPLEQ_HEAD(, MirrorBuffer) buf_free;
int buf_free_count;
+ uint64_t last_pause_ns;
unsigned long *in_flight_bitmap;
int in_flight;
- int sectors_in_flight;
+ int64_t sectors_in_flight;
int ret;
bool unmap;
bool waiting_for_io;
@@ -303,8 +304,9 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
s->in_flight++;
s->sectors_in_flight += nb_sectors;
if (is_discard) {
- blk_aio_discard(s->target, sector_num, op->nb_sectors,
- mirror_write_complete, op);
+ blk_aio_pdiscard(s->target, sector_num << BDRV_SECTOR_BITS,
+ op->nb_sectors << BDRV_SECTOR_BITS,
+ mirror_write_complete, op);
} else {
blk_aio_pwrite_zeroes(s->target, sector_num * BDRV_SECTOR_SIZE,
op->nb_sectors * BDRV_SECTOR_SIZE,
@@ -322,6 +324,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
int nb_chunks = 1;
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
+ bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
sector_num = hbitmap_iter_next(&s->hbi);
if (sector_num < 0) {
@@ -372,7 +375,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
while (nb_chunks > 0 && sector_num < end) {
int ret;
- int io_sectors;
+ int io_sectors, io_sectors_acct;
BlockDriverState *file;
enum MirrorMethod {
MIRROR_METHOD_COPY,
@@ -405,16 +408,26 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
}
}
+ while (s->in_flight >= MAX_IN_FLIGHT) {
+ trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
+ mirror_wait_for_io(s);
+ }
+
mirror_clip_sectors(s, sector_num, &io_sectors);
switch (mirror_method) {
case MIRROR_METHOD_COPY:
io_sectors = mirror_do_read(s, sector_num, io_sectors);
+ io_sectors_acct = io_sectors;
break;
case MIRROR_METHOD_ZERO:
- mirror_do_zero_or_discard(s, sector_num, io_sectors, false);
- break;
case MIRROR_METHOD_DISCARD:
- mirror_do_zero_or_discard(s, sector_num, io_sectors, true);
+ mirror_do_zero_or_discard(s, sector_num, io_sectors,
+ mirror_method == MIRROR_METHOD_DISCARD);
+ if (write_zeroes_ok) {
+ io_sectors_acct = 0;
+ } else {
+ io_sectors_acct = io_sectors;
+ }
break;
default:
abort();
@@ -423,7 +436,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
sector_num += io_sectors;
nb_chunks -= DIV_ROUND_UP(io_sectors, sectors_per_chunk);
if (s->common.speed) {
- delay_ns = ratelimit_calculate_delay(&s->limit, io_sectors);
+ delay_ns = ratelimit_calculate_delay(&s->limit, io_sectors_acct);
}
}
return delay_ns;
@@ -511,19 +524,94 @@ static void mirror_exit(BlockJob *job, void *opaque)
bdrv_unref(src);
}
+static void mirror_throttle(MirrorBlockJob *s)
+{
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+
+ if (now - s->last_pause_ns > SLICE_TIME) {
+ s->last_pause_ns = now;
+ block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0);
+ } else {
+ block_job_pause_point(&s->common);
+ }
+}
+
+static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
+{
+ int64_t sector_num, end;
+ BlockDriverState *base = s->base;
+ BlockDriverState *bs = blk_bs(s->common.blk);
+ BlockDriverState *target_bs = blk_bs(s->target);
+ int ret, n;
+
+ end = s->bdev_length / BDRV_SECTOR_SIZE;
+
+ if (base == NULL && !bdrv_has_zero_init(target_bs)) {
+ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
+ bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, end);
+ return 0;
+ }
+
+ for (sector_num = 0; sector_num < end; ) {
+ int nb_sectors = MIN(end - sector_num,
+ QEMU_ALIGN_DOWN(INT_MAX, s->granularity) >> BDRV_SECTOR_BITS);
+
+ mirror_throttle(s);
+
+ if (block_job_is_cancelled(&s->common)) {
+ return 0;
+ }
+
+ if (s->in_flight >= MAX_IN_FLIGHT) {
+ trace_mirror_yield(s, s->in_flight, s->buf_free_count, -1);
+ mirror_wait_for_io(s);
+ continue;
+ }
+
+ mirror_do_zero_or_discard(s, sector_num, nb_sectors, false);
+ sector_num += nb_sectors;
+ }
+
+ mirror_drain(s);
+ }
+
+ /* First part, loop on the sectors and initialize the dirty bitmap. */
+ for (sector_num = 0; sector_num < end; ) {
+ /* Just to make sure we are not exceeding int limit. */
+ int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS,
+ end - sector_num);
+
+ mirror_throttle(s);
+
+ if (block_job_is_cancelled(&s->common)) {
+ return 0;
+ }
+
+ ret = bdrv_is_allocated_above(bs, base, sector_num, nb_sectors, &n);
+ if (ret < 0) {
+ return ret;
+ }
+
+ assert(n > 0);
+ if (ret == 1) {
+ bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n);
+ }
+ sector_num += n;
+ }
+ return 0;
+}
+
static void coroutine_fn mirror_run(void *opaque)
{
MirrorBlockJob *s = opaque;
MirrorExitData *data;
BlockDriverState *bs = blk_bs(s->common.blk);
BlockDriverState *target_bs = blk_bs(s->target);
- int64_t sector_num, end, length;
- uint64_t last_pause_ns;
+ int64_t length;
BlockDriverInfo bdi;
char backing_filename[2]; /* we only need 2 characters because we are only
checking for a NULL string */
int ret = 0;
- int n;
int target_cluster_size = BDRV_SECTOR_SIZE;
if (block_job_is_cancelled(&s->common)) {
@@ -565,7 +653,6 @@ static void coroutine_fn mirror_run(void *opaque)
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov);
- end = s->bdev_length / BDRV_SECTOR_SIZE;
s->buf = qemu_try_blockalign(bs, s->buf_size);
if (s->buf == NULL) {
ret = -ENOMEM;
@@ -574,47 +661,18 @@ static void coroutine_fn mirror_run(void *opaque)
mirror_free_init(s);
- last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
if (!s->is_none_mode) {
- /* First part, loop on the sectors and initialize the dirty bitmap. */
- BlockDriverState *base = s->base;
- bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(target_bs);
-
- for (sector_num = 0; sector_num < end; ) {
- /* Just to make sure we are not exceeding int limit. */
- int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS,
- end - sector_num);
- int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
-
- if (now - last_pause_ns > SLICE_TIME) {
- last_pause_ns = now;
- block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0);
- } else {
- block_job_pause_point(&s->common);
- }
-
- if (block_job_is_cancelled(&s->common)) {
- goto immediate_exit;
- }
-
- ret = bdrv_is_allocated_above(bs, base, sector_num, nb_sectors, &n);
-
- if (ret < 0) {
- goto immediate_exit;
- }
-
- assert(n > 0);
- if (ret == 1 || mark_all_dirty) {
- bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n);
- }
- sector_num += n;
+ ret = mirror_dirty_init(s);
+ if (ret < 0 || block_job_is_cancelled(&s->common)) {
+ goto immediate_exit;
}
}
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
for (;;) {
uint64_t delay_ns = 0;
- int64_t cnt;
+ int64_t cnt, delta;
bool should_complete;
if (s->ret < 0) {
@@ -637,9 +695,10 @@ static void coroutine_fn mirror_run(void *opaque)
* We do so every SLICE_TIME nanoseconds, or when there is an error,
* or when the source is clean, whichever comes first.
*/
- if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - last_pause_ns < SLICE_TIME &&
+ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns;
+ if (delta < SLICE_TIME &&
s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
- if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
+ if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
(cnt == 0 && s->in_flight > 0)) {
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
mirror_wait_for_io(s);
@@ -707,7 +766,7 @@ static void coroutine_fn mirror_run(void *opaque)
s->common.cancelled = false;
break;
}
- last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
}
immediate_exit:
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 4cc408d206..2cf3237ef3 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -116,7 +116,7 @@ static void nbd_restart_write(void *opaque)
static int nbd_co_send_request(BlockDriverState *bs,
struct nbd_request *request,
- QEMUIOVector *qiov, int offset)
+ QEMUIOVector *qiov)
{
NbdClientSession *s = nbd_get_client_session(bs);
AioContext *aio_context;
@@ -149,8 +149,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
qio_channel_set_cork(s->ioc, true);
rc = nbd_send_request(s->ioc, request);
if (rc >= 0) {
- ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
- offset, request->len, 0);
+ ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
+ false);
if (ret != request->len) {
rc = -EIO;
}
@@ -167,8 +167,9 @@ static int nbd_co_send_request(BlockDriverState *bs,
}
static void nbd_co_receive_reply(NbdClientSession *s,
- struct nbd_request *request, struct nbd_reply *reply,
- QEMUIOVector *qiov, int offset)
+ struct nbd_request *request,
+ struct nbd_reply *reply,
+ QEMUIOVector *qiov)
{
int ret;
@@ -181,8 +182,8 @@ static void nbd_co_receive_reply(NbdClientSession *s,
reply->error = EIO;
} else {
if (qiov && reply->error == 0) {
- ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov,
- offset, request->len, 1);
+ ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
+ true);
if (ret != request->len) {
reply->error = EIO;
}
@@ -217,36 +218,41 @@ static void nbd_coroutine_end(NbdClientSession *s,
}
}
-static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov,
- int offset)
+int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov, int flags)
{
NbdClientSession *client = nbd_get_client_session(bs);
- struct nbd_request request = { .type = NBD_CMD_READ };
+ struct nbd_request request = {
+ .type = NBD_CMD_READ,
+ .from = offset,
+ .len = bytes,
+ };
struct nbd_reply reply;
ssize_t ret;
- request.from = sector_num * 512;
- request.len = nb_sectors * 512;
+ assert(bytes <= NBD_MAX_BUFFER_SIZE);
+ assert(!flags);
nbd_coroutine_start(client, &request);
- ret = nbd_co_send_request(bs, &request, NULL, 0);
+ ret = nbd_co_send_request(bs, &request, NULL);
if (ret < 0) {
reply.error = -ret;
} else {
- nbd_co_receive_reply(client, &request, &reply, qiov, offset);
+ nbd_co_receive_reply(client, &request, &reply, qiov);
}
nbd_coroutine_end(client, &request);
return -reply.error;
-
}
-static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov,
- int offset, int flags)
+int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov, int flags)
{
NbdClientSession *client = nbd_get_client_session(bs);
- struct nbd_request request = { .type = NBD_CMD_WRITE };
+ struct nbd_request request = {
+ .type = NBD_CMD_WRITE,
+ .from = offset,
+ .len = bytes,
+ };
struct nbd_reply reply;
ssize_t ret;
@@ -255,55 +261,19 @@ static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
request.type |= NBD_CMD_FLAG_FUA;
}
- request.from = sector_num * 512;
- request.len = nb_sectors * 512;
+ assert(bytes <= NBD_MAX_BUFFER_SIZE);
nbd_coroutine_start(client, &request);
- ret = nbd_co_send_request(bs, &request, qiov, offset);
+ ret = nbd_co_send_request(bs, &request, qiov);
if (ret < 0) {
reply.error = -ret;
} else {
- nbd_co_receive_reply(client, &request, &reply, NULL, 0);
+ nbd_co_receive_reply(client, &request, &reply, NULL);
}
nbd_coroutine_end(client, &request);
return -reply.error;
}
-int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov)
-{
- int offset = 0;
- int ret;
- while (nb_sectors > NBD_MAX_SECTORS) {
- ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
- if (ret < 0) {
- return ret;
- }
- offset += NBD_MAX_SECTORS * 512;
- sector_num += NBD_MAX_SECTORS;
- nb_sectors -= NBD_MAX_SECTORS;
- }
- return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset);
-}
-
-int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov, int flags)
-{
- int offset = 0;
- int ret;
- while (nb_sectors > NBD_MAX_SECTORS) {
- ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset,
- flags);
- if (ret < 0) {
- return ret;
- }
- offset += NBD_MAX_SECTORS * 512;
- sector_num += NBD_MAX_SECTORS;
- nb_sectors -= NBD_MAX_SECTORS;
- }
- return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset, flags);
-}
-
int nbd_client_co_flush(BlockDriverState *bs)
{
NbdClientSession *client = nbd_get_client_session(bs);
@@ -319,36 +289,37 @@ int nbd_client_co_flush(BlockDriverState *bs)
request.len = 0;
nbd_coroutine_start(client, &request);
- ret = nbd_co_send_request(bs, &request, NULL, 0);
+ ret = nbd_co_send_request(bs, &request, NULL);
if (ret < 0) {
reply.error = -ret;
} else {
- nbd_co_receive_reply(client, &request, &reply, NULL, 0);
+ nbd_co_receive_reply(client, &request, &reply, NULL);
}
nbd_coroutine_end(client, &request);
return -reply.error;
}
-int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
+int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
{
NbdClientSession *client = nbd_get_client_session(bs);
- struct nbd_request request = { .type = NBD_CMD_TRIM };
+ struct nbd_request request = {
+ .type = NBD_CMD_TRIM,
+ .from = offset,
+ .len = count,
+ };
struct nbd_reply reply;
ssize_t ret;
if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) {
return 0;
}
- request.from = sector_num * 512;
- request.len = nb_sectors * 512;
nbd_coroutine_start(client, &request);
- ret = nbd_co_send_request(bs, &request, NULL, 0);
+ ret = nbd_co_send_request(bs, &request, NULL);
if (ret < 0) {
reply.error = -ret;
} else {
- nbd_co_receive_reply(client, &request, &reply, NULL, 0);
+ nbd_co_receive_reply(client, &request, &reply, NULL);
}
nbd_coroutine_end(client, &request);
return -reply.error;
diff --git a/block/nbd-client.h b/block/nbd-client.h
index c618dadc39..fa9817b7d7 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -44,13 +44,12 @@ int nbd_client_init(BlockDriverState *bs,
Error **errp);
void nbd_client_close(BlockDriverState *bs);
-int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors);
+int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count);
int nbd_client_co_flush(BlockDriverState *bs);
-int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov, int flags);
-int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov);
+int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov, int flags);
+int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov, int flags);
void nbd_client_detach_aio_context(BlockDriverState *bs);
void nbd_client_attach_aio_context(BlockDriverState *bs,
diff --git a/block/nbd.c b/block/nbd.c
index 08e5b67b2f..8d57220f18 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -349,12 +349,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}
-static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov)
-{
- return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov);
-}
-
static int nbd_co_flush(BlockDriverState *bs)
{
return nbd_client_co_flush(bs);
@@ -366,12 +360,6 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE;
}
-static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
-{
- return nbd_client_co_discard(bs, sector_num, nb_sectors);
-}
-
static void nbd_close(BlockDriverState *bs)
{
nbd_client_close(bs);
@@ -450,11 +438,11 @@ static BlockDriver bdrv_nbd = {
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
- .bdrv_co_readv = nbd_co_readv,
- .bdrv_co_writev_flags = nbd_client_co_writev,
+ .bdrv_co_preadv = nbd_client_co_preadv,
+ .bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
- .bdrv_co_discard = nbd_co_discard,
+ .bdrv_co_pdiscard = nbd_client_co_pdiscard,
.bdrv_refresh_limits = nbd_refresh_limits,
.bdrv_getlength = nbd_getlength,
.bdrv_detach_aio_context = nbd_detach_aio_context,
@@ -468,11 +456,11 @@ static BlockDriver bdrv_nbd_tcp = {
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
- .bdrv_co_readv = nbd_co_readv,
- .bdrv_co_writev_flags = nbd_client_co_writev,
+ .bdrv_co_preadv = nbd_client_co_preadv,
+ .bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
- .bdrv_co_discard = nbd_co_discard,
+ .bdrv_co_pdiscard = nbd_client_co_pdiscard,
.bdrv_refresh_limits = nbd_refresh_limits,
.bdrv_getlength = nbd_getlength,
.bdrv_detach_aio_context = nbd_detach_aio_context,
@@ -486,11 +474,11 @@ static BlockDriver bdrv_nbd_unix = {
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
- .bdrv_co_readv = nbd_co_readv,
- .bdrv_co_writev_flags = nbd_client_co_writev,
+ .bdrv_co_preadv = nbd_client_co_preadv,
+ .bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
- .bdrv_co_discard = nbd_co_discard,
+ .bdrv_co_pdiscard = nbd_client_co_pdiscard,
.bdrv_refresh_limits = nbd_refresh_limits,
.bdrv_getlength = nbd_getlength,
.bdrv_detach_aio_context = nbd_detach_aio_context,
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 49b6ce6bfd..cbfb3fe064 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -615,9 +615,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
/* Discard is optional, ignore the return value */
if (ret >= 0) {
- bdrv_discard(bs->file->bs,
- d->offset >> BDRV_SECTOR_BITS,
- d->bytes >> BDRV_SECTOR_BITS);
+ bdrv_pdiscard(bs->file->bs, d->offset, d->bytes);
}
g_free(d);
diff --git a/block/qcow2.c b/block/qcow2.c
index a6bca735e5..d620d0a85b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2479,15 +2479,15 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
return ret;
}
-static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors)
+static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count)
{
int ret;
BDRVQcow2State *s = bs->opaque;
qemu_co_mutex_lock(&s->lock);
- ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
- nb_sectors, QCOW2_DISCARD_REQUEST, false);
+ ret = qcow2_discard_clusters(bs, offset, count >> BDRV_SECTOR_BITS,
+ QCOW2_DISCARD_REQUEST, false);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
@@ -3410,7 +3410,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
- .bdrv_co_discard = qcow2_co_discard,
+ .bdrv_co_pdiscard = qcow2_co_pdiscard,
.bdrv_truncate = qcow2_truncate,
.bdrv_write_compressed = qcow2_write_compressed,
.bdrv_make_empty = qcow2_make_empty,
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 20f4d7aa8d..6ed7547392 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -1214,7 +1214,7 @@ static int paio_submit_co(BlockDriverState *bs, int fd,
}
static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ int64_t offset, QEMUIOVector *qiov, int count,
BlockCompletionFunc *cb, void *opaque, int type)
{
RawPosixAIOData *acb = g_new(RawPosixAIOData, 1);
@@ -1224,8 +1224,8 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
acb->aio_type = type;
acb->aio_fildes = fd;
- acb->aio_nbytes = nb_sectors * BDRV_SECTOR_SIZE;
- acb->aio_offset = sector_num * BDRV_SECTOR_SIZE;
+ acb->aio_nbytes = count;
+ acb->aio_offset = offset;
if (qiov) {
acb->aio_iov = qiov->iov;
@@ -1233,7 +1233,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
assert(qiov->size == acb->aio_nbytes);
}
- trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
+ trace_paio_submit(acb, opaque, offset, count, type);
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
}
@@ -1786,13 +1786,13 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
return ret | BDRV_BLOCK_OFFSET_VALID | start;
}
-static coroutine_fn BlockAIOCB *raw_aio_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors,
+static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque)
{
BDRVRawState *s = bs->opaque;
- return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors,
+ return paio_submit(bs, s->fd, offset, NULL, count,
cb, opaque, QEMU_AIO_DISCARD);
}
@@ -1864,7 +1864,7 @@ BlockDriver bdrv_file = {
.bdrv_co_preadv = raw_co_preadv,
.bdrv_co_pwritev = raw_co_pwritev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_aio_discard = raw_aio_discard,
+ .bdrv_aio_pdiscard = raw_aio_pdiscard,
.bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
@@ -2203,8 +2203,8 @@ static int fd_open(BlockDriverState *bs)
return -EIO;
}
-static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors,
+static coroutine_fn BlockAIOCB *hdev_aio_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque)
{
BDRVRawState *s = bs->opaque;
@@ -2212,7 +2212,7 @@ static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs,
if (fd_open(bs) < 0) {
return NULL;
}
- return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors,
+ return paio_submit(bs, s->fd, offset, NULL, count,
cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
}
@@ -2307,7 +2307,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_co_preadv = raw_co_preadv,
.bdrv_co_pwritev = raw_co_pwritev,
.bdrv_aio_flush = raw_aio_flush,
- .bdrv_aio_discard = hdev_aio_discard,
+ .bdrv_aio_pdiscard = hdev_aio_pdiscard,
.bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug,
.bdrv_io_unplug = raw_aio_unplug,
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 9b813d99ae..56f45fea9e 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -142,7 +142,7 @@ static int aio_worker(void *arg)
}
static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ int64_t offset, QEMUIOVector *qiov, int count,
BlockCompletionFunc *cb, void *opaque, int type)
{
RawWin32AIOData *acb = g_new(RawWin32AIOData, 1);
@@ -155,11 +155,12 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
if (qiov) {
acb->aio_iov = qiov->iov;
acb->aio_niov = qiov->niov;
+ assert(qiov->size == count);
}
- acb->aio_nbytes = nb_sectors * 512;
- acb->aio_offset = sector_num * 512;
+ acb->aio_nbytes = count;
+ acb->aio_offset = offset;
- trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
+ trace_paio_submit(acb, opaque, offset, count, type);
pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
}
@@ -378,9 +379,10 @@ static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
BDRVRawState *s = bs->opaque;
if (s->aio) {
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
- nb_sectors, cb, opaque, QEMU_AIO_READ);
+ nb_sectors, cb, opaque, QEMU_AIO_READ);
} else {
- return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
+ return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
+ nb_sectors << BDRV_SECTOR_BITS,
cb, opaque, QEMU_AIO_READ);
}
}
@@ -392,9 +394,10 @@ static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
BDRVRawState *s = bs->opaque;
if (s->aio) {
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
- nb_sectors, cb, opaque, QEMU_AIO_WRITE);
+ nb_sectors, cb, opaque, QEMU_AIO_WRITE);
} else {
- return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
+ return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
+ nb_sectors << BDRV_SECTOR_BITS,
cb, opaque, QEMU_AIO_WRITE);
}
}
diff --git a/block/raw_bsd.c b/block/raw_bsd.c
index 5f9dd299a6..588d4080f9 100644
--- a/block/raw_bsd.c
+++ b/block/raw_bsd.c
@@ -50,33 +50,30 @@ static int raw_reopen_prepare(BDRVReopenState *reopen_state,
return 0;
}
-static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov)
+static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov,
+ int flags)
{
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
- return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov);
+ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
}
-static int coroutine_fn
-raw_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, QEMUIOVector *qiov,
+ int flags)
{
void *buf = NULL;
BlockDriver *drv;
QEMUIOVector local_qiov;
int ret;
- if (bs->probed && sector_num == 0) {
- /* As long as these conditions are true, we can't get partial writes to
- * the probe buffer and can just directly check the request. */
+ if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
+ /* Handling partial writes would be a pain - so we just
+ * require that guests have 512-byte request alignment if
+ * probing occurred */
QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512);
QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512);
-
- if (nb_sectors == 0) {
- /* qemu_iovec_to_buf() would fail, but we want to return success
- * instead of -EINVAL in this case. */
- return 0;
- }
+ assert(offset == 0 && bytes >= BLOCK_PROBE_BUF_SIZE);
buf = qemu_try_blockalign(bs->file->bs, 512);
if (!buf) {
@@ -105,8 +102,7 @@ raw_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
}
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
- ret = bdrv_co_pwritev(bs->file, sector_num * BDRV_SECTOR_SIZE,
- nb_sectors * BDRV_SECTOR_SIZE, qiov, flags);
+ ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
fail:
if (qiov == &local_qiov) {
@@ -134,10 +130,10 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
}
-static int coroutine_fn raw_co_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors)
+static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count)
{
- return bdrv_co_discard(bs->file->bs, sector_num, nb_sectors);
+ return bdrv_co_pdiscard(bs->file->bs, offset, count);
}
static int64_t raw_getlength(BlockDriverState *bs)
@@ -150,6 +146,16 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return bdrv_get_info(bs->file->bs, bdi);
}
+static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
+{
+ if (bs->probed) {
+ /* To make it easier to protect the first sector, any probed
+ * image is restricted to read-modify-write on sub-sector
+ * operations. */
+ bs->bl.request_alignment = BDRV_SECTOR_SIZE;
+ }
+}
+
static int raw_truncate(BlockDriverState *bs, int64_t offset)
{
return bdrv_truncate(bs->file->bs, offset);
@@ -192,8 +198,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
bs->sg = bs->file->bs->sg;
- bs->supported_write_flags = BDRV_REQ_FUA;
- bs->supported_zero_flags = BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP;
+ bs->supported_write_flags = BDRV_REQ_FUA &
+ bs->file->bs->supported_write_flags;
+ bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
+ bs->file->bs->supported_zero_flags;
if (bs->probed && !bdrv_is_read_only(bs)) {
fprintf(stderr,
@@ -238,15 +246,16 @@ BlockDriver bdrv_raw = {
.bdrv_open = &raw_open,
.bdrv_close = &raw_close,
.bdrv_create = &raw_create,
- .bdrv_co_readv = &raw_co_readv,
- .bdrv_co_writev_flags = &raw_co_writev_flags,
+ .bdrv_co_preadv = &raw_co_preadv,
+ .bdrv_co_pwritev = &raw_co_pwritev,
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
- .bdrv_co_discard = &raw_co_discard,
+ .bdrv_co_pdiscard = &raw_co_pdiscard,
.bdrv_co_get_block_status = &raw_co_get_block_status,
.bdrv_truncate = &raw_truncate,
.bdrv_getlength = &raw_getlength,
.has_variable_length = true,
.bdrv_get_info = &raw_get_info,
+ .bdrv_refresh_limits = &raw_refresh_limits,
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
.bdrv_probe_geometry = &raw_probe_geometry,
.bdrv_media_changed = &raw_media_changed,
diff --git a/block/rbd.c b/block/rbd.c
index 0a5840d08b..0106fea45f 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -649,9 +649,9 @@ static int rbd_aio_flush_wrapper(rbd_image_t image,
}
static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
- int64_t sector_num,
+ int64_t off,
QEMUIOVector *qiov,
- int nb_sectors,
+ int64_t size,
BlockCompletionFunc *cb,
void *opaque,
RBDAIOCmd cmd)
@@ -659,7 +659,6 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
RBDAIOCB *acb;
RADOSCB *rcb = NULL;
rbd_completion_t c;
- int64_t off, size;
char *buf;
int r;
@@ -668,6 +667,7 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque);
acb->cmd = cmd;
acb->qiov = qiov;
+ assert(!qiov || qiov->size == size);
if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) {
acb->bounce = NULL;
} else {
@@ -687,9 +687,6 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
buf = acb->bounce;
- off = sector_num * BDRV_SECTOR_SIZE;
- size = nb_sectors * BDRV_SECTOR_SIZE;
-
rcb = g_new(RADOSCB, 1);
rcb->acb = acb;
rcb->buf = buf;
@@ -739,7 +736,8 @@ static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
BlockCompletionFunc *cb,
void *opaque)
{
- return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
+ nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
RBD_AIO_READ);
}
@@ -750,7 +748,8 @@ static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
BlockCompletionFunc *cb,
void *opaque)
{
- return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
+ return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
+ nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
RBD_AIO_WRITE);
}
@@ -931,13 +930,13 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
}
#ifdef LIBRBD_SUPPORTS_DISCARD
-static BlockAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
- int64_t sector_num,
- int nb_sectors,
- BlockCompletionFunc *cb,
- void *opaque)
+static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
+ int64_t offset,
+ int count,
+ BlockCompletionFunc *cb,
+ void *opaque)
{
- return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque,
+ return rbd_start_aio(bs, offset, NULL, count, cb, opaque,
RBD_AIO_DISCARD);
}
#endif
@@ -1001,7 +1000,7 @@ static BlockDriver bdrv_rbd = {
#endif
#ifdef LIBRBD_SUPPORTS_DISCARD
- .bdrv_aio_discard = qemu_rbd_aio_discard,
+ .bdrv_aio_pdiscard = qemu_rbd_aio_pdiscard,
#endif
.bdrv_snapshot_create = qemu_rbd_snap_create,
diff --git a/block/sheepdog.c b/block/sheepdog.c
index e739c56f08..66e1cb2b2d 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -2800,8 +2800,8 @@ static int sd_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
}
-static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors)
+static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset,
+ int count)
{
SheepdogAIOCB *acb;
BDRVSheepdogState *s = bs->opaque;
@@ -2811,7 +2811,7 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
uint32_t zero = 0;
if (!s->discard_supported) {
- return 0;
+ return 0;
}
memset(&discard_iov, 0, sizeof(discard_iov));
@@ -2820,7 +2820,10 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
iov.iov_len = sizeof(zero);
discard_iov.iov = &iov;
discard_iov.niov = 1;
- acb = sd_aio_setup(bs, &discard_iov, sector_num, nb_sectors);
+ assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((count & (BDRV_SECTOR_SIZE - 1)) == 0);
+ acb = sd_aio_setup(bs, &discard_iov, offset >> BDRV_SECTOR_BITS,
+ count >> BDRV_SECTOR_BITS);
acb->aiocb_type = AIOCB_DISCARD_OBJ;
acb->aio_done_func = sd_finish_aiocb;
@@ -2954,7 +2957,7 @@ static BlockDriver bdrv_sheepdog = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
- .bdrv_co_discard = sd_co_discard,
+ .bdrv_co_pdiscard = sd_co_pdiscard,
.bdrv_co_get_block_status = sd_co_get_block_status,
.bdrv_snapshot_create = sd_snapshot_create,
@@ -2990,7 +2993,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
- .bdrv_co_discard = sd_co_discard,
+ .bdrv_co_pdiscard = sd_co_pdiscard,
.bdrv_co_get_block_status = sd_co_get_block_status,
.bdrv_snapshot_create = sd_snapshot_create,
@@ -3026,7 +3029,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
- .bdrv_co_discard = sd_co_discard,
+ .bdrv_co_pdiscard = sd_co_pdiscard,
.bdrv_co_get_block_status = sd_co_get_block_status,
.bdrv_snapshot_create = sd_snapshot_create,
diff --git a/block/trace-events b/block/trace-events
index 354967eacb..978ef4f02a 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -9,7 +9,7 @@ blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags %x"
# block/io.c
-bdrv_aio_discard(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
+bdrv_aio_pdiscard(void *bs, int64_t offset, int count, void *opaque) "bs %p offset %"PRId64" count %d opaque %p"
bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p"
bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
@@ -58,7 +58,7 @@ qmp_block_stream(void *bs, void *job) "bs %p job %p"
# block/raw-win32.c
# block/raw-posix.c
paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d"
-paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d"
+paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
# block/qcow2.c
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset %" PRIx64 " bytes %d"
diff --git a/blockdev.c b/blockdev.c
index 384ad3bba6..eafeba96d0 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2634,49 +2634,17 @@ fail:
}
/* throttling disk I/O limits */
-void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
- int64_t bps_wr,
- int64_t iops,
- int64_t iops_rd,
- int64_t iops_wr,
- bool has_bps_max,
- int64_t bps_max,
- bool has_bps_rd_max,
- int64_t bps_rd_max,
- bool has_bps_wr_max,
- int64_t bps_wr_max,
- bool has_iops_max,
- int64_t iops_max,
- bool has_iops_rd_max,
- int64_t iops_rd_max,
- bool has_iops_wr_max,
- int64_t iops_wr_max,
- bool has_bps_max_length,
- int64_t bps_max_length,
- bool has_bps_rd_max_length,
- int64_t bps_rd_max_length,
- bool has_bps_wr_max_length,
- int64_t bps_wr_max_length,
- bool has_iops_max_length,
- int64_t iops_max_length,
- bool has_iops_rd_max_length,
- int64_t iops_rd_max_length,
- bool has_iops_wr_max_length,
- int64_t iops_wr_max_length,
- bool has_iops_size,
- int64_t iops_size,
- bool has_group,
- const char *group, Error **errp)
+void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
{
ThrottleConfig cfg;
BlockDriverState *bs;
BlockBackend *blk;
AioContext *aio_context;
- blk = blk_by_name(device);
+ blk = blk_by_name(arg->device);
if (!blk) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ "Device '%s' not found", arg->device);
return;
}
@@ -2685,59 +2653,59 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
bs = blk_bs(blk);
if (!bs) {
- error_setg(errp, "Device '%s' has no medium", device);
+ error_setg(errp, "Device '%s' has no medium", arg->device);
goto out;
}
throttle_config_init(&cfg);
- cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
- cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
- cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
+ cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
+ cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
+ cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
- cfg.buckets[THROTTLE_OPS_TOTAL].avg = iops;
- cfg.buckets[THROTTLE_OPS_READ].avg = iops_rd;
- cfg.buckets[THROTTLE_OPS_WRITE].avg = iops_wr;
+ cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
+ cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
+ cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
- if (has_bps_max) {
- cfg.buckets[THROTTLE_BPS_TOTAL].max = bps_max;
+ if (arg->has_bps_max) {
+ cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
}
- if (has_bps_rd_max) {
- cfg.buckets[THROTTLE_BPS_READ].max = bps_rd_max;
+ if (arg->has_bps_rd_max) {
+ cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
}
- if (has_bps_wr_max) {
- cfg.buckets[THROTTLE_BPS_WRITE].max = bps_wr_max;
+ if (arg->has_bps_wr_max) {
+ cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
}
- if (has_iops_max) {
- cfg.buckets[THROTTLE_OPS_TOTAL].max = iops_max;
+ if (arg->has_iops_max) {
+ cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
}
- if (has_iops_rd_max) {
- cfg.buckets[THROTTLE_OPS_READ].max = iops_rd_max;
+ if (arg->has_iops_rd_max) {
+ cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
}
- if (has_iops_wr_max) {
- cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
+ if (arg->has_iops_wr_max) {
+ cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
}
- if (has_bps_max_length) {
- cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = bps_max_length;
+ if (arg->has_bps_max_length) {
+ cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
}
- if (has_bps_rd_max_length) {
- cfg.buckets[THROTTLE_BPS_READ].burst_length = bps_rd_max_length;
+ if (arg->has_bps_rd_max_length) {
+ cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
}
- if (has_bps_wr_max_length) {
- cfg.buckets[THROTTLE_BPS_WRITE].burst_length = bps_wr_max_length;
+ if (arg->has_bps_wr_max_length) {
+ cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
}
- if (has_iops_max_length) {
- cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = iops_max_length;
+ if (arg->has_iops_max_length) {
+ cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
}
- if (has_iops_rd_max_length) {
- cfg.buckets[THROTTLE_OPS_READ].burst_length = iops_rd_max_length;
+ if (arg->has_iops_rd_max_length) {
+ cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
}
- if (has_iops_wr_max_length) {
- cfg.buckets[THROTTLE_OPS_WRITE].burst_length = iops_wr_max_length;
+ if (arg->has_iops_wr_max_length) {
+ cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
}
- if (has_iops_size) {
- cfg.op_size = iops_size;
+ if (arg->has_iops_size) {
+ cfg.op_size = arg->iops_size;
}
if (!throttle_is_valid(&cfg, errp)) {
@@ -2748,9 +2716,10 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
/* Enable I/O limits if they're not enabled yet, otherwise
* just update the throttling group. */
if (!blk_get_public(blk)->throttle_state) {
- blk_io_limits_enable(blk, has_group ? group : device);
- } else if (has_group) {
- blk_io_limits_update_group(blk, group);
+ blk_io_limits_enable(blk,
+ arg->has_group ? arg->group : arg->device);
+ } else if (arg->has_group) {
+ blk_io_limits_update_group(blk, arg->group);
}
/* Set the new throttling configuration */
blk_set_io_limits(blk, &cfg);
@@ -3497,19 +3466,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
block_job_cb, bs, errp);
}
-void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
- const char *target, bool has_format, const char *format,
- bool has_node_name, const char *node_name,
- bool has_replaces, const char *replaces,
- enum MirrorSyncMode sync,
- bool has_mode, enum NewImageMode mode,
- bool has_speed, int64_t speed,
- bool has_granularity, uint32_t granularity,
- bool has_buf_size, int64_t buf_size,
- bool has_on_source_error, BlockdevOnError on_source_error,
- bool has_on_target_error, BlockdevOnError on_target_error,
- bool has_unmap, bool unmap,
- Error **errp)
+void qmp_drive_mirror(DriveMirror *arg, Error **errp)
{
BlockDriverState *bs;
BlockBackend *blk;
@@ -3520,11 +3477,12 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
QDict *options = NULL;
int flags;
int64_t size;
+ const char *format = arg->format;
- blk = blk_by_name(device);
+ blk = blk_by_name(arg->device);
if (!blk) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device);
+ "Device '%s' not found", arg->device);
return;
}
@@ -3532,24 +3490,25 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
aio_context_acquire(aio_context);
if (!blk_is_available(blk)) {
- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, arg->device);
goto out;
}
bs = blk_bs(blk);
- if (!has_mode) {
- mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ if (!arg->has_mode) {
+ arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
- if (!has_format) {
- format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
+ if (!arg->has_format) {
+ format = (arg->mode == NEW_IMAGE_MODE_EXISTING
+ ? NULL : bs->drv->format_name);
}
flags = bs->open_flags | BDRV_O_RDWR;
source = backing_bs(bs);
- if (!source && sync == MIRROR_SYNC_MODE_TOP) {
- sync = MIRROR_SYNC_MODE_FULL;
+ if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) {
+ arg->sync = MIRROR_SYNC_MODE_FULL;
}
- if (sync == MIRROR_SYNC_MODE_NONE) {
+ if (arg->sync == MIRROR_SYNC_MODE_NONE) {
source = bs;
}
@@ -3559,18 +3518,18 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
goto out;
}
- if (has_replaces) {
+ if (arg->has_replaces) {
BlockDriverState *to_replace_bs;
AioContext *replace_aio_context;
int64_t replace_size;
- if (!has_node_name) {
+ if (!arg->has_node_name) {
error_setg(errp, "a node-name must be provided when replacing a"
" named node of the graph");
goto out;
}
- to_replace_bs = check_to_replace_node(bs, replaces, &local_err);
+ to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err);
if (!to_replace_bs) {
error_propagate(errp, local_err);
@@ -3589,26 +3548,26 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
}
}
- if (mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
+ if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
} else {
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
}
- if ((sync == MIRROR_SYNC_MODE_FULL || !source)
- && mode != NEW_IMAGE_MODE_EXISTING)
+ if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
+ && arg->mode != NEW_IMAGE_MODE_EXISTING)
{
/* create new image w/o backing file */
assert(format);
- bdrv_img_create(target, format,
+ bdrv_img_create(arg->target, format,
NULL, NULL, NULL, size, flags, &local_err, false);
} else {
- switch (mode) {
+ switch (arg->mode) {
case NEW_IMAGE_MODE_EXISTING:
break;
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
/* create new image with backing file */
- bdrv_img_create(target, format,
+ bdrv_img_create(arg->target, format,
source->filename,
source->drv->format_name,
NULL, size, flags, &local_err, false);
@@ -3624,8 +3583,8 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
}
options = qdict_new();
- if (has_node_name) {
- qdict_put(options, "node-name", qstring_from_str(node_name));
+ if (arg->has_node_name) {
+ qdict_put(options, "node-name", qstring_from_str(arg->node_name));
}
if (format) {
qdict_put(options, "driver", qstring_from_str(format));
@@ -3634,22 +3593,22 @@ void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
/* Mirroring takes care of copy-on-write using the source's backing
* file.
*/
- target_bs = bdrv_open(target, NULL, options, flags | BDRV_O_NO_BACKING,
- errp);
+ target_bs = bdrv_open(arg->target, NULL, options,
+ flags | BDRV_O_NO_BACKING, errp);
if (!target_bs) {
goto out;
}
bdrv_set_aio_context(target_bs, aio_context);
- blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
- has_replaces, replaces, sync, backing_mode,
- has_speed, speed,
- has_granularity, granularity,
- has_buf_size, buf_size,
- has_on_source_error, on_source_error,
- has_on_target_error, on_target_error,
- has_unmap, unmap,
+ blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
+ arg->has_replaces, arg->replaces, arg->sync,
+ backing_mode, arg->has_speed, arg->speed,
+ arg->has_granularity, arg->granularity,
+ arg->has_buf_size, arg->buf_size,
+ arg->has_on_source_error, arg->on_source_error,
+ arg->has_on_target_error, arg->on_target_error,
+ arg->has_unmap, arg->unmap,
&local_err);
bdrv_unref(target_bs);
error_propagate(errp, local_err);
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index 1f86f4f07f..a36d2d9bdf 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -2,6 +2,7 @@ crypto-obj-y = init.o
crypto-obj-y += hash.o
crypto-obj-$(CONFIG_NETTLE) += hash-nettle.o
crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += hash-gcrypt.o
+crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT),n,y)) += hash-glib.o
crypto-obj-y += aes.o
crypto-obj-y += desrfb.o
crypto-obj-y += cipher.o
@@ -12,6 +13,7 @@ crypto-obj-y += tlssession.o
crypto-obj-y += secret.o
crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o
crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o
+crypto-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS_RND),n,y)) += random-platform.o
crypto-obj-y += pbkdf.o
crypto-obj-$(CONFIG_NETTLE_KDF) += pbkdf-nettle.o
crypto-obj-$(if $(CONFIG_NETTLE_KDF),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o
@@ -28,6 +30,4 @@ crypto-obj-y += block-luks.o
# Let the userspace emulators avoid linking gnutls/etc
crypto-aes-obj-y = aes.o
-stub-obj-y += random-stub.o
stub-obj-y += pbkdf-stub.o
-stub-obj-y += hash-stub.o
diff --git a/crypto/hash-gcrypt.c b/crypto/hash-gcrypt.c
index ed6f842461..7690690f70 100644
--- a/crypto/hash-gcrypt.c
+++ b/crypto/hash-gcrypt.c
@@ -55,8 +55,7 @@ int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
gcry_md_hd_t md;
unsigned char *digest;
- if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) ||
- qcrypto_hash_alg_map[alg] == GCRY_MD_NONE) {
+ if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
diff --git a/crypto/hash-glib.c b/crypto/hash-glib.c
new file mode 100644
index 0000000000..ec99ac9df9
--- /dev/null
+++ b/crypto/hash-glib.c
@@ -0,0 +1,97 @@
+/*
+ * QEMU Crypto hash algorithms
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/hash.h"
+
+
+static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
+ [QCRYPTO_HASH_ALG_MD5] = G_CHECKSUM_MD5,
+ [QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1,
+ [QCRYPTO_HASH_ALG_SHA224] = -1,
+ [QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256,
+ [QCRYPTO_HASH_ALG_SHA384] = -1,
+#if GLIB_CHECK_VERSION(2, 36, 0)
+ [QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512,
+#else
+ [QCRYPTO_HASH_ALG_SHA512] = -1,
+#endif
+ [QCRYPTO_HASH_ALG_RIPEMD160] = -1,
+};
+
+gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
+{
+ if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) &&
+ qcrypto_hash_alg_map[alg] != -1) {
+ return true;
+ }
+ return false;
+}
+
+
+int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
+ const struct iovec *iov,
+ size_t niov,
+ uint8_t **result,
+ size_t *resultlen,
+ Error **errp)
+{
+ int i, ret;
+ GChecksum *cs;
+
+ if (!qcrypto_hash_supports(alg)) {
+ error_setg(errp,
+ "Unknown hash algorithm %d",
+ alg);
+ return -1;
+ }
+
+ cs = g_checksum_new(qcrypto_hash_alg_map[alg]);
+
+ for (i = 0; i < niov; i++) {
+ g_checksum_update(cs, iov[i].iov_base, iov[i].iov_len);
+ }
+
+ ret = g_checksum_type_get_length(qcrypto_hash_alg_map[alg]);
+ if (ret < 0) {
+ error_setg(errp, "%s",
+ "Unable to get hash length");
+ goto error;
+ }
+ if (*resultlen == 0) {
+ *resultlen = ret;
+ *result = g_new0(uint8_t, *resultlen);
+ } else if (*resultlen != ret) {
+ error_setg(errp,
+ "Result buffer size %zu is smaller than hash %d",
+ *resultlen, ret);
+ goto error;
+ }
+
+ g_checksum_get_digest(cs, *result, resultlen);
+
+ g_checksum_free(cs);
+ return 0;
+
+ error:
+ g_checksum_free(cs);
+ return -1;
+}
diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c
index 4c6f50b65d..6a206dcb18 100644
--- a/crypto/hash-nettle.c
+++ b/crypto/hash-nettle.c
@@ -113,8 +113,7 @@ int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
int i;
union qcrypto_hash_ctx ctx;
- if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) ||
- qcrypto_hash_alg_map[alg].init == NULL) {
+ if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
diff --git a/crypto/hash-stub.c b/crypto/hash-stub.c
deleted file mode 100644
index 8a9b8d4c09..0000000000
--- a/crypto/hash-stub.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * QEMU Crypto hash algorithms
- *
- * Copyright (c) 2016 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "crypto/hash.h"
-
-gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED)
-{
- return false;
-}
-
-int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
- const struct iovec *iov G_GNUC_UNUSED,
- size_t niov G_GNUC_UNUSED,
- uint8_t **result G_GNUC_UNUSED,
- size_t *resultlen G_GNUC_UNUSED,
- Error **errp)
-{
- error_setg(errp,
- "Hash algorithm %d not supported without GNUTLS",
- alg);
- return -1;
-}
diff --git a/crypto/random-stub.c b/crypto/random-platform.c
index 63bbf41473..82b755afad 100644
--- a/crypto/random-stub.c
+++ b/crypto/random-platform.c
@@ -26,6 +26,39 @@ int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
size_t buflen G_GNUC_UNUSED,
Error **errp)
{
- error_setg(errp, "No random byte source provided in this build");
- return -1;
+ int fd;
+ int ret = -1;
+ int got;
+
+ /* TBD perhaps also add support for BSD getentropy / Linux
+ * getrandom syscalls directly */
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1 && errno == ENOENT) {
+ fd = open("/dev/random", O_RDONLY);
+ }
+
+ if (fd < 0) {
+ error_setg(errp, "No /dev/urandom or /dev/random found");
+ return -1;
+ }
+
+ while (buflen > 0) {
+ got = read(fd, buf, buflen);
+ if (got < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to read random bytes");
+ goto cleanup;
+ } else if (!got) {
+ error_setg(errp,
+ "Unexpected EOF reading random bytes");
+ goto cleanup;
+ }
+ buflen -= got;
+ buf += got;
+ }
+
+ ret = 0;
+ cleanup:
+ close(fd);
+ return ret;
}
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 48b0b31f2e..de298dcaec 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -410,7 +410,7 @@ following example objects:
=== Commands ===
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
- '*returns': TYPE-NAME,
+ '*returns': TYPE-NAME, '*boxed': true,
'*gen': false, '*success-response': false }
Commands are defined by using a dictionary containing several members,
@@ -461,6 +461,20 @@ which would validate this Client JSON Protocol transaction:
=> { "execute": "my-second-command" }
<= { "return": [ { "value": "one" }, { } ] }
+The generator emits a prototype for the user's function implementing
+the command. Normally, 'data' is a dictionary for an anonymous type,
+or names a struct type (possibly empty, but not a union), and its
+members are passed as separate arguments to this function. If the
+command definition includes a key 'boxed' with the boolean value true,
+then 'data' is instead the name of any non-empty complex type
+(struct, union, or alternate), and a pointer to that QAPI type is
+passed as a single argument.
+
+The generator also emits a marshalling function that extracts
+arguments for the user's function out of an input QDict, calls the
+user's function, and if it succeeded, builds an output QObject from
+its return value.
+
In rare cases, QAPI cannot express a type-safe representation of a
corresponding Client JSON Protocol command. You then have to suppress
generation of a marshalling function by including a key 'gen' with
@@ -484,7 +498,8 @@ use of this member.
=== Events ===
-Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT }
+Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
+ '*boxed': true }
Events are defined with the keyword 'event'. It is not allowed to
name an event 'MAX', since the generator also produces a C enumeration
@@ -505,6 +520,14 @@ Resulting in this JSON object:
"data": { "b": "test string" },
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+The generator emits a function to send the event. Normally, 'data' is
+a dictionary for an anonymous type, or names a struct type (possibly
+empty, but not a union), and its members are passed as separate
+arguments to this function. If the event definition includes a key
+'boxed' with the boolean value true, then 'data' is instead the name of
+any non-empty complex type (struct, union, or alternate), and a
+pointer to that QAPI type is passed as a single argument.
+
== Client JSON Protocol introspection ==
diff --git a/hmp.c b/hmp.c
index 3ca79c3ea3..cc2056e9e2 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1077,31 +1077,28 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)
void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
{
- const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_str(qdict, "target");
const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false);
bool full = qdict_get_try_bool(qdict, "full", false);
- enum NewImageMode mode;
Error *err = NULL;
+ DriveMirror mirror = {
+ .device = (char *)qdict_get_str(qdict, "device"),
+ .target = (char *)filename,
+ .has_format = !!format,
+ .format = (char *)format,
+ .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
+ .has_mode = true,
+ .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
+ .unmap = true,
+ };
if (!filename) {
error_setg(&err, QERR_MISSING_PARAMETER, "target");
hmp_handle_error(mon, &err);
return;
}
-
- if (reuse) {
- mode = NEW_IMAGE_MODE_EXISTING;
- } else {
- mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
- }
-
- qmp_drive_mirror(false, NULL, device, filename, !!format, format,
- false, NULL, false, NULL,
- full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
- true, mode, false, 0, false, 0, false, 0,
- false, 0, false, 0, false, true, &err);
+ qmp_drive_mirror(&mirror, &err);
hmp_handle_error(mon, &err);
}
@@ -1439,42 +1436,17 @@ void hmp_change(Monitor *mon, const QDict *qdict)
void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
+ BlockIOThrottle throttle = {
+ .device = (char *) qdict_get_str(qdict, "device"),
+ .bps = qdict_get_int(qdict, "bps"),
+ .bps_rd = qdict_get_int(qdict, "bps_rd"),
+ .bps_wr = qdict_get_int(qdict, "bps_wr"),
+ .iops = qdict_get_int(qdict, "iops"),
+ .iops_rd = qdict_get_int(qdict, "iops_rd"),
+ .iops_wr = qdict_get_int(qdict, "iops_wr"),
+ };
- qmp_block_set_io_throttle(qdict_get_str(qdict, "device"),
- qdict_get_int(qdict, "bps"),
- qdict_get_int(qdict, "bps_rd"),
- qdict_get_int(qdict, "bps_wr"),
- qdict_get_int(qdict, "iops"),
- qdict_get_int(qdict, "iops_rd"),
- qdict_get_int(qdict, "iops_wr"),
- false, /* no burst max via HMP */
- 0,
- false,
- 0,
- false,
- 0,
- false,
- 0,
- false,
- 0,
- false,
- 0,
- false, /* no burst length via HMP */
- 0,
- false,
- 0,
- false,
- 0,
- false,
- 0,
- false,
- 0,
- false,
- 0,
- false, /* No default I/O size */
- 0,
- false,
- NULL, &err);
+ qmp_block_set_io_throttle(&throttle, &err);
hmp_handle_error(mon, &err);
}
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 7a4cc07dd5..cc50ace13d 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -378,7 +378,7 @@ static void eth_cleanup(NetClientState *nc)
}
static NetClientInfo net_mv88w8618_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = eth_receive,
.cleanup = eth_cleanup,
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 90aca73121..3b8ad33fc5 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -574,9 +574,10 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
{
struct blkif_request_discard *discard_req = (void *)&ioreq->req;
ioreq->aio_inflight++;
- blk_aio_discard(blkdev->blk,
- discard_req->sector_number, discard_req->nr_sectors,
- qemu_aio_complete, ioreq);
+ blk_aio_pdiscard(blkdev->blk,
+ discard_req->sector_number << BDRV_SECTOR_BITS,
+ discard_req->nr_sectors << BDRV_SECTOR_BITS,
+ qemu_aio_complete, ioreq);
break;
}
default:
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index ab1bc5e945..2ba2504ea0 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -247,7 +247,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name,
}
queues = qemu_find_net_clients_except(str, peers,
- NET_CLIENT_OPTIONS_KIND_NIC,
+ NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
if (queues == 0) {
err = -ENOENT;
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 46cc86690c..0e2682d28b 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1816,16 +1816,17 @@ static void qxl_hw_update(void *opaque)
static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
uint32_t height, int32_t stride)
{
- uint64_t offset;
- uint32_t slot, size;
+ uint64_t offset, size;
+ uint32_t slot;
bool rc;
rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset);
assert(rc == true);
- size = height * abs(stride);
- trace_qxl_surfaces_dirty(qxl->id, (int)offset, size);
+ size = (uint64_t)height * abs(stride);
+ trace_qxl_surfaces_dirty(qxl->id, offset, size);
qxl_set_dirty(qxl->guest_slots[slot].mr,
- qxl->guest_slots[slot].offset + offset, size);
+ qxl->guest_slots[slot].offset + offset,
+ qxl->guest_slots[slot].offset + offset + size);
}
static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
diff --git a/hw/display/trace-events b/hw/display/trace-events
index 9dd82cecde..78f04657a0 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -105,7 +105,7 @@ qxl_spice_reset_image_cache(int qid) "%d"
qxl_spice_reset_memslots(int qid) "%d"
qxl_spice_update_area(int qid, uint32_t surface_id, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "%d sid=%d [%d,%d,%d,%d]"
qxl_spice_update_area_rest(int qid, uint32_t num_dirty_rects, uint32_t clear_dirty_region) "%d #d=%d clear=%d"
-qxl_surfaces_dirty(int qid, int offset, int size) "%d offset=%d size=%d"
+qxl_surfaces_dirty(int qid, uint64_t offset, uint64_t size) "%d offset=0x%"PRIx64" size=0x%"PRIx64
qxl_send_events(int qid, uint32_t events) "%d %d"
qxl_send_events_vm_stopped(int qid, uint32_t events) "%d %d"
qxl_set_guest_bug(int qid) "%d"
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index c5983c79be..2bd0de82b4 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -184,19 +184,24 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp)
{
APICCommonState *s = APIC_COMMON(dev);
- memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi",
- APIC_SPACE_SIZE);
+ memory_region_init_io(&s->io_memory, OBJECT(s), &kvm_apic_io_ops, s,
+ "kvm-apic-msi", APIC_SPACE_SIZE);
if (kvm_has_gsi_routing()) {
msi_nonbroken = true;
}
}
+static void kvm_apic_unrealize(DeviceState *dev, Error **errp)
+{
+}
+
static void kvm_apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
k->realize = kvm_apic_realize;
+ k->unrealize = kvm_apic_unrealize;
k->reset = kvm_apic_reset;
k->set_base = kvm_apic_set_base;
k->set_tpr = kvm_apic_set_tpr;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 979f36d99f..9e3c70fb23 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -471,9 +471,6 @@ void pc_cmos_init(PCMachineState *pcms,
rtc_set_memory(s, 0x5c, val >> 8);
rtc_set_memory(s, 0x5d, val >> 16);
- /* set the number of CPU */
- rtc_set_memory(s, 0x5f, smp_cpus - 1);
-
object_property_add_link(OBJECT(pcms), "rtc_state",
TYPE_ISA_DEVICE,
(Object **)&pcms->rtc,
@@ -1090,6 +1087,17 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
}
}
+static int pc_present_cpus_count(PCMachineState *pcms)
+{
+ int i, boot_cpus = 0;
+ for (i = 0; i < pcms->possible_cpus->len; i++) {
+ if (pcms->possible_cpus->cpus[i].cpu) {
+ boot_cpus++;
+ }
+ }
+ return boot_cpus;
+}
+
static X86CPU *pc_new_cpu(const char *typename, int64_t apic_id,
Error **errp)
{
@@ -1122,18 +1130,6 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
return;
}
- if (cpu_exists(apic_id)) {
- error_setg(errp, "Unable to add CPU: %" PRIi64
- ", it already exists", id);
- return;
- }
-
- if (id >= max_cpus) {
- error_setg(errp, "Unable to add CPU: %" PRIi64
- ", max allowed: %d", id, max_cpus - 1);
- return;
- }
-
if (apic_id >= ACPI_CPU_HOTPLUG_ID_LIMIT) {
error_setg(errp, "Unable to add CPU: %" PRIi64
", resulting APIC ID (%" PRIi64 ") is too large",
@@ -1208,7 +1204,6 @@ void pc_cpus_init(PCMachineState *pcms)
if (i < smp_cpus) {
cpu = pc_new_cpu(typename, x86_cpu_apic_id_from_index(i),
&error_fatal);
- pcms->possible_cpus->cpus[i].cpu = CPU(cpu);
object_unref(OBJECT(cpu));
}
}
@@ -1252,6 +1247,9 @@ void pc_machine_done(Notifier *notifier, void *data)
PCMachineState, machine_done);
PCIBus *bus = pcms->bus;
+ /* set the number of CPUs */
+ rtc_set_memory(pcms->rtc, 0x5f, pc_present_cpus_count(pcms) - 1);
+
if (bus) {
int extra_hosts = 0;
@@ -1759,39 +1757,48 @@ static int pc_apic_cmp(const void *a, const void *b)
return apic_a->arch_id - apic_b->arch_id;
}
+/* returns pointer to CPUArchId descriptor that matches CPU's apic_id
+ * in pcms->possible_cpus->cpus, if pcms->possible_cpus->cpus has no
+ * entry correponding to CPU's apic_id returns NULL.
+ */
+static CPUArchId *pc_find_cpu_slot(PCMachineState *pcms, CPUState *cpu,
+ int *idx)
+{
+ CPUClass *cc = CPU_GET_CLASS(cpu);
+ CPUArchId apic_id, *found_cpu;
+
+ apic_id.arch_id = cc->get_arch_id(CPU(cpu));
+ found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus,
+ pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus),
+ pc_apic_cmp);
+ if (found_cpu && idx) {
+ *idx = found_cpu - pcms->possible_cpus->cpus;
+ }
+ return found_cpu;
+}
+
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- CPUClass *cc = CPU_GET_CLASS(dev);
- CPUArchId apic_id, *found_cpu;
+ CPUArchId *found_cpu;
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
- if (!dev->hotplugged) {
- goto out;
- }
-
- if (!pcms->acpi_dev) {
- error_setg(&local_err,
- "cpu hotplug is not enabled: missing acpi device");
- goto out;
+ if (pcms->acpi_dev) {
+ hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
+ if (local_err) {
+ goto out;
+ }
}
- hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
- hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
- if (local_err) {
- goto out;
+ if (dev->hotplugged) {
+ /* increment the number of CPUs */
+ rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
}
- /* increment the number of CPUs */
- rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
-
- apic_id.arch_id = cc->get_arch_id(CPU(dev));
- found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus,
- pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus),
- pc_apic_cmp);
- assert(found_cpu);
+ found_cpu = pc_find_cpu_slot(pcms, CPU(dev), NULL);
found_cpu->cpu = CPU(dev);
out:
error_propagate(errp, local_err);
@@ -1799,10 +1806,35 @@ out:
static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
+ int idx = -1;
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ pc_find_cpu_slot(pcms, CPU(dev), &idx);
+ assert(idx != -1);
+ if (idx == 0) {
+ error_setg(&local_err, "Boot CPU is unpluggable");
+ goto out;
+ }
+
+ if (idx < pcms->possible_cpus->len - 1 &&
+ pcms->possible_cpus->cpus[idx + 1].cpu != NULL) {
+ X86CPU *cpu;
+
+ for (idx = pcms->possible_cpus->len - 1;
+ pcms->possible_cpus->cpus[idx].cpu == NULL; idx--) {
+ ;;
+ }
+
+ cpu = X86_CPU(pcms->possible_cpus->cpus[idx].cpu);
+ error_setg(&local_err, "CPU [socket-id: %u, core-id: %u,"
+ " thread-id: %u] should be removed first",
+ cpu->socket_id, cpu->core_id, cpu->thread_id);
+ goto out;
+
+ }
+
hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
@@ -1818,6 +1850,7 @@ static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
+ CPUArchId *found_cpu;
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
@@ -1829,17 +1862,129 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
goto out;
}
- /*
- * TODO: enable unplug once generic CPU remove bits land
- * for now guest will be able to eject CPU ACPI wise but
- * it will come back again on machine reset.
- */
- /* object_unparent(OBJECT(dev)); */
+ found_cpu = pc_find_cpu_slot(pcms, CPU(dev), NULL);
+ found_cpu->cpu = NULL;
+ object_unparent(OBJECT(dev));
+ rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) - 1);
out:
error_propagate(errp, local_err);
}
+static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ int idx;
+ CPUArchId *cpu_slot;
+ X86CPUTopoInfo topo;
+ X86CPU *cpu = X86_CPU(dev);
+ PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+
+ /* if APIC ID is not set, set it based on socket/core/thread properties */
+ if (cpu->apic_id == UNASSIGNED_APIC_ID) {
+ int max_socket = (max_cpus - 1) / smp_threads / smp_cores;
+
+ if (cpu->socket_id < 0) {
+ error_setg(errp, "CPU socket-id is not set");
+ return;
+ } else if (cpu->socket_id > max_socket) {
+ error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",
+ cpu->socket_id, max_socket);
+ return;
+ }
+ if (cpu->core_id < 0) {
+ error_setg(errp, "CPU core-id is not set");
+ return;
+ } else if (cpu->core_id > (smp_cores - 1)) {
+ error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
+ cpu->core_id, smp_cores - 1);
+ return;
+ }
+ if (cpu->thread_id < 0) {
+ error_setg(errp, "CPU thread-id is not set");
+ return;
+ } else if (cpu->thread_id > (smp_threads - 1)) {
+ error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",
+ cpu->thread_id, smp_threads - 1);
+ return;
+ }
+
+ topo.pkg_id = cpu->socket_id;
+ topo.core_id = cpu->core_id;
+ topo.smt_id = cpu->thread_id;
+ cpu->apic_id = apicid_from_topo_ids(smp_cores, smp_threads, &topo);
+ }
+
+ cpu_slot = pc_find_cpu_slot(pcms, CPU(dev), &idx);
+ if (!cpu_slot) {
+ x86_topo_ids_from_apicid(cpu->apic_id, smp_cores, smp_threads, &topo);
+ error_setg(errp, "Invalid CPU [socket: %u, core: %u, thread: %u] with"
+ " APIC ID %" PRIu32 ", valid index range 0:%d",
+ topo.pkg_id, topo.core_id, topo.smt_id, cpu->apic_id,
+ pcms->possible_cpus->len - 1);
+ return;
+ }
+
+ if (cpu_slot->cpu) {
+ error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",
+ idx, cpu->apic_id);
+ return;
+ }
+
+ if (idx != 0 && pcms->possible_cpus->cpus[idx - 1].cpu == NULL) {
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+
+ for (idx = 1; pcms->possible_cpus->cpus[idx].cpu != NULL; idx++) {
+ ;;
+ }
+
+ x86_topo_ids_from_apicid(pcms->possible_cpus->cpus[idx].arch_id,
+ smp_cores, smp_threads, &topo);
+
+ if (!pcmc->legacy_cpu_hotplug) {
+ error_setg(errp, "CPU [socket: %u, core: %u, thread: %u] should be"
+ " added first", topo.pkg_id, topo.core_id, topo.smt_id);
+ return;
+ }
+ }
+
+ /* if 'address' properties socket-id/core-id/thread-id are not set, set them
+ * so that query_hotpluggable_cpus would show correct values
+ */
+ /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
+ * once -smp refactoring is complete and there will be CPU private
+ * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
+ x86_topo_ids_from_apicid(cpu->apic_id, smp_cores, smp_threads, &topo);
+ if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) {
+ error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
+ " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, topo.pkg_id);
+ return;
+ }
+ cpu->socket_id = topo.pkg_id;
+
+ if (cpu->core_id != -1 && cpu->core_id != topo.core_id) {
+ error_setg(errp, "property core-id: %u doesn't match set apic-id:"
+ " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, topo.core_id);
+ return;
+ }
+ cpu->core_id = topo.core_id;
+
+ if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) {
+ error_setg(errp, "property thread-id: %u doesn't match set apic-id:"
+ " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, topo.smt_id);
+ return;
+ }
+ cpu->thread_id = topo.smt_id;
+}
+
+static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ pc_cpu_pre_plug(hotplug_dev, dev, errp);
+ }
+}
+
static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -2093,6 +2238,50 @@ static CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *machine)
return list;
}
+static HotpluggableCPUList *pc_query_hotpluggable_cpus(MachineState *machine)
+{
+ int i;
+ CPUState *cpu;
+ HotpluggableCPUList *head = NULL;
+ PCMachineState *pcms = PC_MACHINE(machine);
+ const char *cpu_type;
+
+ cpu = pcms->possible_cpus->cpus[0].cpu;
+ assert(cpu); /* BSP is always present */
+ cpu_type = object_class_get_name(OBJECT_CLASS(CPU_GET_CLASS(cpu)));
+
+ for (i = 0; i < pcms->possible_cpus->len; i++) {
+ X86CPUTopoInfo topo;
+ HotpluggableCPUList *list_item = g_new0(typeof(*list_item), 1);
+ HotpluggableCPU *cpu_item = g_new0(typeof(*cpu_item), 1);
+ CpuInstanceProperties *cpu_props = g_new0(typeof(*cpu_props), 1);
+ const uint32_t apic_id = pcms->possible_cpus->cpus[i].arch_id;
+
+ x86_topo_ids_from_apicid(apic_id, smp_cores, smp_threads, &topo);
+
+ cpu_item->type = g_strdup(cpu_type);
+ cpu_item->vcpus_count = 1;
+ cpu_props->has_socket_id = true;
+ cpu_props->socket_id = topo.pkg_id;
+ cpu_props->has_core_id = true;
+ cpu_props->core_id = topo.core_id;
+ cpu_props->has_thread_id = true;
+ cpu_props->thread_id = topo.smt_id;
+ cpu_item->props = cpu_props;
+
+ cpu = pcms->possible_cpus->cpus[i].cpu;
+ if (cpu) {
+ cpu_item->has_qom_path = true;
+ cpu_item->qom_path = object_get_canonical_path(OBJECT(cpu));
+ }
+
+ list_item->value = cpu_item;
+ list_item->next = head;
+ head = list_item;
+ }
+ return head;
+}
+
static void x86_nmi(NMIState *n, int cpu_index, Error **errp)
{
/* cpu index isn't used */
@@ -2133,10 +2322,12 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
mc->get_hotplug_handler = pc_get_hotpug_handler;
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
+ mc->query_hotpluggable_cpus = pc_query_hotpluggable_cpus;
mc->default_boot_order = "cad";
mc->hot_add_cpu = pc_hot_add_cpu;
mc->max_cpus = 255;
mc->reset = pc_machine_reset;
+ hc->pre_plug = pc_machine_device_pre_plug_cb;
hc->plug = pc_machine_device_plug_cb;
hc->unplug_request = pc_machine_device_unplug_request_cb;
hc->unplug = pc_machine_device_unplug_cb;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index b1daf967d6..081c9eb765 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -423,8 +423,10 @@ static void ide_issue_trim_cb(void *opaque, int ret)
}
/* Got an entry! Submit and exit. */
- iocb->aiocb = blk_aio_discard(iocb->blk, sector, count,
- ide_issue_trim_cb, opaque);
+ iocb->aiocb = blk_aio_pdiscard(iocb->blk,
+ sector << BDRV_SECTOR_BITS,
+ count << BDRV_SECTOR_BITS,
+ ide_issue_trim_cb, opaque);
return;
}
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index e1ab9354c6..45887d99c0 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -28,7 +28,9 @@
#include "trace.h"
#include "hw/i386/pc.h"
#include "hw/i386/apic-msidef.h"
+#include "qapi/error.h"
+#define MAX_APICS 255
#define MAX_APIC_WORDS 8
#define SYNC_FROM_VAPIC 0x1
@@ -419,7 +421,7 @@ static int apic_find_dest(uint8_t dest)
int i;
if (apic && apic->id == dest)
- return dest; /* shortcut in case apic->id == apic->idx */
+ return dest; /* shortcut in case apic->id == local_apics[dest]->id */
for (i = 0; i < MAX_APICS; i++) {
apic = local_apics[i];
@@ -502,14 +504,14 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
break;
case 1:
memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
- apic_set_bit(deliver_bitmask, s->idx);
+ apic_set_bit(deliver_bitmask, s->id);
break;
case 2:
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
break;
case 3:
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
- apic_reset_bit(deliver_bitmask, s->idx);
+ apic_reset_bit(deliver_bitmask, s->id);
break;
}
@@ -870,20 +872,36 @@ static void apic_realize(DeviceState *dev, Error **errp)
{
APICCommonState *s = APIC_COMMON(dev);
+ if (s->id >= MAX_APICS) {
+ error_setg(errp, "%s initialization failed. APIC ID %d is invalid",
+ object_get_typename(OBJECT(dev)), s->id);
+ return;
+ }
+
memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic-msi",
APIC_SPACE_SIZE);
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
- local_apics[s->idx] = s;
+ local_apics[s->id] = s;
msi_nonbroken = true;
}
+static void apic_unrealize(DeviceState *dev, Error **errp)
+{
+ APICCommonState *s = APIC_COMMON(dev);
+
+ timer_del(s->timer);
+ timer_free(s->timer);
+ local_apics[s->id] = NULL;
+}
+
static void apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
k->realize = apic_realize;
+ k->unrealize = apic_unrealize;
k->set_base = apic_set_base;
k->set_tpr = apic_set_tpr;
k->get_tpr = apic_get_tpr;
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index e6eb694de0..14ac43c186 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -294,19 +294,14 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+static const VMStateDescription vmstate_apic_common;
+
static void apic_common_realize(DeviceState *dev, Error **errp)
{
APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info;
static DeviceState *vapic;
- static int apic_no;
-
- if (apic_no >= MAX_APICS) {
- error_setg(errp, "%s initialization failed.",
- object_get_typename(OBJECT(dev)));
- return;
- }
- s->idx = apic_no++;
+ int instance_id = s->id;
info = APIC_COMMON_GET_CLASS(s);
info->realize(dev, errp);
@@ -321,6 +316,24 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
info->enable_tpr_reporting(s, true);
}
+ if (s->legacy_instance_id) {
+ instance_id = -1;
+ }
+ vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common,
+ s, -1, 0);
+}
+
+static void apic_common_unrealize(DeviceState *dev, Error **errp)
+{
+ APICCommonState *s = APIC_COMMON(dev);
+ APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
+
+ vmstate_unregister(NULL, &vmstate_apic_common, s);
+ info->unrealize(dev, errp);
+
+ if (apic_report_tpr_access && info->enable_tpr_reporting) {
+ info->enable_tpr_reporting(s, false);
+ }
}
static int apic_pre_load(void *opaque)
@@ -418,6 +431,8 @@ static Property apic_properties_common[] = {
DEFINE_PROP_UINT8("version", APICCommonState, version, 0x14),
DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT,
true),
+ DEFINE_PROP_BOOL("legacy-instance-id", APICCommonState, legacy_instance_id,
+ false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -425,10 +440,10 @@ static void apic_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->vmsd = &vmstate_apic_common;
dc->reset = apic_reset_common;
dc->props = apic_properties_common;
dc->realize = apic_common_realize;
+ dc->unrealize = apic_common_unrealize;
/*
* Reason: APIC and CPU need to be wired up by
* x86_cpu_apic_create()
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 2f60096e6e..77e5cfa327 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -420,6 +420,8 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
MemTxResult r;
int cpuidx;
+ assert((offset & (size - 1)) == 0);
+
/* This region covers all the redistributor pages; there are
* (for GICv3) two 64K pages per CPU. At the moment they are
* all contiguous (ie in this one region), though we might later
@@ -468,6 +470,8 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
MemTxResult r;
int cpuidx;
+ assert((offset & (size - 1)) == 0);
+
/* This region covers all the redistributor pages; there are
* (for GICv3) two 64K pages per CPU. At the moment they are
* all contiguous (ie in this one region), though we might later
diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
index d57502300c..50e8361e52 100644
--- a/hw/net/allwinner_emac.c
+++ b/hw/net/allwinner_emac.c
@@ -424,7 +424,7 @@ static const MemoryRegionOps aw_emac_mem_ops = {
};
static NetClientInfo net_aw_emac_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = aw_emac_can_receive,
.receive = aw_emac_receive,
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 8a4be1e667..db1b301e7f 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1207,7 +1207,7 @@ static void gem_set_link(NetClientState *nc)
}
static NetClientInfo net_gem_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = gem_can_receive,
.receive = gem_receive,
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
index 0fa652c392..17f0338d1c 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -812,7 +812,7 @@ static void dp8393x_reset(DeviceState *dev)
}
static NetClientInfo net_dp83932_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = dp8393x_can_receive,
.receive = dp8393x_receive,
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 06ca7b2638..93249497f4 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1563,7 +1563,7 @@ pci_e1000_uninit(PCIDevice *dev)
}
static NetClientInfo net_e1000_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = e1000_can_receive,
.receive = e1000_receive,
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index b4758bc441..d001c96668 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -225,7 +225,7 @@ e1000e_set_link_status(NetClientState *nc)
}
static NetClientInfo net_e1000e_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = e1000e_nc_can_receive,
.receive = e1000e_nc_receive,
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index b10c419838..bab4dbfc98 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -1848,7 +1848,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
}
static NetClientInfo net_eepro100_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = nic_receive,
};
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index 05495ec405..efaa49faae 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -578,7 +578,7 @@ static const MemoryRegionOps eth_ops = {
};
static NetClientInfo net_etraxfs_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = eth_receive,
.link_status_changed = eth_set_link,
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index 98250e0591..b5c777fbf6 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -371,7 +371,7 @@ static void etsec_set_link_status(NetClientState *nc)
}
static NetClientInfo net_etsec_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = etsec_receive,
.link_status_changed = etsec_set_link_status,
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index d91e029382..1c415ab3b1 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -1147,7 +1147,7 @@ static void imx_eth_cleanup(NetClientState *nc)
}
static NetClientInfo imx_eth_net_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = imx_eth_can_receive,
.receive = imx_eth_receive,
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index 205207356c..4615d873b1 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -1313,7 +1313,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
};
static NetClientInfo net_lan9118_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = lan9118_receive,
.link_status_changed = lan9118_set_link,
diff --git a/hw/net/lance.c b/hw/net/lance.c
index 6253d2103d..573d724bcf 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -93,7 +93,7 @@ static const MemoryRegionOps lance_mem_ops = {
};
static NetClientInfo net_lance_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = pcnet_receive,
.link_status_changed = pcnet_set_link_status,
diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
index 7c0398ed99..0ee8ad9d66 100644
--- a/hw/net/mcf_fec.c
+++ b/hw/net/mcf_fec.c
@@ -507,7 +507,7 @@ static const MemoryRegionOps mcf_fec_ops = {
};
static NetClientInfo net_mcf_fec_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = mcf_fec_receive,
};
diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c
index 1e147c33c5..c3a12e1197 100644
--- a/hw/net/milkymist-minimac2.c
+++ b/hw/net/milkymist-minimac2.c
@@ -447,7 +447,7 @@ static void milkymist_minimac2_reset(DeviceState *d)
}
static NetClientInfo net_milkymist_minimac2_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = minimac2_rx,
};
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
index 5115adcaea..5a63df7ccb 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -224,7 +224,7 @@ static const VMStateDescription vmstate_mipsnet = {
};
static NetClientInfo net_mipsnet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = mipsnet_receive,
};
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 8fab7ae2ec..f3455339ee 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -44,7 +44,7 @@ typedef struct ISANE2000State {
} ISANE2000State;
static NetClientInfo net_ne2000_isa_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = ne2000_receive,
};
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index f0feaf96b0..798d681e25 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -712,7 +712,7 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
}
static NetClientInfo net_ne2000_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = ne2000_receive,
};
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
index 484d113eb6..268d6a7892 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -473,7 +473,7 @@ static ssize_t open_eth_receive(NetClientState *nc,
}
static NetClientInfo net_open_eth_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = open_eth_can_receive,
.receive = open_eth_receive,
diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c
index 595439a65b..0acf8a4879 100644
--- a/hw/net/pcnet-pci.c
+++ b/hw/net/pcnet-pci.c
@@ -272,7 +272,7 @@ static void pci_pcnet_uninit(PCIDevice *dev)
}
static NetClientInfo net_pci_pcnet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = pcnet_receive,
.link_status_changed = pcnet_set_link_status,
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
index 0149899c62..1305ac36cc 100644
--- a/hw/net/rocker/rocker_fp.c
+++ b/hw/net/rocker/rocker_fp.c
@@ -167,7 +167,7 @@ static void fp_port_set_link_status(NetClientState *nc)
}
static NetClientInfo fp_port_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = fp_port_receive,
.receive_iov = fp_port_receive_iov,
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 07297cb78f..3345bc6b5e 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3393,7 +3393,7 @@ static void rtl8139_set_link_status(NetClientState *nc)
}
static NetClientInfo net_rtl8139_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = rtl8139_can_receive,
.receive = rtl8139_receive,
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
index 21c1b8f54b..3b16dcf5a1 100644
--- a/hw/net/smc91c111.c
+++ b/hw/net/smc91c111.c
@@ -755,7 +755,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
};
static NetClientInfo net_smc91c111_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = smc91c111_can_receive_nc,
.receive = smc91c111_receive,
diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
index 8b2eebd4e3..b273eda933 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -278,7 +278,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
}
static NetClientInfo net_spapr_vlan_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = spapr_vlan_can_receive,
.receive = spapr_vlan_receive,
diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index 6880894945..957730e023 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -460,7 +460,7 @@ static void stellaris_enet_reset(stellaris_enet_state *s)
}
static NetClientInfo net_stellaris_enet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = stellaris_enet_receive,
};
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 11fabc0b0a..f92d3f829e 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -88,10 +88,10 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
const int *feature_bits = 0;
switch (net->nc->info->type) {
- case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_DRIVER_TAP:
feature_bits = kernel_feature_bits;
break;
- case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+ case NET_CLIENT_DRIVER_VHOST_USER:
feature_bits = user_feature_bits;
break;
default:
@@ -128,7 +128,7 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net)
static int vhost_net_get_fd(NetClientState *backend)
{
switch (backend->info->type) {
- case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_DRIVER_TAP:
return tap_get_fd(backend);
default:
fprintf(stderr, "vhost-net requires tap backend\n");
@@ -191,7 +191,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
}
/* Set sane init value. Override when guest acks. */
- if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
features = vhost_user_get_acked_features(net->nc);
if (~net->dev.features & features) {
fprintf(stderr, "vhost lacks feature mask %" PRIu64
@@ -238,7 +238,7 @@ static int vhost_net_start_one(struct vhost_net *net,
net->nc->info->poll(net->nc, false);
}
- if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
file.fd = net->backend;
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
@@ -253,7 +253,7 @@ static int vhost_net_start_one(struct vhost_net *net,
return 0;
fail:
file.fd = -1;
- if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
while (file.index-- > 0) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
@@ -275,7 +275,7 @@ static void vhost_net_stop_one(struct vhost_net *net,
{
struct vhost_vring_file file = { .fd = -1 };
- if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
@@ -312,7 +312,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
* because vhost user doesn't interrupt masking/unmasking
* properly.
*/
- if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
dev->use_guest_notifier_mask = false;
}
}
@@ -413,10 +413,10 @@ VHostNetState *get_vhost_net(NetClientState *nc)
}
switch (nc->info->type) {
- case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_DRIVER_TAP:
vhost_net = tap_get_vhost_net(nc);
break;
- case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+ case NET_CLIENT_DRIVER_VHOST_USER:
vhost_net = vhost_user_get_vhost_net(nc);
break;
default:
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index bb311c4587..01f1351554 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -468,11 +468,11 @@ static int peer_attach(VirtIONet *n, int index)
return 0;
}
- if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
vhost_set_vring_enable(nc->peer, 1);
}
- if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
return 0;
}
@@ -487,11 +487,11 @@ static int peer_detach(VirtIONet *n, int index)
return 0;
}
- if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
vhost_set_vring_enable(nc->peer, 0);
}
- if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
return 0;
}
@@ -1658,7 +1658,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
}
static NetClientInfo net_virtio_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = virtio_net_can_receive,
.receive = virtio_net_receive,
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index e767fc64b8..bbf44adbcc 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2087,7 +2087,7 @@ static void vmxnet3_set_link_status(NetClientState *nc)
}
static NetClientInfo net_vmxnet3_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = vmxnet3_receive,
.link_status_changed = vmxnet3_set_link_status,
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 0b4ddae48c..6856b52999 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -269,7 +269,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
/* ------------------------------------------------------------- */
static NetClientInfo net_xen_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = net_rx_packet,
};
diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
index 0c5f793bd2..46b1aa17fa 100644
--- a/hw/net/xgmac.c
+++ b/hw/net/xgmac.c
@@ -371,7 +371,7 @@ out:
}
static NetClientInfo net_xgmac_enet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = eth_rx,
};
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index de23ab5dcd..b6701844d3 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -935,7 +935,7 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
}
static NetClientInfo net_xilinx_enet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = eth_rx,
};
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index bc846e7096..54db2b83bd 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -217,7 +217,7 @@ static void xilinx_ethlite_reset(DeviceState *dev)
}
static NetClientInfo net_xilinx_ethlite_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = eth_can_rx,
.receive = eth_rx,
diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c
index e4c24e21f3..9a7f7ee60c 100644
--- a/hw/s390x/css-bridge.c
+++ b/hw/s390x/css-bridge.c
@@ -59,11 +59,28 @@ static void virtual_css_bus_reset(BusState *qbus)
css_reset();
}
+static char *virtual_css_bus_get_dev_path(DeviceState *dev)
+{
+ CcwDevice *ccw_dev = CCW_DEVICE(dev);
+ SubchDev *sch = ccw_dev->sch;
+ VirtualCssBridge *bridge =
+ VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent);
+
+ /*
+ * We can't provide a dev path for backward compatibility on
+ * older machines, as it is visible in the migration stream.
+ */
+ return bridge->css_dev_path ?
+ g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno) :
+ NULL;
+}
+
static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
k->reset = virtual_css_bus_reset;
+ k->get_dev_path = virtual_css_bus_get_dev_path;
}
static const TypeInfo virtual_css_bus_info = {
@@ -95,6 +112,12 @@ VirtualCssBus *virtual_css_bus_init(void)
/***************** Virtual-css Bus Bridge Device ********************/
+static Property virtual_css_bridge_properties[] = {
+ DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path,
+ true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
{
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
@@ -102,12 +125,13 @@ static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
hc->unplug = ccw_device_unplug;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->props = virtual_css_bridge_properties;
}
static const TypeInfo virtual_css_bridge_info = {
.name = TYPE_VIRTUAL_CSS_BRIDGE,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SysBusDevice),
+ .instance_size = sizeof(VirtualCssBridge),
.class_init = virtual_css_bridge_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index aa61773885..bb8e4be339 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -511,6 +511,7 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
path = 0x80;
if (!(s->ctrl & SCSW_ACTL_SUSP)) {
+ /* Start Function triggered via ssch, i.e. we have an ORB */
s->cstat = 0;
s->dstat = 0;
/* Look at the orb and try to execute the channel program. */
@@ -524,9 +525,12 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
return;
}
sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT);
+ s->flags |= (sch->ccw_fmt_1) ? SCSW_FLAGS_MASK_FMT : 0;
sch->ccw_no_data_cnt = 0;
suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND);
} else {
+ /* Start Function resumed via rsch, i.e. we don't have an
+ * ORB */
s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
/* The channel program had been suspended before. */
suspend_allowed = true;
@@ -609,6 +613,7 @@ static void do_subchannel_work(SubchDev *sch, ORB *orb)
} else if (s->ctrl & SCSW_FCTL_HALT_FUNC) {
sch_handle_halt_func(sch);
} else if (s->ctrl & SCSW_FCTL_START_FUNC) {
+ /* Triggered by both ssch and rsch. */
sch_handle_start_func(sch, orb);
} else {
/* Cannot happen. */
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index caf0a682a7..91d9cefbb5 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -322,6 +322,10 @@ static const TypeInfo ccw_machine_info = {
.driver = TYPE_S390_IPL,\
.property = "iplbext_migration",\
.value = "off",\
+ }, {\
+ .driver = TYPE_VIRTUAL_CSS_BRIDGE,\
+ .property = "css_dev_path",\
+ .value = "off",\
},
#define CCW_COMPAT_2_5 \
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 8dbfc10b78..836a1553ed 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1609,10 +1609,10 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
goto done;
}
- r->req.aiocb = blk_aio_discard(s->qdev.conf.blk,
- sector_num * (s->qdev.blocksize / 512),
- nb_sectors * (s->qdev.blocksize / 512),
- scsi_unmap_complete, data);
+ r->req.aiocb = blk_aio_pdiscard(s->qdev.conf.blk,
+ sector_num * s->qdev.blocksize,
+ nb_sectors * s->qdev.blocksize,
+ scsi_unmap_complete, data);
data->count--;
data->inbuf += 16;
return;
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 5c18198f55..c0f1193ba9 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1334,7 +1334,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
}
static NetClientInfo net_usbnet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = usbnet_receive,
.cleanup = usbnet_cleanup,
@@ -1396,7 +1396,7 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
qemu_opt_set(opts, "type", "nic", &error_abort);
qemu_opt_set(opts, "model", "usb", &error_abort);
- idx = net_client_init(opts, 0, &local_err);
+ idx = net_client_init(opts, false, &local_err);
if (local_err) {
error_report_err(local_err);
return NULL;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 976bfb0659..188f95416a 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2201,7 +2201,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
xfer->trb_count = length;
for (i = 0; i < length; i++) {
- assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL));
+ TRBType type;
+ type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL);
+ assert(type);
}
xfer->streamid = streamid;
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 8ec8484349..444672a000 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -109,6 +109,7 @@ struct USBRedirDevice {
uint8_t debug;
char *filter_str;
int32_t bootindex;
+ bool enable_streams;
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
const uint8_t *read_buf;
int read_buf_size;
@@ -1229,7 +1230,9 @@ static void usbredir_create_parser(USBRedirDevice *dev)
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
#if USBREDIR_VERSION >= 0x000700
- usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+ if (dev->enable_streams) {
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+ }
#endif
if (runstate_check(RUN_STATE_INMIGRATE)) {
@@ -2476,6 +2479,7 @@ static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning),
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+ DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/include/block/block.h b/include/block/block.h
index 616d8b9f12..11c162d594 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -316,9 +316,9 @@ BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num,
BlockCompletionFunc *cb, void *opaque);
BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
BlockCompletionFunc *cb, void *opaque);
-BlockAIOCB *bdrv_aio_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors,
- BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs,
+ int64_t offset, int count,
+ BlockCompletionFunc *cb, void *opaque);
void bdrv_aio_cancel(BlockAIOCB *acb);
void bdrv_aio_cancel_async(BlockAIOCB *acb);
@@ -341,8 +341,8 @@ void bdrv_drain(BlockDriverState *bs);
void coroutine_fn bdrv_co_drain(BlockDriverState *bs);
void bdrv_drain_all(void);
-int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
-int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
+int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count);
+int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int count);
int bdrv_has_zero_init_1(BlockDriverState *bs);
int bdrv_has_zero_init(BlockDriverState *bs);
bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index a6b13adb45..1fe0fd9ff6 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -142,8 +142,8 @@ struct BlockDriver {
BlockCompletionFunc *cb, void *opaque);
BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
BlockCompletionFunc *cb, void *opaque);
- BlockAIOCB *(*bdrv_aio_discard)(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors,
+ BlockAIOCB *(*bdrv_aio_pdiscard)(BlockDriverState *bs,
+ int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque);
int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs,
@@ -165,8 +165,8 @@ struct BlockDriver {
*/
int coroutine_fn (*bdrv_co_pwrite_zeroes)(BlockDriverState *bs,
int64_t offset, int count, BdrvRequestFlags flags);
- int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors);
+ int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs,
+ int64_t offset, int count);
int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum,
BlockDriverState **file);
@@ -783,7 +783,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force);
bool blk_dev_is_tray_open(BlockBackend *blk);
bool blk_dev_is_medium_locked(BlockBackend *blk);
-void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
+void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int64_t nr_sect);
bool bdrv_requests_pending(BlockDriverState *bs);
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
index 80afe603f6..ee3388f90d 100644
--- a/include/block/dirty-bitmap.h
+++ b/include/block/dirty-bitmap.h
@@ -33,9 +33,9 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap);
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
int64_t sector);
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
- int64_t cur_sector, int nr_sectors);
+ int64_t cur_sector, int64_t nr_sectors);
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
- int64_t cur_sector, int nr_sectors);
+ int64_t cur_sector, int64_t nr_sectors);
void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
diff --git a/include/block/nbd.h b/include/block/nbd.h
index eeda3ebdf7..cb91820f38 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -77,7 +77,6 @@ enum {
/* Maximum size of a single READ/WRITE data buffer */
#define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024)
-#define NBD_MAX_SECTORS (NBD_MAX_BUFFER_SIZE / BDRV_SECTOR_SIZE)
/* Maximum size of an export name. The NBD spec requires 256 and
* suggests that servers support up to 4096, but we stick to only the
@@ -89,7 +88,6 @@ enum {
ssize_t nbd_wr_syncv(QIOChannel *ioc,
struct iovec *iov,
size_t niov,
- size_t offset,
size_t length,
bool do_read);
int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags,
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 73ce71674f..06c4e9f6f9 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -121,8 +121,6 @@
#define VAPIC_ENABLE_BIT 0
#define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT)
-#define MAX_APICS 255
-
typedef struct APICCommonState APICCommonState;
#define TYPE_APIC_COMMON "apic-common"
@@ -138,6 +136,7 @@ typedef struct APICCommonClass
DeviceClass parent_class;
DeviceRealize realize;
+ DeviceUnrealize unrealize;
void (*set_base)(APICCommonState *s, uint64_t val);
void (*set_tpr)(APICCommonState *s, uint8_t val);
uint8_t (*get_tpr)(APICCommonState *s);
@@ -176,7 +175,6 @@ struct APICCommonState {
uint32_t initial_count;
int64_t initial_count_load_time;
int64_t next_time;
- int idx;
QEMUTimer *timer;
int64_t timer_expiry;
int sipi_vector;
@@ -185,6 +183,7 @@ struct APICCommonState {
uint32_t vapic_control;
DeviceState *vapic;
hwaddr vapic_paddr; /* note: persistence via kvmvapic */
+ bool legacy_instance_id;
};
typedef struct VAPICState {
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 9811125492..c87c5c1eec 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -381,6 +381,16 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
.driver = "vmxnet3",\
.property = "romfile",\
.value = "",\
+ },\
+ {\
+ .driver = TYPE_X86_CPU,\
+ .property = "fill-mtrr-mask",\
+ .value = "off",\
+ },\
+ {\
+ .driver = "apic",\
+ .property = "legacy-instance-id",\
+ .value = "on",\
},
#define PC_COMPAT_2_5 \
diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h
index fc95572394..1ebaee0f76 100644
--- a/include/hw/i386/topology.h
+++ b/include/hw/i386/topology.h
@@ -117,6 +117,21 @@ static inline void x86_topo_ids_from_idx(unsigned nr_cores,
topo->pkg_id = core_index / nr_cores;
}
+/* Calculate thread/core/package IDs for a specific topology,
+ * based on APIC ID
+ */
+static inline void x86_topo_ids_from_apicid(apic_id_t apicid,
+ unsigned nr_cores,
+ unsigned nr_threads,
+ X86CPUTopoInfo *topo)
+{
+ topo->smt_id = apicid &
+ ~(0xFFFFFFFFUL << apicid_smt_width(nr_cores, nr_threads));
+ topo->core_id = (apicid >> apicid_core_offset(nr_cores, nr_threads)) &
+ ~(0xFFFFFFFFUL << apicid_core_width(nr_cores, nr_threads));
+ topo->pkg_id = apicid >> apicid_pkg_offset(nr_cores, nr_threads);
+}
+
/* Make APIC ID for the CPU 'cpu_index'
*
* 'cpu_index' is a sequential, contiguous ID for the CPU.
diff --git a/include/hw/s390x/css-bridge.h b/include/hw/s390x/css-bridge.h
index ad73c1faf6..5a0203be5f 100644
--- a/include/hw/s390x/css-bridge.h
+++ b/include/hw/s390x/css-bridge.h
@@ -16,7 +16,14 @@
#include "hw/qdev-core.h"
/* virtual css bridge */
+typedef struct VirtualCssBridge {
+ SysBusDevice sysbus_dev;
+ bool css_dev_path;
+} VirtualCssBridge;
+
#define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge"
+#define VIRTUAL_CSS_BRIDGE(obj) \
+ OBJECT_CHECK(VirtualCssBridge, (obj), TYPE_VIRTUAL_CSS_BRIDGE)
/* virtual css bus type */
typedef struct VirtualCssBus {
diff --git a/include/net/net.h b/include/net/net.h
index a5c5095154..e8d9e9e4e9 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -66,7 +66,7 @@ typedef struct SocketReadState SocketReadState;
typedef void (SocketReadStateFinalize)(SocketReadState *rs);
typedef struct NetClientInfo {
- NetClientOptionsKind type;
+ NetClientDriver type;
size_t size;
NetReceive *receive;
NetReceive *receive_raw;
@@ -122,7 +122,7 @@ int net_fill_rstate(SocketReadState *rs, const uint8_t *buf, int size);
char *qemu_mac_strdup_printf(const uint8_t *macaddr);
NetClientState *qemu_find_netdev(const char *id);
int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
- NetClientOptionsKind type, int max);
+ NetClientDriver type, int max);
NetClientState *qemu_new_net_client(NetClientInfo *info,
NetClientState *peer,
const char *model,
@@ -203,7 +203,7 @@ extern const char *host_net_devices[];
extern const char *legacy_tftp_prefix;
extern const char *legacy_bootp_filename;
-int net_client_init(QemuOpts *opts, int is_netdev, Error **errp);
+int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp);
int net_client_parse(QemuOptsList *opts_list, const char *str);
int net_init_clients(void);
void net_check_clients(void);
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 3c3e82f05d..2da4905d18 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -139,15 +139,14 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset,
BlockCompletionFunc *cb, void *opaque);
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque);
-BlockAIOCB *blk_aio_discard(BlockBackend *blk,
- int64_t sector_num, int nb_sectors,
- BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int count,
+ BlockCompletionFunc *cb, void *opaque);
void blk_aio_cancel(BlockAIOCB *acb);
void blk_aio_cancel_async(BlockAIOCB *acb);
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf);
BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
BlockCompletionFunc *cb, void *opaque);
-int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors);
+int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int count);
int blk_co_flush(BlockBackend *blk);
int blk_flush(BlockBackend *blk);
int blk_flush_all(void);
@@ -207,7 +206,7 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
int blk_write_compressed(BlockBackend *blk, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
int blk_truncate(BlockBackend *blk, int64_t offset);
-int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors);
+int blk_pdiscard(BlockBackend *blk, int64_t offset, int count);
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
int64_t pos, int size);
int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size);
diff --git a/linux-user/aarch64/syscall_nr.h b/linux-user/aarch64/syscall_nr.h
index 59511d855d..a3c9a3b679 100644
--- a/linux-user/aarch64/syscall_nr.h
+++ b/linux-user/aarch64/syscall_nr.h
@@ -86,8 +86,7 @@
#define TARGET_NR_sync 81
#define TARGET_NR_fsync 82
#define TARGET_NR_fdatasync 83
-#define TARGET_NR_sync_file_range2 84
-/* #define TARGET_NR_sync_file_range 84 */
+#define TARGET_NR_sync_file_range 84
#define TARGET_NR_timerfd_create 85
#define TARGET_NR_timerfd_settime 86
#define TARGET_NR_timerfd_gettime 87
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index e672655100..7e2c133ba1 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -76,10 +76,39 @@
IOCTL(BLKFLSBUF, 0, TYPE_NULL)
IOCTL(BLKRASET, 0, TYPE_INT)
IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG))
- IOCTL(BLKSSZGET, IOC_R, MK_PTR(TYPE_LONG))
+ IOCTL(BLKSSZGET, IOC_R, MK_PTR(TYPE_INT))
IOCTL(BLKBSZGET, IOC_R, MK_PTR(TYPE_INT))
IOCTL_SPECIAL(BLKPG, IOC_W, do_ioctl_blkpg,
MK_PTR(MK_STRUCT(STRUCT_blkpg_ioctl_arg)))
+
+#ifdef BLKDISCARD
+ IOCTL(BLKDISCARD, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2)))
+#endif
+#ifdef BLKIOMIN
+ IOCTL(BLKIOMIN, IOC_R, MK_PTR(TYPE_INT))
+#endif
+#ifdef BLKIOOPT
+ IOCTL(BLKIOOPT, IOC_R, MK_PTR(TYPE_INT))
+#endif
+#ifdef BLKALIGNOFF
+ IOCTL(BLKALIGNOFF, IOC_R, MK_PTR(TYPE_INT))
+#endif
+#ifdef BLKPBSZGET
+ IOCTL(BLKPBSZGET, IOC_R, MK_PTR(TYPE_INT))
+#endif
+#ifdef BLKDISCARDZEROES
+ IOCTL(BLKDISCARDZEROES, IOC_R, MK_PTR(TYPE_INT))
+#endif
+#ifdef BLKSECDISCARD
+ IOCTL(BLKSECDISCARD, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2)))
+#endif
+#ifdef BLKROTATIONAL
+ IOCTL(BLKROTATIONAL, IOC_R, MK_PTR(TYPE_SHORT))
+#endif
+#ifdef BLKZEROOUT
+ IOCTL(BLKZEROOUT, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2)))
+#endif
+
#ifdef FIBMAP
IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG))
#endif
@@ -91,7 +120,7 @@
MK_PTR(MK_STRUCT(STRUCT_fiemap)))
#endif
- IOCTL(SIOCATMARK, 0, TYPE_NULL)
+ IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT))
IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
IOCTL(SIOCSIFFLAGS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
@@ -322,11 +351,15 @@
IOCTL(LOOP_SET_FD, 0, TYPE_INT)
IOCTL(LOOP_CLR_FD, 0, TYPE_INT)
IOCTL(LOOP_SET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
- IOCTL(LOOP_GET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
+ IOCTL(LOOP_GET_STATUS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
IOCTL(LOOP_SET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
- IOCTL(LOOP_GET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
+ IOCTL(LOOP_GET_STATUS64, IOC_R, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
IOCTL(LOOP_CHANGE_FD, 0, TYPE_INT)
+ IOCTL(LOOP_CTL_ADD, 0, TYPE_INT)
+ IOCTL(LOOP_CTL_REMOVE, 0, TYPE_INT)
+ IOCTL(LOOP_CTL_GET_FREE, 0, TYPE_NULL)
+
IOCTL(MTIOCTOP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_mtop)))
IOCTL(MTIOCGET, IOC_R, MK_PTR(MK_STRUCT(STRUCT_mtget)))
IOCTL(MTIOCPOS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_mtpos)))
diff --git a/linux-user/linux_loop.h b/linux-user/linux_loop.h
index 1f52403814..c69fea11e4 100644
--- a/linux-user/linux_loop.h
+++ b/linux-user/linux_loop.h
@@ -1,4 +1,6 @@
-/* Copied from 2.6.25 kernel headers to avoid problems on older hosts. */
+/* Copied from 2.6.25 kernel headers to avoid problems on older hosts,
+ * and subsequently updated to match newer additions to the API.
+ */
#ifndef LINUX_LOOP_H
#define LINUX_LOOP_H
@@ -92,5 +94,12 @@ struct loop_info64 {
#define LOOP_SET_STATUS64 0x4C04
#define LOOP_GET_STATUS64 0x4C05
#define LOOP_CHANGE_FD 0x4C06
+#define LOOP_SET_CAPACITY 0x4C07
+#define LOOP_SET_DIRECT_IO 0x4C08
+
+/* /dev/loop-control interface */
+#define LOOP_CTL_ADD 0x4C80
+#define LOOP_CTL_REMOVE 0x4C81
+#define LOOP_CTL_GET_FREE 0x4C82
#endif
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 9d980456ec..9a4d894e3a 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -5826,7 +5826,8 @@ long do_rt_sigreturn(CPUArchState *env)
#endif
-static void handle_pending_signal(CPUArchState *cpu_env, int sig)
+static void handle_pending_signal(CPUArchState *cpu_env, int sig,
+ struct emulated_sigtable *k)
{
CPUState *cpu = ENV_GET_CPU(cpu_env);
abi_ulong handler;
@@ -5834,7 +5835,6 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig)
target_sigset_t target_old_set;
struct target_sigaction *sa;
TaskState *ts = cpu->opaque;
- struct emulated_sigtable *k = &ts->sigtab[sig - 1];
trace_user_handle_signal(cpu_env, sig);
/* dequeue signal */
@@ -5937,7 +5937,7 @@ void process_pending_signals(CPUArchState *cpu_env)
sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL;
}
- handle_pending_signal(cpu_env, sig);
+ handle_pending_signal(cpu_env, sig, &ts->sync_signal);
}
for (sig = 1; sig <= TARGET_NSIG; sig++) {
@@ -5947,7 +5947,7 @@ void process_pending_signals(CPUArchState *cpu_env)
if (ts->sigtab[sig - 1].pending &&
(!sigismember(blocked_set,
target_to_host_signal_table[sig]))) {
- handle_pending_signal(cpu_env, sig);
+ handle_pending_signal(cpu_env, sig, &ts->sigtab[sig - 1]);
/* Restart scan from the beginning */
sig = 1;
}
diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h
index 732b1052a4..e713c9d5f4 100644
--- a/linux-user/sparc/syscall_nr.h
+++ b/linux-user/sparc/syscall_nr.h
@@ -179,6 +179,9 @@
#define TARGET_NR_readahead 205 /* Linux Specific */
#define TARGET_NR_socketcall 206 /* Linux Specific */
#define TARGET_NR_syslog 207 /* Linux Specific */
+#define TARGET_NR_lookup_dcookie 208 /* Linux Specific */
+#define TARGET_NR_fadvise64 209 /* Linux Specific */
+#define TARGET_NR_fadvise64_64 210 /* Linux Specific */
#define TARGET_NR_tgkill 211 /* Linux Specific */
#define TARGET_NR_waitpid 212 /* Linux Specific */
#define TARGET_NR_swapoff 213 /* Linux Specific */
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 8bf6205dc2..ca6a2b495a 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -100,9 +100,11 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
#include <linux/route.h>
#include <linux/filter.h>
#include <linux/blkpg.h>
+#include <netpacket/packet.h>
#include <linux/netlink.h>
#ifdef CONFIG_RTNETLINK
#include <linux/rtnetlink.h>
+#include <linux/if_bridge.h>
#endif
#include <linux/audit.h>
#include "linux_loop.h"
@@ -1374,15 +1376,26 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr,
{
struct target_sockaddr *target_saddr;
+ if (len == 0) {
+ return 0;
+ }
+
target_saddr = lock_user(VERIFY_WRITE, target_addr, len, 0);
if (!target_saddr)
return -TARGET_EFAULT;
memcpy(target_saddr, addr, len);
- target_saddr->sa_family = tswap16(addr->sa_family);
- if (addr->sa_family == AF_NETLINK) {
+ if (len >= offsetof(struct target_sockaddr, sa_family) +
+ sizeof(target_saddr->sa_family)) {
+ target_saddr->sa_family = tswap16(addr->sa_family);
+ }
+ if (addr->sa_family == AF_NETLINK && len >= sizeof(struct sockaddr_nl)) {
struct sockaddr_nl *target_nl = (struct sockaddr_nl *)target_saddr;
target_nl->nl_pid = tswap32(target_nl->nl_pid);
target_nl->nl_groups = tswap32(target_nl->nl_groups);
+ } else if (addr->sa_family == AF_PACKET) {
+ struct sockaddr_ll *target_ll = (struct sockaddr_ll *)target_saddr;
+ target_ll->sll_ifindex = tswap32(target_ll->sll_ifindex);
+ target_ll->sll_hatype = tswap16(target_ll->sll_hatype);
}
unlock_user(target_saddr, target_addr, len);
@@ -1707,6 +1720,33 @@ static abi_long target_to_host_for_each_nlmsg(struct nlmsghdr *nlh,
}
#ifdef CONFIG_RTNETLINK
+static abi_long host_to_target_for_each_nlattr(struct nlattr *nlattr,
+ size_t len, void *context,
+ abi_long (*host_to_target_nlattr)
+ (struct nlattr *,
+ void *context))
+{
+ unsigned short nla_len;
+ abi_long ret;
+
+ while (len > sizeof(struct nlattr)) {
+ nla_len = nlattr->nla_len;
+ if (nla_len < sizeof(struct nlattr) ||
+ nla_len > len) {
+ break;
+ }
+ ret = host_to_target_nlattr(nlattr, context);
+ nlattr->nla_len = tswap16(nlattr->nla_len);
+ nlattr->nla_type = tswap16(nlattr->nla_type);
+ if (ret < 0) {
+ return ret;
+ }
+ len -= NLA_ALIGN(nla_len);
+ nlattr = (struct nlattr *)(((char *)nlattr) + NLA_ALIGN(nla_len));
+ }
+ return 0;
+}
+
static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr,
size_t len,
abi_long (*host_to_target_rtattr)
@@ -1733,12 +1773,292 @@ static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr,
return 0;
}
+#define NLA_DATA(nla) ((void *)((char *)(nla)) + NLA_HDRLEN)
+
+static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr,
+ void *context)
+{
+ uint16_t *u16;
+ uint32_t *u32;
+ uint64_t *u64;
+
+ switch (nlattr->nla_type) {
+ /* no data */
+ case IFLA_BR_FDB_FLUSH:
+ break;
+ /* binary */
+ case IFLA_BR_GROUP_ADDR:
+ break;
+ /* uint8_t */
+ case IFLA_BR_VLAN_FILTERING:
+ case IFLA_BR_TOPOLOGY_CHANGE:
+ case IFLA_BR_TOPOLOGY_CHANGE_DETECTED:
+ case IFLA_BR_MCAST_ROUTER:
+ case IFLA_BR_MCAST_SNOOPING:
+ case IFLA_BR_MCAST_QUERY_USE_IFADDR:
+ case IFLA_BR_MCAST_QUERIER:
+ case IFLA_BR_NF_CALL_IPTABLES:
+ case IFLA_BR_NF_CALL_IP6TABLES:
+ case IFLA_BR_NF_CALL_ARPTABLES:
+ break;
+ /* uint16_t */
+ case IFLA_BR_PRIORITY:
+ case IFLA_BR_VLAN_PROTOCOL:
+ case IFLA_BR_GROUP_FWD_MASK:
+ case IFLA_BR_ROOT_PORT:
+ case IFLA_BR_VLAN_DEFAULT_PVID:
+ u16 = NLA_DATA(nlattr);
+ *u16 = tswap16(*u16);
+ break;
+ /* uint32_t */
+ case IFLA_BR_FORWARD_DELAY:
+ case IFLA_BR_HELLO_TIME:
+ case IFLA_BR_MAX_AGE:
+ case IFLA_BR_AGEING_TIME:
+ case IFLA_BR_STP_STATE:
+ case IFLA_BR_ROOT_PATH_COST:
+ case IFLA_BR_MCAST_HASH_ELASTICITY:
+ case IFLA_BR_MCAST_HASH_MAX:
+ case IFLA_BR_MCAST_LAST_MEMBER_CNT:
+ case IFLA_BR_MCAST_STARTUP_QUERY_CNT:
+ u32 = NLA_DATA(nlattr);
+ *u32 = tswap32(*u32);
+ break;
+ /* uint64_t */
+ case IFLA_BR_HELLO_TIMER:
+ case IFLA_BR_TCN_TIMER:
+ case IFLA_BR_GC_TIMER:
+ case IFLA_BR_TOPOLOGY_CHANGE_TIMER:
+ case IFLA_BR_MCAST_LAST_MEMBER_INTVL:
+ case IFLA_BR_MCAST_MEMBERSHIP_INTVL:
+ case IFLA_BR_MCAST_QUERIER_INTVL:
+ case IFLA_BR_MCAST_QUERY_INTVL:
+ case IFLA_BR_MCAST_QUERY_RESPONSE_INTVL:
+ case IFLA_BR_MCAST_STARTUP_QUERY_INTVL:
+ u64 = NLA_DATA(nlattr);
+ *u64 = tswap64(*u64);
+ break;
+ /* ifla_bridge_id: uin8_t[] */
+ case IFLA_BR_ROOT_ID:
+ case IFLA_BR_BRIDGE_ID:
+ break;
+ default:
+ gemu_log("Unknown IFLA_BR type %d\n", nlattr->nla_type);
+ break;
+ }
+ return 0;
+}
+
+static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr,
+ void *context)
+{
+ uint16_t *u16;
+ uint32_t *u32;
+ uint64_t *u64;
+
+ switch (nlattr->nla_type) {
+ /* uint8_t */
+ case IFLA_BRPORT_STATE:
+ case IFLA_BRPORT_MODE:
+ case IFLA_BRPORT_GUARD:
+ case IFLA_BRPORT_PROTECT:
+ case IFLA_BRPORT_FAST_LEAVE:
+ case IFLA_BRPORT_LEARNING:
+ case IFLA_BRPORT_UNICAST_FLOOD:
+ case IFLA_BRPORT_PROXYARP:
+ case IFLA_BRPORT_LEARNING_SYNC:
+ case IFLA_BRPORT_PROXYARP_WIFI:
+ case IFLA_BRPORT_TOPOLOGY_CHANGE_ACK:
+ case IFLA_BRPORT_CONFIG_PENDING:
+ case IFLA_BRPORT_MULTICAST_ROUTER:
+ break;
+ /* uint16_t */
+ case IFLA_BRPORT_PRIORITY:
+ case IFLA_BRPORT_DESIGNATED_PORT:
+ case IFLA_BRPORT_DESIGNATED_COST:
+ case IFLA_BRPORT_ID:
+ case IFLA_BRPORT_NO:
+ u16 = NLA_DATA(nlattr);
+ *u16 = tswap16(*u16);
+ break;
+ /* uin32_t */
+ case IFLA_BRPORT_COST:
+ u32 = NLA_DATA(nlattr);
+ *u32 = tswap32(*u32);
+ break;
+ /* uint64_t */
+ case IFLA_BRPORT_MESSAGE_AGE_TIMER:
+ case IFLA_BRPORT_FORWARD_DELAY_TIMER:
+ case IFLA_BRPORT_HOLD_TIMER:
+ u64 = NLA_DATA(nlattr);
+ *u64 = tswap64(*u64);
+ break;
+ /* ifla_bridge_id: uint8_t[] */
+ case IFLA_BRPORT_ROOT_ID:
+ case IFLA_BRPORT_BRIDGE_ID:
+ break;
+ default:
+ gemu_log("Unknown IFLA_BRPORT type %d\n", nlattr->nla_type);
+ break;
+ }
+ return 0;
+}
+
+struct linkinfo_context {
+ int len;
+ char *name;
+ int slave_len;
+ char *slave_name;
+};
+
+static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr,
+ void *context)
+{
+ struct linkinfo_context *li_context = context;
+
+ switch (nlattr->nla_type) {
+ /* string */
+ case IFLA_INFO_KIND:
+ li_context->name = NLA_DATA(nlattr);
+ li_context->len = nlattr->nla_len - NLA_HDRLEN;
+ break;
+ case IFLA_INFO_SLAVE_KIND:
+ li_context->slave_name = NLA_DATA(nlattr);
+ li_context->slave_len = nlattr->nla_len - NLA_HDRLEN;
+ break;
+ /* stats */
+ case IFLA_INFO_XSTATS:
+ /* FIXME: only used by CAN */
+ break;
+ /* nested */
+ case IFLA_INFO_DATA:
+ if (strncmp(li_context->name, "bridge",
+ li_context->len) == 0) {
+ return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
+ nlattr->nla_len,
+ NULL,
+ host_to_target_data_bridge_nlattr);
+ } else {
+ gemu_log("Unknown IFLA_INFO_KIND %s\n", li_context->name);
+ }
+ break;
+ case IFLA_INFO_SLAVE_DATA:
+ if (strncmp(li_context->slave_name, "bridge",
+ li_context->slave_len) == 0) {
+ return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
+ nlattr->nla_len,
+ NULL,
+ host_to_target_slave_data_bridge_nlattr);
+ } else {
+ gemu_log("Unknown IFLA_INFO_SLAVE_KIND %s\n",
+ li_context->slave_name);
+ }
+ break;
+ default:
+ gemu_log("Unknown host IFLA_INFO type: %d\n", nlattr->nla_type);
+ break;
+ }
+
+ return 0;
+}
+
+static abi_long host_to_target_data_inet_nlattr(struct nlattr *nlattr,
+ void *context)
+{
+ uint32_t *u32;
+ int i;
+
+ switch (nlattr->nla_type) {
+ case IFLA_INET_CONF:
+ u32 = NLA_DATA(nlattr);
+ for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32);
+ i++) {
+ u32[i] = tswap32(u32[i]);
+ }
+ break;
+ default:
+ gemu_log("Unknown host AF_INET type: %d\n", nlattr->nla_type);
+ }
+ return 0;
+}
+
+static abi_long host_to_target_data_inet6_nlattr(struct nlattr *nlattr,
+ void *context)
+{
+ uint32_t *u32;
+ uint64_t *u64;
+ struct ifla_cacheinfo *ci;
+ int i;
+
+ switch (nlattr->nla_type) {
+ /* binaries */
+ case IFLA_INET6_TOKEN:
+ break;
+ /* uint8_t */
+ case IFLA_INET6_ADDR_GEN_MODE:
+ break;
+ /* uint32_t */
+ case IFLA_INET6_FLAGS:
+ u32 = NLA_DATA(nlattr);
+ *u32 = tswap32(*u32);
+ break;
+ /* uint32_t[] */
+ case IFLA_INET6_CONF:
+ u32 = NLA_DATA(nlattr);
+ for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32);
+ i++) {
+ u32[i] = tswap32(u32[i]);
+ }
+ break;
+ /* ifla_cacheinfo */
+ case IFLA_INET6_CACHEINFO:
+ ci = NLA_DATA(nlattr);
+ ci->max_reasm_len = tswap32(ci->max_reasm_len);
+ ci->tstamp = tswap32(ci->tstamp);
+ ci->reachable_time = tswap32(ci->reachable_time);
+ ci->retrans_time = tswap32(ci->retrans_time);
+ break;
+ /* uint64_t[] */
+ case IFLA_INET6_STATS:
+ case IFLA_INET6_ICMP6STATS:
+ u64 = NLA_DATA(nlattr);
+ for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u64);
+ i++) {
+ u64[i] = tswap64(u64[i]);
+ }
+ break;
+ default:
+ gemu_log("Unknown host AF_INET6 type: %d\n", nlattr->nla_type);
+ }
+ return 0;
+}
+
+static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr,
+ void *context)
+{
+ switch (nlattr->nla_type) {
+ case AF_INET:
+ return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
+ NULL,
+ host_to_target_data_inet_nlattr);
+ case AF_INET6:
+ return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
+ NULL,
+ host_to_target_data_inet6_nlattr);
+ default:
+ gemu_log("Unknown host AF_SPEC type: %d\n", nlattr->nla_type);
+ break;
+ }
+ return 0;
+}
+
static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
struct rtnl_link_stats *st;
struct rtnl_link_stats64 *st64;
struct rtnl_link_ifmap *map;
+ struct linkinfo_context li_context;
switch (rtattr->rta_type) {
/* binary stream */
@@ -1846,11 +2166,15 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr)
map->irq = tswap16(map->irq);
break;
/* nested */
- case IFLA_AF_SPEC:
case IFLA_LINKINFO:
- /* FIXME: implement nested type */
- gemu_log("Unimplemented nested type %d\n", rtattr->rta_type);
- break;
+ memset(&li_context, 0, sizeof(li_context));
+ return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
+ &li_context,
+ host_to_target_data_linkinfo_nlattr);
+ case IFLA_AF_SPEC:
+ return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
+ NULL,
+ host_to_target_data_spec_nlattr);
default:
gemu_log("Unknown host IFLA type: %d\n", rtattr->rta_type);
break;
@@ -2826,12 +3150,26 @@ static TargetFdTrans target_packet_trans = {
#ifdef CONFIG_RTNETLINK
static abi_long netlink_route_target_to_host(void *buf, size_t len)
{
- return target_to_host_nlmsg_route(buf, len);
+ abi_long ret;
+
+ ret = target_to_host_nlmsg_route(buf, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return len;
}
static abi_long netlink_route_host_to_target(void *buf, size_t len)
{
- return host_to_target_nlmsg_route(buf, len);
+ abi_long ret;
+
+ ret = host_to_target_nlmsg_route(buf, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return len;
}
static TargetFdTrans target_netlink_route_trans = {
@@ -2842,12 +3180,26 @@ static TargetFdTrans target_netlink_route_trans = {
static abi_long netlink_audit_target_to_host(void *buf, size_t len)
{
- return target_to_host_nlmsg_audit(buf, len);
+ abi_long ret;
+
+ ret = target_to_host_nlmsg_audit(buf, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return len;
}
static abi_long netlink_audit_host_to_target(void *buf, size_t len)
{
- return host_to_target_nlmsg_audit(buf, len);
+ abi_long ret;
+
+ ret = host_to_target_nlmsg_audit(buf, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return len;
}
static TargetFdTrans target_netlink_audit_trans = {
@@ -2989,13 +3341,22 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp,
if (send) {
if (fd_trans_target_to_host_data(fd)) {
- ret = fd_trans_target_to_host_data(fd)(msg.msg_iov->iov_base,
+ void *host_msg;
+
+ host_msg = g_malloc(msg.msg_iov->iov_len);
+ memcpy(host_msg, msg.msg_iov->iov_base, msg.msg_iov->iov_len);
+ ret = fd_trans_target_to_host_data(fd)(host_msg,
msg.msg_iov->iov_len);
+ if (ret >= 0) {
+ msg.msg_iov->iov_base = host_msg;
+ ret = get_errno(safe_sendmsg(fd, &msg, flags));
+ }
+ g_free(host_msg);
} else {
ret = target_to_host_cmsg(&msg, msgp);
- }
- if (ret == 0) {
- ret = get_errno(safe_sendmsg(fd, &msg, flags));
+ if (ret == 0) {
+ ret = get_errno(safe_sendmsg(fd, &msg, flags));
+ }
}
} else {
ret = get_errno(safe_recvmsg(fd, &msg, flags));
@@ -3211,6 +3572,7 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags,
{
void *addr;
void *host_msg;
+ void *copy_msg = NULL;
abi_long ret;
if ((int)addrlen < 0) {
@@ -3221,23 +3583,29 @@ static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags,
if (!host_msg)
return -TARGET_EFAULT;
if (fd_trans_target_to_host_data(fd)) {
+ copy_msg = host_msg;
+ host_msg = g_malloc(len);
+ memcpy(host_msg, copy_msg, len);
ret = fd_trans_target_to_host_data(fd)(host_msg, len);
if (ret < 0) {
- unlock_user(host_msg, msg, 0);
- return ret;
+ goto fail;
}
}
if (target_addr) {
addr = alloca(addrlen+1);
ret = target_to_host_sockaddr(fd, addr, target_addr, addrlen);
if (ret) {
- unlock_user(host_msg, msg, 0);
- return ret;
+ goto fail;
}
ret = get_errno(safe_sendto(fd, host_msg, len, flags, addr, addrlen));
} else {
ret = get_errno(safe_sendto(fd, host_msg, len, flags, NULL, 0));
}
+fail:
+ if (copy_msg) {
+ g_free(host_msg);
+ host_msg = copy_msg;
+ }
unlock_user(host_msg, msg, 0);
return ret;
}
@@ -3272,6 +3640,9 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, NULL, 0));
}
if (!is_error(ret)) {
+ if (fd_trans_host_to_target_data(fd)) {
+ ret = fd_trans_host_to_target_data(fd)(host_msg, ret);
+ }
if (target_addr) {
host_to_target_sockaddr(target_addr, addr, addrlen);
if (put_user_u32(addrlen, target_addrlen)) {
@@ -7614,7 +7985,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#if defined(TARGET_ALPHA)
struct target_sigaction act, oact, *pact = 0;
struct target_rt_sigaction *rt_act;
- /* ??? arg4 == sizeof(sigset_t). */
+
+ if (arg4 != sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
if (arg2) {
if (!lock_user_struct(VERIFY_READ, rt_act, arg2, 1))
goto efault;
@@ -7638,6 +8013,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
struct target_sigaction *act;
struct target_sigaction *oact;
+ if (arg4 != sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
if (arg2) {
if (!lock_user_struct(VERIFY_READ, act, arg2, 1))
goto efault;
@@ -7769,6 +8148,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
int how = arg1;
sigset_t set, oldset, *set_ptr;
+ if (arg4 != sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
if (arg2) {
switch(how) {
case TARGET_SIG_BLOCK:
@@ -7819,6 +8203,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
case TARGET_NR_rt_sigpending:
{
sigset_t set;
+
+ /* Yes, this check is >, not != like most. We follow the kernel's
+ * logic and it does it like this because it implements
+ * NR_sigpending through the same code path, and in that case
+ * the old_sigset_t is smaller in size.
+ */
+ if (arg2 > sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
ret = get_errno(sigpending(&set));
if (!is_error(ret)) {
if (!(p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0)))
@@ -7852,6 +8247,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
case TARGET_NR_rt_sigsuspend:
{
TaskState *ts = cpu->opaque;
+
+ if (arg2 != sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
goto efault;
target_to_host_sigset(&ts->sigsuspend_mask, p);
@@ -7869,6 +8269,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
struct timespec uts, *puts;
siginfo_t uinfo;
+ if (arg4 != sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
goto efault;
target_to_host_sigset(&set, p);
@@ -9120,6 +9525,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
}
if (arg4) {
+ if (arg5 != sizeof(target_sigset_t)) {
+ unlock_user(target_pfd, arg1, 0);
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
target_set = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1);
if (!target_set) {
unlock_user(target_pfd, arg1, 0);
@@ -10939,6 +11350,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
sigset_t _set, *set = &_set;
if (arg5) {
+ if (arg6 != sizeof(target_sigset_t)) {
+ ret = -TARGET_EINVAL;
+ break;
+ }
+
target_set = lock_user(VERIFY_READ, arg5,
sizeof(target_sigset_t), 1);
if (!target_set) {
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index b43966ece0..783565463f 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -985,6 +985,17 @@ struct target_pollfd {
#define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,abi_ulong)
/* return device size in bytes
(u64 *arg) */
+
+#define TARGET_BLKDISCARD TARGET_IO(0x12, 119)
+#define TARGET_BLKIOMIN TARGET_IO(0x12, 120)
+#define TARGET_BLKIOOPT TARGET_IO(0x12, 121)
+#define TARGET_BLKALIGNOFF TARGET_IO(0x12, 122)
+#define TARGET_BLKPBSZGET TARGET_IO(0x12, 123)
+#define TARGET_BLKDISCARDZEROES TARGET_IO(0x12, 124)
+#define TARGET_BLKSECDISCARD TARGET_IO(0x12, 125)
+#define TARGET_BLKROTATIONAL TARGET_IO(0x12, 126)
+#define TARGET_BLKZEROOUT TARGET_IO(0x12, 127)
+
#define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */
#define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */
#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap)
@@ -1117,6 +1128,10 @@ struct target_pollfd {
#define TARGET_LOOP_GET_STATUS64 0x4C05
#define TARGET_LOOP_CHANGE_FD 0x4C06
+#define TARGET_LOOP_CTL_ADD 0x4C80
+#define TARGET_LOOP_CTL_REMOVE 0x4C81
+#define TARGET_LOOP_CTL_GET_FREE 0x4C82
+
/* fb ioctls */
#define TARGET_FBIOGET_VSCREENINFO 0x4600
#define TARGET_FBIOPUT_VSCREENINFO 0x4601
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
index 1fd4ee0bfd..af79fbf1de 100644
--- a/linux-user/syscall_types.h
+++ b/linux-user/syscall_types.h
@@ -103,10 +103,11 @@ STRUCT(loop_info64,
TYPE_ULONGLONG, /* lo_inode */
TYPE_ULONGLONG, /* lo_rdevice */
TYPE_ULONGLONG, /* lo_offset */
- TYPE_ULONG, /* lo_number */
- TYPE_ULONG, /* lo_encrypt_type */
- TYPE_ULONG, /* lo_encrypt_key_size */
- TYPE_ULONG, /* lo_flags */
+ TYPE_ULONGLONG, /* lo_sizelimit */
+ TYPE_INT, /* lo_number */
+ TYPE_INT, /* lo_encrypt_type */
+ TYPE_INT, /* lo_encrypt_key_size */
+ TYPE_INT, /* lo_flags */
MK_ARRAY(TYPE_CHAR, 64), /* lo_name */
MK_ARRAY(TYPE_CHAR, 64), /* lo_crypt_name */
MK_ARRAY(TYPE_CHAR, 32), /* lo_encrypt_key */
diff --git a/linux-user/x86_64/termbits.h b/linux-user/x86_64/termbits.h
index 1c3445c6a2..387e742592 100644
--- a/linux-user/x86_64/termbits.h
+++ b/linux-user/x86_64/termbits.h
@@ -209,12 +209,12 @@ struct target_termios {
#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
-#define TARGET_TCGETS2 _IOR('T',0x2A, struct termios2)
-#define TARGET_TCSETS2 _IOW('T',0x2B, struct termios2)
-#define TARGET_TCSETSW2 _IOW('T',0x2C, struct termios2)
-#define TARGET_TCSETSF2 _IOW('T',0x2D, struct termios2)
-#define TARGET_TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
-#define TARGET_TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TARGET_TCGETS2 TARGET_IOR('T',0x2A, struct termios2)
+#define TARGET_TCSETS2 TARGET_IOW('T',0x2B, struct termios2)
+#define TARGET_TCSETSW2 TARGET_IOW('T',0x2C, struct termios2)
+#define TARGET_TCSETSF2 TARGET_IOW('T',0x2D, struct termios2)
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define TARGET_FIOCLEX 0x5451
diff --git a/monitor.c b/monitor.c
index c1591f286d..5d68a5d6fc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3080,8 +3080,8 @@ void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
}
len = strlen(str);
readline_set_completion_index(rs, len);
- for (i = 0; NetClientOptionsKind_lookup[i]; i++) {
- add_completion_option(rs, str, NetClientOptionsKind_lookup[i]);
+ for (i = 0; NetClientDriver_lookup[i]; i++) {
+ add_completion_option(rs, str, NetClientDriver_lookup[i]);
}
}
@@ -3281,7 +3281,7 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
NetClientState *ncs[MAX_QUEUE_NUM];
int count, i;
count = qemu_find_net_clients_except(NULL, ncs,
- NET_CLIENT_OPTIONS_KIND_NONE,
+ NET_CLIENT_DRIVER_NONE,
MAX_QUEUE_NUM);
for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
const char *name = ncs[i]->name;
@@ -3306,7 +3306,7 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
len = strlen(str);
readline_set_completion_index(rs, len);
- count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC,
+ count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
QemuOpts *opts;
@@ -3435,7 +3435,7 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
readline_set_completion_index(rs, len);
if (nb_args == 2) {
count = qemu_find_net_clients_except(NULL, ncs,
- NET_CLIENT_OPTIONS_KIND_NONE,
+ NET_CLIENT_DRIVER_NONE,
MAX_QUEUE_NUM);
for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
int id;
@@ -3452,13 +3452,13 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
return;
} else if (nb_args == 3) {
count = qemu_find_net_clients_except(NULL, ncs,
- NET_CLIENT_OPTIONS_KIND_NIC,
+ NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
int id;
const char *name;
- if (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT ||
+ if (ncs[i]->info->type == NET_CLIENT_DRIVER_HUBPORT ||
net_hub_id_for_client(ncs[i], &id)) {
continue;
}
diff --git a/nbd/common.c b/nbd/common.c
index 8ddb2dd2f0..b583a4f4cf 100644
--- a/nbd/common.c
+++ b/nbd/common.c
@@ -23,7 +23,6 @@
ssize_t nbd_wr_syncv(QIOChannel *ioc,
struct iovec *iov,
size_t niov,
- size_t offset,
size_t length,
bool do_read)
{
@@ -33,9 +32,7 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
struct iovec *local_iov_head = local_iov;
unsigned int nlocal_iov = niov;
- nlocal_iov = iov_copy(local_iov, nlocal_iov,
- iov, niov,
- offset, length);
+ nlocal_iov = iov_copy(local_iov, nlocal_iov, iov, niov, 0, length);
while (nlocal_iov > 0) {
ssize_t len;
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 26a9f4d9fd..93a6ca8549 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -101,14 +101,14 @@ static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
* our request/reply. Synchronization is done with recv_coroutine, so
* that this is coroutine-safe.
*/
- return nbd_wr_syncv(ioc, &iov, 1, 0, size, true);
+ return nbd_wr_syncv(ioc, &iov, 1, size, true);
}
static inline ssize_t write_sync(QIOChannel *ioc, void *buffer, size_t size)
{
struct iovec iov = { .iov_base = buffer, .iov_len = size };
- return nbd_wr_syncv(ioc, &iov, 1, 0, size, false);
+ return nbd_wr_syncv(ioc, &iov, 1, size, false);
}
struct NBDTLSHandshakeData {
diff --git a/nbd/server.c b/nbd/server.c
index fbc82de932..29e2099b5e 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1182,20 +1182,11 @@ static void nbd_trip(void *opaque)
break;
case NBD_CMD_TRIM:
TRACE("Request type is TRIM");
- /* Ignore unaligned head or tail, until block layer adds byte
- * interface */
- if (request.len >= BDRV_SECTOR_SIZE) {
- request.len -= (request.from + request.len) % BDRV_SECTOR_SIZE;
- ret = blk_co_discard(exp->blk,
- DIV_ROUND_UP(request.from + exp->dev_offset,
- BDRV_SECTOR_SIZE),
- request.len / BDRV_SECTOR_SIZE);
- if (ret < 0) {
- LOG("discard failed");
- reply.error = -ret;
- }
- } else {
- TRACE("trim request too small, ignoring");
+ ret = blk_co_pdiscard(exp->blk, request.from + exp->dev_offset,
+ request.len);
+ if (ret < 0) {
+ LOG("discard failed");
+ reply.error = -ret;
}
if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
diff --git a/net/clients.h b/net/clients.h
index d47530e82f..5cae479730 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -27,39 +27,39 @@
#include "net/net.h"
#include "qapi-types.h"
-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
#ifdef CONFIG_SLIRP
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
#endif
-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
-int net_init_l2tpv3(const NetClientOptions *opts, const char *name,
+int net_init_l2tpv3(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
#ifdef CONFIG_VDE
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
#endif
#ifdef CONFIG_NETMAP
-int net_init_netmap(const NetClientOptions *opts, const char *name,
+int net_init_netmap(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
#endif
-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp);
#endif /* QEMU_NET_CLIENTS_H */
diff --git a/net/dump.c b/net/dump.c
index 41f7673efd..89a149b5dd 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -172,14 +172,14 @@ static void dumpclient_cleanup(NetClientState *nc)
}
static NetClientInfo net_dump_info = {
- .type = NET_CLIENT_OPTIONS_KIND_DUMP,
+ .type = NET_CLIENT_DRIVER_DUMP,
.size = sizeof(DumpNetClient),
.receive = dumpclient_receive,
.receive_iov = dumpclient_receive_iov,
.cleanup = dumpclient_cleanup,
};
-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
int len, rc;
@@ -189,8 +189,8 @@ int net_init_dump(const NetClientOptions *opts, const char *name,
NetClientState *nc;
DumpNetClient *dnc;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_DUMP);
- dump = opts->u.dump.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_DUMP);
+ dump = &netdev->u.dump;
assert(peer);
diff --git a/net/filter.c b/net/filter.c
index 8ac79f3b7b..888fe6dd93 100644
--- a/net/filter.c
+++ b/net/filter.c
@@ -201,7 +201,7 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
}
queues = qemu_find_net_clients_except(nf->netdev_id, ncs,
- NET_CLIENT_OPTIONS_KIND_NIC,
+ NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
if (queues < 1) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev",
diff --git a/net/hub.c b/net/hub.c
index 6d90c6ee67..32d8cf5cd4 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -131,7 +131,7 @@ static void net_hub_port_cleanup(NetClientState *nc)
}
static NetClientInfo net_hub_port_info = {
- .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
+ .type = NET_CLIENT_DRIVER_HUBPORT,
.size = sizeof(NetHubPort),
.can_receive = net_hub_port_can_receive,
.receive = net_hub_port_receive,
@@ -266,10 +266,10 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
{
NetHubPort *port;
- if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
port = DO_UPCAST(NetHubPort, nc, nc);
} else if (nc->peer != NULL && nc->peer->info->type ==
- NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ NET_CLIENT_DRIVER_HUBPORT) {
port = DO_UPCAST(NetHubPort, nc, nc->peer);
} else {
return -ENOENT;
@@ -281,14 +281,14 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
return 0;
}
-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
const NetdevHubPortOptions *hubport;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+ assert(netdev->type == NET_CLIENT_DRIVER_HUBPORT);
assert(!peer);
- hubport = opts->u.hubport.data;
+ hubport = &netdev->u.hubport;
net_hub_add_port(hubport->hubid, name);
return 0;
@@ -315,14 +315,14 @@ void net_hub_check_clients(void)
}
switch (peer->info->type) {
- case NET_CLIENT_OPTIONS_KIND_NIC:
+ case NET_CLIENT_DRIVER_NIC:
has_nic = 1;
break;
- case NET_CLIENT_OPTIONS_KIND_USER:
- case NET_CLIENT_OPTIONS_KIND_TAP:
- case NET_CLIENT_OPTIONS_KIND_SOCKET:
- case NET_CLIENT_OPTIONS_KIND_VDE:
- case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+ case NET_CLIENT_DRIVER_USER:
+ case NET_CLIENT_DRIVER_TAP:
+ case NET_CLIENT_DRIVER_SOCKET:
+ case NET_CLIENT_DRIVER_VDE:
+ case NET_CLIENT_DRIVER_VHOST_USER:
has_host_dev = 1;
break;
default:
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 5c668f7376..6745b78990 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -516,7 +516,7 @@ static void net_l2tpv3_cleanup(NetClientState *nc)
}
static NetClientInfo net_l2tpv3_info = {
- .type = NET_CLIENT_OPTIONS_KIND_L2TPV3,
+ .type = NET_CLIENT_DRIVER_L2TPV3,
.size = sizeof(NetL2TPV3State),
.receive = net_l2tpv3_receive_dgram,
.receive_iov = net_l2tpv3_receive_dgram_iov,
@@ -524,7 +524,7 @@ static NetClientInfo net_l2tpv3_info = {
.cleanup = net_l2tpv3_cleanup,
};
-int net_init_l2tpv3(const NetClientOptions *opts,
+int net_init_l2tpv3(const Netdev *netdev,
const char *name,
NetClientState *peer, Error **errp)
{
@@ -545,8 +545,8 @@ int net_init_l2tpv3(const NetClientOptions *opts,
s->queue_tail = 0;
s->header_mismatch = false;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3);
- l2tpv3 = opts->u.l2tpv3.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_L2TPV3);
+ l2tpv3 = &netdev->u.l2tpv3;
if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index 019aaad0fc..c124b11e4d 100644
--- a/net/net.c
+++ b/net/net.c
@@ -289,7 +289,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
NICState *nic;
int i, queues = MAX(1, conf->peers.queues);
- assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
+ assert(info->type == NET_CLIENT_DRIVER_NIC);
assert(info->size >= sizeof(NICState));
nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
@@ -360,13 +360,13 @@ void qemu_del_net_client(NetClientState *nc)
int queues, i;
NetFilterState *nf, *next;
- assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
+ assert(nc->info->type != NET_CLIENT_DRIVER_NIC);
/* If the NetClientState belongs to a multiqueue backend, we will change all
* other NetClientStates also.
*/
queues = qemu_find_net_clients_except(nc->name, ncs,
- NET_CLIENT_OPTIONS_KIND_NIC,
+ NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
assert(queues != 0);
@@ -375,7 +375,7 @@ void qemu_del_net_client(NetClientState *nc)
}
/* If there is a peer NIC, delete and cleanup client, but do not free. */
- if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
NICState *nic = qemu_get_nic(nc->peer);
if (nic->peer_deleted) {
return;
@@ -431,7 +431,7 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
NetClientState *nc;
QTAILQ_FOREACH(nc, &net_clients, next) {
- if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
if (nc->queue_index == 0) {
func(qemu_get_nic(nc), opaque);
}
@@ -603,7 +603,7 @@ void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge)
{
nc->receive_disabled = 0;
- if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_HUBPORT) {
if (net_hub_flush(nc->peer)) {
qemu_notify_event();
}
@@ -777,7 +777,7 @@ NetClientState *qemu_find_netdev(const char *id)
NetClientState *nc;
QTAILQ_FOREACH(nc, &net_clients, next) {
- if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+ if (nc->info->type == NET_CLIENT_DRIVER_NIC)
continue;
if (!strcmp(nc->name, id)) {
return nc;
@@ -788,7 +788,7 @@ NetClientState *qemu_find_netdev(const char *id)
}
int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
- NetClientOptionsKind type, int max)
+ NetClientDriver type, int max)
{
NetClientState *nc;
int ret = 0;
@@ -862,15 +862,15 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
return -1;
}
-static int net_init_nic(const NetClientOptions *opts, const char *name,
+static int net_init_nic(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
int idx;
NICInfo *nd;
const NetLegacyNicOptions *nic;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_NIC);
- nic = opts->u.nic.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_NIC);
+ nic = &netdev->u.nic;
idx = nic_get_free_idx();
if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -930,70 +930,111 @@ static int net_init_nic(const NetClientOptions *opts, const char *name,
}
-static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND__MAX])(
- const NetClientOptions *opts,
+static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])(
+ const Netdev *netdev,
const char *name,
NetClientState *peer, Error **errp) = {
- [NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic,
+ [NET_CLIENT_DRIVER_NIC] = net_init_nic,
#ifdef CONFIG_SLIRP
- [NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp,
+ [NET_CLIENT_DRIVER_USER] = net_init_slirp,
#endif
- [NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap,
- [NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket,
+ [NET_CLIENT_DRIVER_TAP] = net_init_tap,
+ [NET_CLIENT_DRIVER_SOCKET] = net_init_socket,
#ifdef CONFIG_VDE
- [NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde,
+ [NET_CLIENT_DRIVER_VDE] = net_init_vde,
#endif
#ifdef CONFIG_NETMAP
- [NET_CLIENT_OPTIONS_KIND_NETMAP] = net_init_netmap,
+ [NET_CLIENT_DRIVER_NETMAP] = net_init_netmap,
#endif
- [NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump,
+ [NET_CLIENT_DRIVER_DUMP] = net_init_dump,
#ifdef CONFIG_NET_BRIDGE
- [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge,
+ [NET_CLIENT_DRIVER_BRIDGE] = net_init_bridge,
#endif
- [NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport,
+ [NET_CLIENT_DRIVER_HUBPORT] = net_init_hubport,
#ifdef CONFIG_VHOST_NET_USED
- [NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user,
+ [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user,
#endif
#ifdef CONFIG_L2TPV3
- [NET_CLIENT_OPTIONS_KIND_L2TPV3] = net_init_l2tpv3,
+ [NET_CLIENT_DRIVER_L2TPV3] = net_init_l2tpv3,
#endif
};
-static int net_client_init1(const void *object, int is_netdev, Error **errp)
+static int net_client_init1(const void *object, bool is_netdev, Error **errp)
{
- const NetClientOptions *opts;
+ Netdev legacy = {0};
+ const Netdev *netdev;
const char *name;
NetClientState *peer = NULL;
if (is_netdev) {
- const Netdev *netdev = object;
- opts = netdev->opts;
+ netdev = object;
name = netdev->id;
- if (opts->type == NET_CLIENT_OPTIONS_KIND_DUMP ||
- opts->type == NET_CLIENT_OPTIONS_KIND_NIC ||
- !net_client_init_fun[opts->type]) {
+ if (netdev->type == NET_CLIENT_DRIVER_DUMP ||
+ netdev->type == NET_CLIENT_DRIVER_NIC ||
+ !net_client_init_fun[netdev->type]) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
"a netdev backend type");
return -1;
}
} else {
const NetLegacy *net = object;
- opts = net->opts;
+ const NetLegacyOptions *opts = net->opts;
+ legacy.id = net->id;
+ netdev = &legacy;
/* missing optional values have been initialized to "all bits zero" */
name = net->has_id ? net->id : net->name;
- if (opts->type == NET_CLIENT_OPTIONS_KIND_NONE) {
+ /* Map the old options to the new flat type */
+ switch (opts->type) {
+ case NET_LEGACY_OPTIONS_KIND_NONE:
return 0; /* nothing to do */
- }
- if (opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
- "a net type");
- return -1;
+ case NET_LEGACY_OPTIONS_KIND_NIC:
+ legacy.type = NET_CLIENT_DRIVER_NIC;
+ legacy.u.nic = *opts->u.nic.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_USER:
+ legacy.type = NET_CLIENT_DRIVER_USER;
+ legacy.u.user = *opts->u.user.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_TAP:
+ legacy.type = NET_CLIENT_DRIVER_TAP;
+ legacy.u.tap = *opts->u.tap.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_L2TPV3:
+ legacy.type = NET_CLIENT_DRIVER_L2TPV3;
+ legacy.u.l2tpv3 = *opts->u.l2tpv3.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_SOCKET:
+ legacy.type = NET_CLIENT_DRIVER_SOCKET;
+ legacy.u.socket = *opts->u.socket.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_VDE:
+ legacy.type = NET_CLIENT_DRIVER_VDE;
+ legacy.u.vde = *opts->u.vde.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_DUMP:
+ legacy.type = NET_CLIENT_DRIVER_DUMP;
+ legacy.u.dump = *opts->u.dump.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_BRIDGE:
+ legacy.type = NET_CLIENT_DRIVER_BRIDGE;
+ legacy.u.bridge = *opts->u.bridge.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_NETMAP:
+ legacy.type = NET_CLIENT_DRIVER_NETMAP;
+ legacy.u.netmap = *opts->u.netmap.data;
+ break;
+ case NET_LEGACY_OPTIONS_KIND_VHOST_USER:
+ legacy.type = NET_CLIENT_DRIVER_VHOST_USER;
+ legacy.u.vhost_user = *opts->u.vhost_user.data;
+ break;
+ default:
+ abort();
}
- if (!net_client_init_fun[opts->type]) {
+ if (!net_client_init_fun[netdev->type]) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
"a net backend type (maybe it is not compiled "
"into this binary)");
@@ -1001,17 +1042,17 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
}
/* Do not add to a vlan if it's a nic with a netdev= parameter. */
- if (opts->type != NET_CLIENT_OPTIONS_KIND_NIC ||
+ if (netdev->type != NET_CLIENT_DRIVER_NIC ||
!opts->u.nic.data->has_netdev) {
peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL);
}
}
- if (net_client_init_fun[opts->type](opts, name, peer, errp) < 0) {
+ if (net_client_init_fun[netdev->type](netdev, name, peer, errp) < 0) {
/* FIXME drop when all init functions store an Error */
if (errp && !*errp) {
error_setg(errp, QERR_DEVICE_INIT_FAILED,
- NetClientOptionsKind_lookup[opts->type]);
+ NetClientDriver_lookup[netdev->type]);
}
return -1;
}
@@ -1019,7 +1060,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
}
-int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
+int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
{
void *object = NULL;
Error *err = NULL;
@@ -1112,7 +1153,7 @@ void hmp_host_net_add(Monitor *mon, const QDict *qdict)
qemu_opt_set(opts, "type", device, &error_abort);
- net_client_init(opts, 0, &local_err);
+ net_client_init(opts, false, &local_err);
if (local_err) {
error_report_err(local_err);
monitor_printf(mon, "adding host network device %s failed\n", device);
@@ -1131,7 +1172,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
device, vlan_id);
return;
}
- if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
error_report("invalid host network device '%s'", device);
return;
}
@@ -1142,7 +1183,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
void netdev_add(QemuOpts *opts, Error **errp)
{
- net_client_init(opts, 1, errp);
+ net_client_init(opts, true, errp);
}
void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp)
@@ -1222,7 +1263,7 @@ void print_net_client(Monitor *mon, NetClientState *nc)
monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
nc->queue_index,
- NetClientOptionsKind_lookup[nc->info->type],
+ NetClientDriver_lookup[nc->info->type],
nc->info_str);
if (!QTAILQ_EMPTY(&nc->filters)) {
monitor_printf(mon, "filters:\n");
@@ -1252,7 +1293,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
}
/* only query rx-filter information of NIC */
- if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
if (has_name) {
error_setg(errp, "net client(%s) isn't a NIC", name);
return NULL;
@@ -1298,7 +1339,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
void hmp_info_network(Monitor *mon, const QDict *qdict)
{
NetClientState *nc, *peer;
- NetClientOptionsKind type;
+ NetClientDriver type;
net_hub_info(mon);
@@ -1311,10 +1352,10 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
continue;
}
- if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (!peer || type == NET_CLIENT_DRIVER_NIC) {
print_net_client(mon, nc);
} /* else it's a netdev connected to a NIC, printed with the NIC */
- if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (peer && type == NET_CLIENT_DRIVER_NIC) {
monitor_printf(mon, " \\ ");
print_net_client(mon, peer);
}
@@ -1328,7 +1369,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
int queues, i;
queues = qemu_find_net_clients_except(name, ncs,
- NET_CLIENT_OPTIONS_KIND__MAX,
+ NET_CLIENT_DRIVER__MAX,
MAX_QUEUE_NUM);
if (queues == 0) {
@@ -1355,7 +1396,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
* multiple clients that can still communicate with each other in
* disconnected mode. For now maintain this compatibility.
*/
- if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
for (i = 0; i < queues; i++) {
ncs[i]->peer->link_down = !up;
}
@@ -1396,7 +1437,7 @@ void net_cleanup(void)
*/
while (!QTAILQ_EMPTY(&net_clients)) {
nc = QTAILQ_FIRST(&net_clients);
- if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
qemu_del_nic(qemu_get_nic(nc));
} else {
qemu_del_net_client(nc);
@@ -1416,7 +1457,7 @@ void net_check_clients(void)
QTAILQ_FOREACH(nc, &net_clients, next) {
if (!nc->peer) {
fprintf(stderr, "Warning: %s %s has no peer\n",
- nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
+ nc->info->type == NET_CLIENT_DRIVER_NIC ?
"nic" : "netdev", nc->name);
}
}
@@ -1440,7 +1481,7 @@ static int net_init_client(void *dummy, QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
- net_client_init(opts, 0, &local_err);
+ net_client_init(opts, false, &local_err);
if (local_err) {
error_report_err(local_err);
return -1;
@@ -1454,7 +1495,7 @@ static int net_init_netdev(void *dummy, QemuOpts *opts, Error **errp)
Error *local_err = NULL;
int ret;
- ret = net_client_init(opts, 1, &local_err);
+ ret = net_client_init(opts, true, &local_err);
if (local_err) {
error_report_err(local_err);
return -1;
diff --git a/net/netmap.c b/net/netmap.c
index 64967b947e..2d11a8f4be 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -400,7 +400,7 @@ static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
/* NetClientInfo methods */
static NetClientInfo net_netmap_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NETMAP,
+ .type = NET_CLIENT_DRIVER_NETMAP,
.size = sizeof(NetmapState),
.receive = netmap_receive,
.receive_iov = netmap_receive_iov,
@@ -418,10 +418,10 @@ static NetClientInfo net_netmap_info = {
*
* ... -net netmap,ifname="..."
*/
-int net_init_netmap(const NetClientOptions *opts,
+int net_init_netmap(const Netdev *netdev,
const char *name, NetClientState *peer, Error **errp)
{
- const NetdevNetmapOptions *netmap_opts = opts->u.netmap.data;
+ const NetdevNetmapOptions *netmap_opts = &netdev->u.netmap;
struct nm_desc *nmd;
NetClientState *nc;
Error *err = NULL;
diff --git a/net/slirp.c b/net/slirp.c
index 28207b6614..facc30ed18 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -137,7 +137,7 @@ static void net_slirp_cleanup(NetClientState *nc)
}
static NetClientInfo net_slirp_info = {
- .type = NET_CLIENT_OPTIONS_KIND_USER,
+ .type = NET_CLIENT_DRIVER_USER,
.size = sizeof(SlirpState),
.receive = net_slirp_receive,
.cleanup = net_slirp_cleanup,
@@ -828,7 +828,7 @@ static const char **slirp_dnssearch(const StringList *dnsname)
return ret;
}
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
/* FIXME error_setg(errp, ...) on failure */
@@ -839,8 +839,8 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
const char **dnssearch;
bool ipv4 = true, ipv6 = true;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER);
- user = opts->u.user.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_USER);
+ user = &netdev->u.user;
if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) ||
(user->has_ipv4 && !user->ipv4)) {
diff --git a/net/socket.c b/net/socket.c
index ae6f92101d..17e635de20 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -311,7 +311,7 @@ static void net_socket_cleanup(NetClientState *nc)
}
static NetClientInfo net_dgram_socket_info = {
- .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+ .type = NET_CLIENT_DRIVER_SOCKET,
.size = sizeof(NetSocketState),
.receive = net_socket_receive_dgram,
.cleanup = net_socket_cleanup,
@@ -395,7 +395,7 @@ static void net_socket_connect(void *opaque)
}
static NetClientInfo net_socket_info = {
- .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+ .type = NET_CLIENT_DRIVER_SOCKET,
.size = sizeof(NetSocketState),
.receive = net_socket_receive,
.cleanup = net_socket_cleanup,
@@ -663,15 +663,15 @@ static int net_socket_udp_init(NetClientState *peer,
return 0;
}
-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
/* FIXME error_setg(errp, ...) on failure */
Error *err = NULL;
const NetdevSocketOptions *sock;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET);
- sock = opts->u.socket.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_SOCKET);
+ sock = &netdev->u.socket;
if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index f1e142ace6..662f9b63e1 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -750,7 +750,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
}
static NetClientInfo net_tap_win32_info = {
- .type = NET_CLIENT_OPTIONS_KIND_TAP,
+ .type = NET_CLIENT_DRIVER_TAP,
.size = sizeof(TAPState),
.receive = tap_receive,
.cleanup = tap_cleanup,
@@ -788,14 +788,14 @@ static int tap_win32_init(NetClientState *peer, const char *model,
return 0;
}
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
/* FIXME error_setg(errp, ...) on failure */
const NetdevTapOptions *tap;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
- tap = opts->u.tap.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_TAP);
+ tap = &netdev->u.tap;
if (!tap->has_ifname) {
error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index 6a2cedcfb1..40a8c741fc 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -223,7 +223,7 @@ static bool tap_has_ufo(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return s->has_ufo;
}
@@ -232,7 +232,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return !!s->host_vnet_hdr_len;
}
@@ -241,7 +241,7 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return !!tap_probe_vnet_hdr_len(s->fd, len);
}
@@ -250,7 +250,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
len == sizeof(struct virtio_net_hdr));
@@ -262,7 +262,7 @@ static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
s->using_vnet_hdr = using_vnet_hdr;
@@ -336,14 +336,14 @@ static void tap_poll(NetClientState *nc, bool enable)
int tap_get_fd(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return s->fd;
}
/* fd support */
static NetClientInfo net_tap_info = {
- .type = NET_CLIENT_OPTIONS_KIND_TAP,
+ .type = NET_CLIENT_DRIVER_TAP,
.size = sizeof(TAPState),
.receive = tap_receive,
.receive_raw = tap_receive_raw,
@@ -571,7 +571,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge,
}
}
-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
const NetdevBridgeOptions *bridge;
@@ -579,8 +579,8 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
TAPState *s;
int fd, vnet_hdr;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE);
- bridge = opts->u.bridge.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_BRIDGE);
+ bridge = &netdev->u.bridge;
helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE;
@@ -735,7 +735,7 @@ static int get_fds(char *str, char *fds[], int max)
return i;
}
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
const NetdevTapOptions *tap;
@@ -747,8 +747,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
const char *vhostfdname;
char ifname[128];
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP);
- tap = opts->u.tap.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_TAP);
+ tap = &netdev->u.tap;
queues = tap->has_queues ? tap->queues : 1;
vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
@@ -921,7 +921,7 @@ free_fail:
VHostNetState *tap_get_vhost_net(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return s->vhost_net;
}
diff --git a/net/vde.c b/net/vde.c
index 9427eaa16f..e50e5d6394 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -68,7 +68,7 @@ static void vde_cleanup(NetClientState *nc)
}
static NetClientInfo net_vde_info = {
- .type = NET_CLIENT_OPTIONS_KIND_VDE,
+ .type = NET_CLIENT_DRIVER_VDE,
.size = sizeof(VDEState),
.receive = vde_receive,
.cleanup = vde_cleanup,
@@ -109,14 +109,14 @@ static int net_vde_init(NetClientState *peer, const char *model,
return 0;
}
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
/* FIXME error_setg(errp, ...) on failure */
const NetdevVdeOptions *vde;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_VDE);
- vde = opts->u.vde.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_VDE);
+ vde = &netdev->u.vde;
/* missing optional values have been initialized to "all bits zero" */
if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index a88dfe0655..c4d63e05eb 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -34,14 +34,14 @@ typedef struct VhostUserChardevProps {
VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
{
VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+ assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
return s->vhost_net;
}
uint64_t vhost_user_get_acked_features(NetClientState *nc)
{
VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+ assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
return s->acked_features;
}
@@ -56,7 +56,7 @@ static void vhost_user_stop(int queues, NetClientState *ncs[])
int i;
for (i = 0; i < queues; i++) {
- assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+ assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
s = DO_UPCAST(VhostUserState, nc, ncs[i]);
if (!vhost_user_running(s)) {
@@ -82,7 +82,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[])
options.backend_type = VHOST_BACKEND_TYPE_USER;
for (i = 0; i < queues; i++) {
- assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+ assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
s = DO_UPCAST(VhostUserState, nc, ncs[i]);
if (vhost_user_running(s)) {
@@ -163,20 +163,20 @@ static void vhost_user_cleanup(NetClientState *nc)
static bool vhost_user_has_vnet_hdr(NetClientState *nc)
{
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+ assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
return true;
}
static bool vhost_user_has_ufo(NetClientState *nc)
{
- assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+ assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
return true;
}
static NetClientInfo net_vhost_user_info = {
- .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
+ .type = NET_CLIENT_DRIVER_VHOST_USER,
.size = sizeof(VhostUserState),
.receive = vhost_user_receive,
.cleanup = vhost_user_cleanup,
@@ -207,7 +207,7 @@ static void net_vhost_user_event(void *opaque, int event)
int queues;
queues = qemu_find_net_clients_except(name, ncs,
- NET_CLIENT_OPTIONS_KIND_NIC,
+ NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
assert(queues < MAX_QUEUE_NUM);
@@ -334,15 +334,15 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
return 0;
}
-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const Netdev *netdev, const char *name,
NetClientState *peer, Error **errp)
{
int queues;
const NetdevVhostUserOptions *vhost_user_opts;
CharDriverState *chr;
- assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
- vhost_user_opts = opts->u.vhost_user.data;
+ assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
+ vhost_user_opts = &netdev->u.vhost_user;
chr = net_vhost_parse_chardev(vhost_user_opts, errp);
if (!chr) {
diff --git a/qapi-schema.json b/qapi-schema.json
index d2d650673b..5658723b37 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2809,16 +2809,32 @@
'*queues': 'int' } }
##
-# @NetClientOptions
+# @NetClientDriver
#
-# A discriminated record of network device traits.
+# Available netdev drivers.
+#
+# Since 2.7
+##
+{ 'enum': 'NetClientDriver',
+ 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', 'dump',
+ 'bridge', 'hubport', 'netmap', 'vhost-user' ] }
+
+##
+# @Netdev
+#
+# Captures the configuration of a network device.
+#
+# @id: identifier for monitor commands.
+#
+# @type: Specify the driver used for interpreting remaining arguments.
#
# Since 1.2
#
# 'l2tpv3' - since 2.1
-#
##
-{ 'union': 'NetClientOptions',
+{ 'union': 'Netdev',
+ 'base': { 'id': 'str', 'type': 'NetClientDriver' },
+ 'discriminator': 'type',
'data': {
'none': 'NetdevNoneOptions',
'nic': 'NetLegacyNicOptions',
@@ -2853,23 +2869,28 @@
'*vlan': 'int32',
'*id': 'str',
'*name': 'str',
- 'opts': 'NetClientOptions' } }
+ 'opts': 'NetLegacyOptions' } }
##
-# @Netdev
+# @NetLegacyOptions
#
-# Captures the configuration of a network device.
-#
-# @id: identifier for monitor commands.
-#
-# @opts: device type specific properties
+# Like Netdev, but for use only by the legacy command line options
#
# Since 1.2
##
-{ 'struct': 'Netdev',
+{ 'union': 'NetLegacyOptions',
'data': {
- 'id': 'str',
- 'opts': 'NetClientOptions' } }
+ 'none': 'NetdevNoneOptions',
+ 'nic': 'NetLegacyNicOptions',
+ 'user': 'NetdevUserOptions',
+ 'tap': 'NetdevTapOptions',
+ 'l2tpv3': 'NetdevL2TPv3Options',
+ 'socket': 'NetdevSocketOptions',
+ 'vde': 'NetdevVdeOptions',
+ 'dump': 'NetdevDumpOptions',
+ 'bridge': 'NetdevBridgeOptions',
+ 'netmap': 'NetdevNetmapOptions',
+ 'vhost-user': 'NetdevVhostUserOptions' } }
##
# @NetFilterDirection
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 3444a9bc3e..f462345ca3 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1120,6 +1120,21 @@
#
# Start mirroring a block device's writes to a new destination.
#
+# See DriveMirror for parameter descriptions
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.3
+##
+{ 'command': 'drive-mirror', 'boxed': true,
+ 'data': 'DriveMirror' }
+
+##
+# DriveMirror
+#
+# A set of parameters describing drive mirror setup.
+#
# @job-id: #optional identifier for the newly-created block job. If
# omitted, the device name will be used. (Since 2.7)
#
@@ -1169,12 +1184,9 @@
# written. Both will result in identical contents.
# Default is true. (Since 2.4)
#
-# Returns: nothing on success
-# If @device is not a valid block device, DeviceNotFound
-#
# Since 1.3
##
-{ 'command': 'drive-mirror',
+{ 'struct': 'DriveMirror',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'*format': 'str', '*node-name': 'str', '*replaces': 'str',
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
@@ -1330,6 +1342,21 @@
# the device will be removed from its group and the rest of its
# members will not be affected. The 'group' parameter is ignored.
#
+# See BlockIOThrottle for parameter descriptions.
+#
+# Returns: Nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since: 1.1
+##
+{ 'command': 'block_set_io_throttle', 'boxed': true,
+ 'data': 'BlockIOThrottle' }
+
+##
+# BlockIOThrottle
+#
+# A set of parameters describing block throttling.
+#
# @device: The name of the device
#
# @bps: total throughput limit in bytes per second
@@ -1396,12 +1423,9 @@
#
# @group: #optional throttle group name (Since 2.4)
#
-# Returns: Nothing on success
-# If @device is not a valid block device, DeviceNotFound
-#
# Since: 1.1
##
-{ 'command': 'block_set_io_throttle',
+{ 'struct': 'BlockIOThrottle',
'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
'*bps_max': 'int', '*bps_rd_max': 'int',
@@ -1666,13 +1690,14 @@
# @host_device, @host_cdrom: Since 2.1
#
# Since: 2.0
+# @gluster: Since 2.7
##
{ 'enum': 'BlockdevDriver',
'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop',
- 'dmg', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device',
- 'http', 'https', 'luks', 'null-aio', 'null-co', 'parallels',
- 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'tftp', 'vdi', 'vhdx',
- 'vmdk', 'vpc', 'vvfat' ] }
+ 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
+ 'host_device', 'http', 'https', 'luks', 'null-aio', 'null-co',
+ 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'tftp',
+ 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
##
# @BlockdevOptionsFile
@@ -2065,6 +2090,63 @@
'*read-pattern': 'QuorumReadPattern' } }
##
+# @GlusterTransport
+#
+# An enumeration of Gluster transport types
+#
+# @tcp: TCP - Transmission Control Protocol
+#
+# @unix: UNIX - Unix domain socket
+#
+# Since: 2.7
+##
+{ 'enum': 'GlusterTransport',
+ 'data': [ 'unix', 'tcp' ] }
+
+
+##
+# @GlusterServer
+#
+# Captures the address of a socket
+#
+# Details for connecting to a gluster server
+#
+# @type: Transport type used for gluster connection
+#
+# @unix: socket file
+#
+# @tcp: host address and port number
+#
+# Since: 2.7
+##
+{ 'union': 'GlusterServer',
+ 'base': { 'type': 'GlusterTransport' },
+ 'discriminator': 'type',
+ 'data': { 'unix': 'UnixSocketAddress',
+ 'tcp': 'InetSocketAddress' } }
+
+##
+# @BlockdevOptionsGluster
+#
+# Driver specific block device options for Gluster
+#
+# @volume: name of gluster volume where VM image resides
+#
+# @path: absolute path to image file in gluster volume
+#
+# @server: gluster server description
+#
+# @debug-level: #optional libgfapi log level (default '4' which is Error)
+#
+# Since: 2.7
+##
+{ 'struct': 'BlockdevOptionsGluster',
+ 'data': { 'volume': 'str',
+ 'path': 'str',
+ 'server': ['GlusterServer'],
+ '*debug_level': 'int' } }
+
+##
# @BlockdevOptions
#
# Options for creating a block device. Many options are available for all
@@ -2111,7 +2193,7 @@
'file': 'BlockdevOptionsFile',
'ftp': 'BlockdevOptionsFile',
'ftps': 'BlockdevOptionsFile',
-# TODO gluster: Wait for structured options
+ 'gluster': 'BlockdevOptionsGluster',
'host_cdrom': 'BlockdevOptionsFile',
'host_device':'BlockdevOptionsFile',
'http': 'BlockdevOptionsFile',
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 21edb3928e..64dd392e6f 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -30,6 +30,8 @@ typedef struct StackObject
GHashTable *h; /* If obj is dict: unvisited keys */
const QListEntry *entry; /* If obj is list: unvisited tail */
+
+ QSLIST_ENTRY(StackObject) node;
} StackObject;
struct QmpInputVisitor
@@ -41,8 +43,7 @@ struct QmpInputVisitor
/* Stack of objects being visited (all entries will be either
* QDict or QList). */
- StackObject stack[QIV_STACK_SIZE];
- int nb_stack;
+ QSLIST_HEAD(, StackObject) stack;
/* True to reject parse in visit_end_struct() if unvisited keys remain. */
bool strict;
@@ -61,13 +62,13 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
QObject *qobj;
QObject *ret;
- if (!qiv->nb_stack) {
+ if (QSLIST_EMPTY(&qiv->stack)) {
/* Starting at root, name is ignored. */
return qiv->root;
}
/* We are in a container; find the next element. */
- tos = &qiv->stack[qiv->nb_stack - 1];
+ tos = QSLIST_FIRST(&qiv->stack);
qobj = tos->obj;
assert(qobj);
@@ -100,18 +101,11 @@ static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
void *qapi, Error **errp)
{
GHashTable *h;
- StackObject *tos = &qiv->stack[qiv->nb_stack];
+ StackObject *tos = g_new0(StackObject, 1);
assert(obj);
- if (qiv->nb_stack >= QIV_STACK_SIZE) {
- error_setg(errp, "An internal buffer overran");
- return NULL;
- }
-
tos->obj = obj;
tos->qapi = qapi;
- assert(!tos->h);
- assert(!tos->entry);
if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
h = g_hash_table_new(g_str_hash, g_str_equal);
@@ -121,7 +115,7 @@ static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
tos->entry = qlist_first(qobject_to_qlist(obj));
}
- qiv->nb_stack++;
+ QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
return tos->entry;
}
@@ -129,10 +123,9 @@ static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
static void qmp_input_check_struct(Visitor *v, Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
- StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
-
- assert(qiv->nb_stack > 0);
+ StackObject *tos = QSLIST_FIRST(&qiv->stack);
+ assert(tos && !tos->entry);
if (qiv->strict) {
GHashTable *const top_ht = tos->h;
if (top_ht) {
@@ -147,23 +140,23 @@ static void qmp_input_check_struct(Visitor *v, Error **errp)
}
}
-static void qmp_input_pop(Visitor *v, void **obj)
+static void qmp_input_stack_object_free(StackObject *tos)
{
- QmpInputVisitor *qiv = to_qiv(v);
- StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
+ if (tos->h) {
+ g_hash_table_unref(tos->h);
+ }
- assert(qiv->nb_stack > 0);
- assert(tos->qapi == obj);
+ g_free(tos);
+}
- if (qiv->strict) {
- GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
- if (top_ht) {
- g_hash_table_unref(top_ht);
- }
- tos->h = NULL;
- }
+static void qmp_input_pop(Visitor *v, void **obj)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ StackObject *tos = QSLIST_FIRST(&qiv->stack);
- qiv->nb_stack--;
+ assert(tos && tos->qapi == obj);
+ QSLIST_REMOVE_HEAD(&qiv->stack, node);
+ qmp_input_stack_object_free(tos);
}
static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
@@ -224,7 +217,7 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail,
size_t size)
{
QmpInputVisitor *qiv = to_qiv(v);
- StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+ StackObject *so = QSLIST_FIRST(&qiv->stack);
if (!so->entry) {
return NULL;
@@ -376,6 +369,12 @@ static void qmp_input_optional(Visitor *v, const char *name, bool *present)
static void qmp_input_free(Visitor *v)
{
QmpInputVisitor *qiv = to_qiv(v);
+ while (!QSLIST_EMPTY(&qiv->stack)) {
+ StackObject *tos = QSLIST_FIRST(&qiv->stack);
+
+ QSLIST_REMOVE_HEAD(&qiv->stack, node);
+ qmp_input_stack_object_free(tos);
+ }
qobject_decref(qiv->root);
g_free(qiv);
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 0452056d42..9e3b67ce13 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -23,15 +23,13 @@ typedef struct QStackEntry
{
QObject *value;
void *qapi; /* sanity check that caller uses same pointer */
- QTAILQ_ENTRY(QStackEntry) node;
+ QSLIST_ENTRY(QStackEntry) node;
} QStackEntry;
-typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
-
struct QmpOutputVisitor
{
Visitor visitor;
- QStack stack; /* Stack of containers that haven't yet been finished */
+ QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */
QObject *root; /* Root of the output visit */
QObject **result; /* User's storage location for result */
};
@@ -56,18 +54,18 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value,
assert(value);
e->value = value;
e->qapi = qapi;
- QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+ QSLIST_INSERT_HEAD(&qov->stack, e, node);
}
/* Pop a value off the stack of QObjects being built, and return it. */
static QObject *qmp_output_pop(QmpOutputVisitor *qov, void *qapi)
{
- QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QStackEntry *e = QSLIST_FIRST(&qov->stack);
QObject *value;
assert(e);
assert(e->qapi == qapi);
- QTAILQ_REMOVE(&qov->stack, e, node);
+ QSLIST_REMOVE_HEAD(&qov->stack, node);
value = e->value;
assert(value);
g_free(e);
@@ -80,7 +78,7 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov, void *qapi)
static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
QObject *value)
{
- QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+ QStackEntry *e = QSLIST_FIRST(&qov->stack);
QObject *cur = e ? e->value : NULL;
if (!cur) {
@@ -206,7 +204,7 @@ static void qmp_output_complete(Visitor *v, void *opaque)
QmpOutputVisitor *qov = to_qov(v);
/* A visit must have occurred, with each start paired with end. */
- assert(qov->root && QTAILQ_EMPTY(&qov->stack));
+ assert(qov->root && QSLIST_EMPTY(&qov->stack));
assert(opaque == qov->result);
qobject_incref(qov->root);
@@ -217,10 +215,11 @@ static void qmp_output_complete(Visitor *v, void *opaque)
static void qmp_output_free(Visitor *v)
{
QmpOutputVisitor *qov = to_qov(v);
- QStackEntry *e, *tmp;
+ QStackEntry *e;
- QTAILQ_FOREACH_SAFE(e, &qov->stack, node, tmp) {
- QTAILQ_REMOVE(&qov->stack, e, node);
+ while (!QSLIST_EMPTY(&qov->stack)) {
+ e = QSLIST_FIRST(&qov->stack);
+ QSLIST_REMOVE_HEAD(&qov->stack, node);
g_free(e);
}
@@ -250,7 +249,6 @@ Visitor *qmp_output_visitor_new(QObject **result)
v->visitor.complete = qmp_output_complete;
v->visitor.free = qmp_output_free;
- QTAILQ_INIT(&v->stack);
*result = NULL;
v->result = result;
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 6e29edb1fd..25954f5634 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -1696,8 +1696,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
}
gettimeofday(&t1, NULL);
- ret = blk_discard(blk, offset >> BDRV_SECTOR_BITS,
- count >> BDRV_SECTOR_BITS);
+ ret = blk_pdiscard(blk, offset, count);
gettimeofday(&t2, NULL);
if (ret < 0) {
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 496d73c275..c8d360ad36 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -5026,3 +5026,18 @@ Example for pseries machine type started with
{ "props": { "core-id": 0 }, "type": "POWER8-spapr-cpu-core",
"vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"}
]}'
+
+Example for pc machine type started with
+-smp 1,maxcpus=2:
+ -> { "execute": "query-hotpluggable-cpus" }
+ <- {"return": [
+ {
+ "type": "qemu64-x86_64-cpu", "vcpus-count": 1,
+ "props": {"core-id": 0, "socket-id": 1, "thread-id": 0}
+ },
+ {
+ "qom-path": "/machine/unattached/device[0]",
+ "type": "qemu64-x86_64-cpu", "vcpus-count": 1,
+ "props": {"core-id": 0, "socket-id": 0, "thread-id": 0}
+ }
+ ]}
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 34b6a3a07f..a06a2c4f9b 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -16,20 +16,23 @@ from qapi import *
import re
-def gen_command_decl(name, arg_type, ret_type):
+def gen_command_decl(name, arg_type, boxed, ret_type):
return mcgen('''
%(c_type)s qmp_%(c_name)s(%(params)s);
''',
c_type=(ret_type and ret_type.c_type()) or 'void',
c_name=c_name(name),
- params=gen_params(arg_type, 'Error **errp'))
+ params=gen_params(arg_type, boxed, 'Error **errp'))
-def gen_call(name, arg_type, ret_type):
+def gen_call(name, arg_type, boxed, ret_type):
ret = ''
argstr = ''
- if arg_type:
+ if boxed:
+ assert arg_type and not arg_type.is_empty()
+ argstr = '&arg, '
+ elif arg_type:
assert not arg_type.variants
for memb in arg_type.members:
if memb.optional:
@@ -46,8 +49,10 @@ def gen_call(name, arg_type, ret_type):
''',
c_name=c_name(name), args=argstr, lhs=lhs)
if ret_type:
- ret += gen_err_check()
ret += mcgen('''
+ if (err) {
+ goto out;
+ }
qmp_marshal_output_%(c_name)s(retval, ret, &err);
''',
@@ -92,7 +97,7 @@ def gen_marshal_decl(name):
proto=gen_marshal_proto(name))
-def gen_marshal(name, arg_type, ret_type):
+def gen_marshal(name, arg_type, boxed, ret_type):
ret = mcgen('''
%(proto)s
@@ -107,7 +112,7 @@ def gen_marshal(name, arg_type, ret_type):
''',
c_type=ret_type.c_type())
- if arg_type and arg_type.members:
+ if arg_type and not arg_type.is_empty():
ret += mcgen('''
Visitor *v;
%(c_name)s arg = {0};
@@ -134,10 +139,10 @@ def gen_marshal(name, arg_type, ret_type):
(void)args;
''')
- ret += gen_call(name, arg_type, ret_type)
+ ret += gen_call(name, arg_type, boxed, ret_type)
# 'goto out' produced above for arg_type, and by gen_call() for ret_type
- if (arg_type and arg_type.members) or ret_type:
+ if (arg_type and not arg_type.is_empty()) or ret_type:
ret += mcgen('''
out:
@@ -145,7 +150,7 @@ out:
ret += mcgen('''
error_propagate(errp, err);
''')
- if arg_type and arg_type.members:
+ if arg_type and not arg_type.is_empty():
ret += mcgen('''
visit_free(v);
v = qapi_dealloc_visitor_new();
@@ -210,16 +215,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
self._visited_ret_types = None
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, boxed):
if not gen:
return
- self.decl += gen_command_decl(name, arg_type, ret_type)
+ self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
if ret_type and ret_type not in self._visited_ret_types:
self._visited_ret_types.add(ret_type)
self.defn += gen_marshal_output(ret_type)
if middle_mode:
self.decl += gen_marshal_decl(name)
- self.defn += gen_marshal(name, arg_type, ret_type)
+ self.defn += gen_marshal(name, arg_type, boxed, ret_type)
if not middle_mode:
self._regy += gen_register_command(name, success_response)
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 9c88627c9f..38d82111ad 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -14,18 +14,18 @@
from qapi import *
-def gen_event_send_proto(name, arg_type):
+def gen_event_send_proto(name, arg_type, boxed):
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
'c_name': c_name(name.lower()),
- 'param': gen_params(arg_type, 'Error **errp')}
+ 'param': gen_params(arg_type, boxed, 'Error **errp')}
-def gen_event_send_decl(name, arg_type):
+def gen_event_send_decl(name, arg_type, boxed):
return mcgen('''
%(proto)s;
''',
- proto=gen_event_send_proto(name, arg_type))
+ proto=gen_event_send_proto(name, arg_type, boxed))
# Declare and initialize an object 'qapi' using parameters from gen_params()
@@ -49,10 +49,15 @@ def gen_param_var(typ):
};
''')
+ if not typ.is_implicit():
+ ret += mcgen('''
+ %(c_name)s *arg = &param;
+''',
+ c_name=typ.c_name())
return ret
-def gen_event_send(name, arg_type):
+def gen_event_send(name, arg_type, boxed):
# FIXME: Our declaration of local variables (and of 'errp' in the
# parameter list) can collide with exploded members of the event's
# data type passed in as parameters. If this collision ever hits in
@@ -67,14 +72,17 @@ def gen_event_send(name, arg_type):
Error *err = NULL;
QMPEventFuncEmit emit;
''',
- proto=gen_event_send_proto(name, arg_type))
+ proto=gen_event_send_proto(name, arg_type, boxed))
- if arg_type and arg_type.members:
+ if arg_type and not arg_type.is_empty():
ret += mcgen('''
QObject *obj;
Visitor *v;
''')
- ret += gen_param_var(arg_type)
+ if not boxed:
+ ret += gen_param_var(arg_type)
+ else:
+ assert not boxed
ret += mcgen('''
@@ -88,9 +96,17 @@ def gen_event_send(name, arg_type):
''',
name=name)
- if arg_type and arg_type.members:
+ if arg_type and not arg_type.is_empty():
ret += mcgen('''
v = qmp_output_visitor_new(&obj);
+''')
+ if not arg_type.is_implicit():
+ ret += mcgen('''
+ visit_type_%(c_name)s(v, "%(name)s", &arg, &err);
+''',
+ name=name, c_name=arg_type.c_name())
+ else:
+ ret += mcgen('''
visit_start_struct(v, "%(name)s", NULL, 0, &err);
if (err) {
@@ -101,14 +117,16 @@ def gen_event_send(name, arg_type):
visit_check_struct(v, &err);
}
visit_end_struct(v, NULL);
+''',
+ name=name, c_name=arg_type.c_name())
+ ret += mcgen('''
if (err) {
goto out;
}
visit_complete(v, &obj);
qdict_put_obj(qmp, "data", obj);
-''',
- name=name, c_name=arg_type.c_name())
+''')
ret += mcgen('''
emit(%(c_enum)s, qmp, &err);
@@ -116,7 +134,7 @@ def gen_event_send(name, arg_type):
''',
c_enum=c_enum_const(event_enum_name, name))
- if arg_type and arg_type.members:
+ if arg_type and not arg_type.is_empty():
ret += mcgen('''
out:
visit_free(v);
@@ -145,9 +163,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
self.defn += gen_enum_lookup(event_enum_name, self._event_names)
self._event_names = None
- def visit_event(self, name, info, arg_type):
- self.decl += gen_event_send_decl(name, arg_type)
- self.defn += gen_event_send(name, arg_type)
+ def visit_event(self, name, info, arg_type, boxed):
+ self.decl += gen_event_send_decl(name, arg_type, boxed)
+ self.defn += gen_event_send(name, arg_type, boxed)
self._event_names.append(name)
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index e0f926be04..541644e350 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -154,14 +154,14 @@ const char %(c_name)s[] = %(c_string)s;
for m in variants.variants]})
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
self._gen_json(name, 'command',
{'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type)})
- def visit_event(self, name, info, arg_type):
+ def visit_event(self, name, info, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5ace2cf065..dabc42e047 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -91,7 +91,7 @@ struct %(c_name)s {
# potential issues with attempting to malloc space for zero-length
# structs in C, and also incompatibility with C++ (where an empty
# struct is size 1).
- if not (base and base.members) and not members and not variants:
+ if (not base or base.is_empty()) and not members and not variants:
ret += mcgen('''
char qapi_dummy_for_empty_struct;
''')
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 0b9e298bc4..96f2491c16 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -47,9 +47,11 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
if base:
ret += mcgen('''
visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
+ if (err) {
+ goto out;
+ }
''',
c_type=base.c_name())
- ret += gen_err_check()
for memb in members:
if memb.optional:
@@ -60,10 +62,12 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
push_indent()
ret += mcgen('''
visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err);
+ if (err) {
+ goto out;
+ }
''',
c_type=memb.type.c_name(), name=memb.name,
c_name=c_name(memb.name))
- ret += gen_err_check()
if memb.optional:
pop_indent()
ret += mcgen('''
diff --git a/scripts/qapi.py b/scripts/qapi.py
index b13ae47899..21bc32fda3 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -522,10 +522,14 @@ def check_type(expr_info, source, value, allow_array=False,
def check_command(expr, expr_info):
name = expr['command']
+ boxed = expr.get('boxed', False)
+ args_meta = ['struct']
+ if boxed:
+ args_meta += ['union', 'alternate']
check_type(expr_info, "'data' for command '%s'" % name,
- expr.get('data'), allow_dict=True, allow_optional=True,
- allow_metas=['struct'])
+ expr.get('data'), allow_dict=not boxed, allow_optional=True,
+ allow_metas=args_meta)
returns_meta = ['union', 'struct']
if name in returns_whitelist:
returns_meta += ['built-in', 'alternate', 'enum']
@@ -537,11 +541,15 @@ def check_command(expr, expr_info):
def check_event(expr, expr_info):
global events
name = expr['event']
+ boxed = expr.get('boxed', False)
+ meta = ['struct']
+ if boxed:
+ meta += ['union', 'alternate']
events.append(name)
check_type(expr_info, "'data' for event '%s'" % name,
- expr.get('data'), allow_dict=True, allow_optional=True,
- allow_metas=['struct'])
+ expr.get('data'), allow_dict=not boxed, allow_optional=True,
+ allow_metas=meta)
def check_union(expr, expr_info):
@@ -612,6 +620,14 @@ def check_union(expr, expr_info):
"enum '%s'" %
(key, enum_define["enum_name"]))
+ # If discriminator is user-defined, ensure all values are covered
+ if enum_define:
+ for value in enum_define['enum_values']:
+ if value not in members.keys():
+ raise QAPIExprError(expr_info,
+ "Union '%s' data missing '%s' branch"
+ % (name, value))
+
def check_alternate(expr, expr_info):
name = expr['alternate']
@@ -686,6 +702,10 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPIExprError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
+ if key == 'boxed' and value is not True:
+ raise QAPIExprError(info,
+ "'%s' of %s '%s' should only use true value"
+ % (key, meta, name))
for key in required:
if key not in expr:
raise QAPIExprError(info,
@@ -717,10 +737,10 @@ def check_exprs(exprs):
add_struct(expr, info)
elif 'command' in expr:
check_keys(expr_elem, 'command', [],
- ['data', 'returns', 'gen', 'success-response'])
+ ['data', 'returns', 'gen', 'success-response', 'boxed'])
add_name(expr['command'], info, 'command')
elif 'event' in expr:
- check_keys(expr_elem, 'event', [], ['data'])
+ check_keys(expr_elem, 'event', [], ['data', 'boxed'])
add_name(expr['event'], info, 'event')
else:
raise QAPIExprError(expr_elem['info'],
@@ -818,10 +838,10 @@ class QAPISchemaVisitor(object):
pass
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, boxed):
pass
- def visit_event(self, name, info, arg_type):
+ def visit_event(self, name, info, arg_type, boxed):
pass
@@ -991,7 +1011,12 @@ class QAPISchemaObjectType(QAPISchemaType):
# _def_predefineds()
return self.name.startswith('q_')
+ def is_empty(self):
+ assert self.members is not None
+ return not self.members and not self.variants
+
def c_name(self):
+ assert self.name != 'q_empty'
return QAPISchemaType.c_name(self)
def c_type(self):
@@ -1084,7 +1109,7 @@ class QAPISchemaObjectTypeVariants(object):
assert len(variants) > 0
for v in variants:
assert isinstance(v, QAPISchemaObjectTypeVariant)
- self.tag_name = tag_name
+ self._tag_name = tag_name
self.tag_member = tag_member
self.variants = variants
@@ -1094,8 +1119,8 @@ class QAPISchemaObjectTypeVariants(object):
def check(self, schema, seen):
if not self.tag_member: # flat union
- self.tag_member = seen[c_name(self.tag_name)]
- assert self.tag_name == self.tag_member.name
+ self.tag_member = seen[c_name(self._tag_name)]
+ assert self._tag_name == self.tag_member.name
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
for v in self.variants:
v.check(schema)
@@ -1125,7 +1150,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
def __init__(self, name, info, variants):
QAPISchemaType.__init__(self, name, info)
assert isinstance(variants, QAPISchemaObjectTypeVariants)
- assert not variants.tag_name
+ assert variants.tag_member
variants.set_owner(name)
variants.tag_member.set_owner(self.name)
self.variants = variants
@@ -1150,9 +1175,13 @@ class QAPISchemaAlternateType(QAPISchemaType):
def visit(self, visitor):
visitor.visit_alternate_type(self.name, self.info, self.variants)
+ def is_empty(self):
+ return False
+
class QAPISchemaCommand(QAPISchemaEntity):
- def __init__(self, name, info, arg_type, ret_type, gen, success_response):
+ def __init__(self, name, info, arg_type, ret_type, gen, success_response,
+ boxed):
QAPISchemaEntity.__init__(self, name, info)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
@@ -1162,12 +1191,24 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.ret_type = None
self.gen = gen
self.success_response = success_response
+ self.boxed = boxed
def check(self, schema):
if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name)
- assert isinstance(self.arg_type, QAPISchemaObjectType)
- assert not self.arg_type.variants # not implemented
+ assert (isinstance(self.arg_type, QAPISchemaObjectType) or
+ isinstance(self.arg_type, QAPISchemaAlternateType))
+ self.arg_type.check(schema)
+ if self.boxed:
+ if self.arg_type.is_empty():
+ raise QAPIExprError(self.info,
+ "Cannot use 'boxed' with empty type")
+ else:
+ assert not isinstance(self.arg_type, QAPISchemaAlternateType)
+ assert not self.arg_type.variants
+ elif self.boxed:
+ raise QAPIExprError(self.info,
+ "Use of 'boxed' requires 'data'")
if self._ret_type_name:
self.ret_type = schema.lookup_type(self._ret_type_name)
assert isinstance(self.ret_type, QAPISchemaType)
@@ -1175,24 +1216,36 @@ class QAPISchemaCommand(QAPISchemaEntity):
def visit(self, visitor):
visitor.visit_command(self.name, self.info,
self.arg_type, self.ret_type,
- self.gen, self.success_response)
+ self.gen, self.success_response, self.boxed)
class QAPISchemaEvent(QAPISchemaEntity):
- def __init__(self, name, info, arg_type):
+ def __init__(self, name, info, arg_type, boxed):
QAPISchemaEntity.__init__(self, name, info)
assert not arg_type or isinstance(arg_type, str)
self._arg_type_name = arg_type
self.arg_type = None
+ self.boxed = boxed
def check(self, schema):
if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name)
- assert isinstance(self.arg_type, QAPISchemaObjectType)
- assert not self.arg_type.variants # not implemented
+ assert (isinstance(self.arg_type, QAPISchemaObjectType) or
+ isinstance(self.arg_type, QAPISchemaAlternateType))
+ self.arg_type.check(schema)
+ if self.boxed:
+ if self.arg_type.is_empty():
+ raise QAPIExprError(self.info,
+ "Cannot use 'boxed' with empty type")
+ else:
+ assert not isinstance(self.arg_type, QAPISchemaAlternateType)
+ assert not self.arg_type.variants
+ elif self.boxed:
+ raise QAPIExprError(self.info,
+ "Use of 'boxed' requires 'data'")
def visit(self, visitor):
- visitor.visit_event(self.name, self.info, self.arg_type)
+ visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
class QAPISchema(object):
@@ -1368,6 +1421,7 @@ class QAPISchema(object):
rets = expr.get('returns')
gen = expr.get('gen', True)
success_response = expr.get('success-response', True)
+ boxed = expr.get('boxed', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, 'arg', self._make_members(data, info))
@@ -1375,15 +1429,16 @@ class QAPISchema(object):
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
- success_response))
+ success_response, boxed))
def _def_event(self, expr, info):
name = expr['event']
data = expr.get('data')
+ boxed = expr.get('boxed', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, 'arg', self._make_members(data, info))
- self._def_entity(QAPISchemaEvent(name, info, data))
+ self._def_entity(QAPISchemaEvent(name, info, data, boxed))
def _def_exprs(self):
for expr_elem in self.exprs:
@@ -1626,31 +1681,29 @@ extern const char *const %(c_name)s_lookup[];
return ret
-def gen_params(arg_type, extra):
+def gen_params(arg_type, boxed, extra):
if not arg_type:
+ assert not boxed
return extra
- assert not arg_type.variants
ret = ''
sep = ''
- for memb in arg_type.members:
- ret += sep
+ if boxed:
+ ret += '%s arg' % arg_type.c_param_type()
sep = ', '
- if memb.optional:
- ret += 'bool has_%s, ' % c_name(memb.name)
- ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name))
+ else:
+ assert not arg_type.variants
+ for memb in arg_type.members:
+ ret += sep
+ sep = ', '
+ if memb.optional:
+ ret += 'bool has_%s, ' % c_name(memb.name)
+ ret += '%s %s' % (memb.type.c_param_type(),
+ c_name(memb.name))
if extra:
ret += sep + extra
return ret
-def gen_err_check():
- return mcgen('''
- if (err) {
- goto out;
- }
-''')
-
-
#
# Common command line parsing
#
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 2dbeb826cd..7a6ca31a8e 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -340,10 +340,9 @@ const char *gicv3_class_name(void)
#else
error_report("KVM GICv3 acceleration is not supported on this "
"platform");
+ exit(1);
#endif
} else {
return "arm-gicv3";
}
-
- exit(1);
}
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 6e49e4ca82..6a1afab595 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -305,12 +305,12 @@ static const char *cpuid_7_0_ebx_feature_name[] = {
};
static const char *cpuid_7_0_ecx_feature_name[] = {
- NULL, NULL, NULL, "pku",
+ NULL, NULL, "umip", "pku",
"ospke", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
+ NULL, NULL, "rdpid", NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
};
@@ -1893,50 +1893,6 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, const char *name,
cpu->env.tsc_khz = cpu->env.user_tsc_khz = value / 1000;
}
-static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- X86CPU *cpu = X86_CPU(obj);
- int64_t value = cpu->apic_id;
-
- visit_type_int(v, name, &value, errp);
-}
-
-static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- X86CPU *cpu = X86_CPU(obj);
- DeviceState *dev = DEVICE(obj);
- const int64_t min = 0;
- const int64_t max = UINT32_MAX;
- Error *error = NULL;
- int64_t value;
-
- if (dev->realized) {
- error_setg(errp, "Attempt to set property '%s' on '%s' after "
- "it was realized", name, object_get_typename(obj));
- return;
- }
-
- visit_type_int(v, name, &value, &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
- if (value < min || value > max) {
- error_setg(errp, "Property %s.%s doesn't take value %" PRId64
- " (minimum: %" PRId64 ", maximum: %" PRId64 ")" ,
- object_get_typename(obj), name, value, min, max);
- return;
- }
-
- if ((value != cpu->apic_id) && cpu_exists(value)) {
- error_setg(errp, "CPU with APIC ID %" PRIi64 " exists", value);
- return;
- }
- cpu->apic_id = value;
-}
-
/* Generic getter for "feature-words" and "filtered-features" properties */
static void x86_cpu_get_feature_words(Object *obj, Visitor *v,
const char *name, void *opaque,
@@ -2641,17 +2597,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
break;
case 0x80000008:
/* virtual & phys address size in low 2 bytes. */
-/* XXX: This value must match the one used in the MMU code. */
if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
- /* 64 bit processor */
-/* XXX: The physical address space is limited to 42 bits in exec.c. */
- *eax = 0x00003028; /* 48 bits virtual, 40 bits physical */
+ /* 64 bit processor, 48 bits virtual, configurable
+ * physical bits.
+ */
+ *eax = 0x00003000 + cpu->phys_bits;
} else {
- if (env->features[FEAT_1_EDX] & CPUID_PSE36) {
- *eax = 0x00000024; /* 36 bits physical */
- } else {
- *eax = 0x00000020; /* 32 bits physical */
- }
+ *eax = cpu->phys_bits;
}
*ebx = 0;
*ecx = 0;
@@ -2874,8 +2826,10 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
cpu->apic_state = DEVICE(object_new(apic_type));
- object_property_add_child(OBJECT(cpu), "apic",
- OBJECT(cpu->apic_state), NULL);
+ object_property_add_child(OBJECT(cpu), "lapic",
+ OBJECT(cpu->apic_state), &error_abort);
+ object_unref(OBJECT(cpu->apic_state));
+
qdev_prop_set_uint8(cpu->apic_state, "id", cpu->apic_id);
/* TODO: convert to link<> */
apic = APIC_COMMON(cpu->apic_state);
@@ -2926,6 +2880,31 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
}
#endif
+/* Note: Only safe for use on x86(-64) hosts */
+static uint32_t x86_host_phys_bits(void)
+{
+ uint32_t eax;
+ uint32_t host_phys_bits;
+
+ host_cpuid(0x80000000, 0, &eax, NULL, NULL, NULL);
+ if (eax >= 0x80000008) {
+ host_cpuid(0x80000008, 0, &eax, NULL, NULL, NULL);
+ /* Note: According to AMD doc 25481 rev 2.34 they have a field
+ * at 23:16 that can specify a maximum physical address bits for
+ * the guest that can override this value; but I've not seen
+ * anything with that set.
+ */
+ host_phys_bits = eax & 0xff;
+ } else {
+ /* It's an odd 64 bit machine that doesn't have the leaf for
+ * physical address bits; fall back to 36 that's most older
+ * Intel.
+ */
+ host_phys_bits = 36;
+ }
+
+ return host_phys_bits;
+}
#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
(env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
@@ -2950,7 +2929,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
goto out;
}
- if (cpu->apic_id < 0) {
+ if (cpu->apic_id == UNASSIGNED_APIC_ID) {
error_setg(errp, "apic-id property was not initialized properly");
return;
}
@@ -2993,7 +2972,70 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
& CPUID_EXT2_AMD_ALIASES);
}
+ /* For 64bit systems think about the number of physical bits to present.
+ * ideally this should be the same as the host; anything other than matching
+ * the host can cause incorrect guest behaviour.
+ * QEMU used to pick the magic value of 40 bits that corresponds to
+ * consumer AMD devices but nothing else.
+ */
+ if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
+ if (kvm_enabled()) {
+ uint32_t host_phys_bits = x86_host_phys_bits();
+ static bool warned;
+
+ if (cpu->host_phys_bits) {
+ /* The user asked for us to use the host physical bits */
+ cpu->phys_bits = host_phys_bits;
+ }
+
+ /* Print a warning if the user set it to a value that's not the
+ * host value.
+ */
+ if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 &&
+ !warned) {
+ error_report("Warning: Host physical bits (%u)"
+ " does not match phys-bits property (%u)",
+ host_phys_bits, cpu->phys_bits);
+ warned = true;
+ }
+
+ if (cpu->phys_bits &&
+ (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
+ cpu->phys_bits < 32)) {
+ error_setg(errp, "phys-bits should be between 32 and %u "
+ " (but is %u)",
+ TARGET_PHYS_ADDR_SPACE_BITS, cpu->phys_bits);
+ return;
+ }
+ } else {
+ if (cpu->phys_bits && cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
+ error_setg(errp, "TCG only supports phys-bits=%u",
+ TCG_PHYS_ADDR_BITS);
+ return;
+ }
+ }
+ /* 0 means it was not explicitly set by the user (or by machine
+ * compat_props or by the host code above). In this case, the default
+ * is the value used by TCG (40).
+ */
+ if (cpu->phys_bits == 0) {
+ cpu->phys_bits = TCG_PHYS_ADDR_BITS;
+ }
+ } else {
+ /* For 32 bit systems don't use the user set value, but keep
+ * phys_bits consistent with what we tell the guest.
+ */
+ if (cpu->phys_bits != 0) {
+ error_setg(errp, "phys-bits is not user-configurable in 32 bit");
+ return;
+ }
+ if (env->features[FEAT_1_EDX] & CPUID_PSE36) {
+ cpu->phys_bits = 36;
+ } else {
+ cpu->phys_bits = 32;
+ }
+ }
cpu_exec_init(cs, &error_abort);
if (tcg_enabled()) {
@@ -3072,6 +3114,21 @@ out:
}
}
+static void x86_cpu_unrealizefn(DeviceState *dev, Error **errp)
+{
+ X86CPU *cpu = X86_CPU(dev);
+
+#ifndef CONFIG_USER_ONLY
+ cpu_remove_sync(CPU(dev));
+ qemu_unregister_reset(x86_cpu_machine_reset_cb, dev);
+#endif
+
+ if (cpu->apic_state) {
+ object_unparent(OBJECT(cpu->apic_state));
+ cpu->apic_state = NULL;
+ }
+}
+
typedef struct BitProperty {
uint32_t *ptr;
uint32_t mask;
@@ -3207,9 +3264,6 @@ static void x86_cpu_initfn(Object *obj)
object_property_add(obj, "tsc-frequency", "int",
x86_cpuid_get_tsc_freq,
x86_cpuid_set_tsc_freq, NULL, NULL, NULL);
- object_property_add(obj, "apic-id", "int",
- x86_cpuid_get_apic_id,
- x86_cpuid_set_apic_id, NULL, NULL, NULL);
object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo",
x86_cpu_get_feature_words,
NULL, NULL, (void *)env->features, NULL);
@@ -3219,11 +3273,6 @@ static void x86_cpu_initfn(Object *obj)
cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY;
-#ifndef CONFIG_USER_ONLY
- /* Any code creating new X86CPU objects have to set apic-id explicitly */
- cpu->apic_id = -1;
-#endif
-
for (w = 0; w < FEATURE_WORDS; w++) {
int bitnr;
@@ -3280,6 +3329,18 @@ static bool x86_cpu_has_work(CPUState *cs)
}
static Property x86_cpu_properties[] = {
+#ifdef CONFIG_USER_ONLY
+ /* apic_id = 0 by default for *-user, see commit 9886e834 */
+ DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, 0),
+ DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, 0),
+ DEFINE_PROP_INT32("core-id", X86CPU, core_id, 0),
+ DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, 0),
+#else
+ DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, UNASSIGNED_APIC_ID),
+ DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, -1),
+ DEFINE_PROP_INT32("core-id", X86CPU, core_id, -1),
+ DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, -1),
+#endif
DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
{ .name = "hv-spinlocks", .info = &qdev_prop_spinlocks },
DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false),
@@ -3294,6 +3355,9 @@ static Property x86_cpu_properties[] = {
DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true),
DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
+ DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0),
+ DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false),
+ DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true),
DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
@@ -3311,6 +3375,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
xcc->parent_realize = dc->realize;
dc->realize = x86_cpu_realizefn;
+ dc->unrealize = x86_cpu_unrealizefn;
dc->props = x86_cpu_properties;
xcc->parent_reset = cc->reset;
@@ -3347,6 +3412,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
cc->cpu_exec_enter = x86_cpu_exec_enter;
cc->cpu_exec_exit = x86_cpu_exec_exit;
+ dc->cannot_instantiate_with_device_add_yet = false;
/*
* Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the
* object in cpus -> dangling pointer after final object_unref().
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 5b14a72baa..65615c0f3b 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -616,8 +616,10 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
#define CPUID_7_0_EBX_AVX512ER (1U << 27) /* AVX-512 Exponential and Reciprocal */
#define CPUID_7_0_EBX_AVX512CD (1U << 28) /* AVX-512 Conflict Detection */
+#define CPUID_7_0_ECX_UMIP (1U << 2)
#define CPUID_7_0_ECX_PKU (1U << 3)
#define CPUID_7_0_ECX_OSPKE (1U << 4)
+#define CPUID_7_0_ECX_RDPID (1U << 22)
#define CPUID_XSAVE_XSAVEOPT (1U << 0)
#define CPUID_XSAVE_XSAVEC (1U << 1)
@@ -845,6 +847,11 @@ typedef struct {
#define NB_OPMASK_REGS 8
+/* CPU can't have 0xFFFFFFFF APIC ID, use that value to distinguish
+ * that APIC ID hasn't been set yet
+ */
+#define UNASSIGNED_APIC_ID 0xFFFFFFFF
+
typedef union X86LegacyXSaveArea {
struct {
uint16_t fcw;
@@ -1174,7 +1181,7 @@ struct X86CPU {
bool expose_kvm;
bool migratable;
bool host_features;
- int64_t apic_id;
+ uint32_t apic_id;
/* if true the CPUID code directly forward host cache leaves to the guest */
bool cache_info_passthrough;
@@ -1198,6 +1205,15 @@ struct X86CPU {
/* Compatibility bits for old machine types: */
bool enable_cpuid_0xb;
+ /* if true fill the top bits of the MTRR_PHYSMASKn variable range */
+ bool fill_mtrr_mask;
+
+ /* if true override the phys_bits value with a value read from the host */
+ bool host_phys_bits;
+
+ /* Number of physical address bits supported */
+ uint32_t phys_bits;
+
/* in order to simplify APIC support, we leave this pointer to the
user */
struct DeviceState *apic_state;
@@ -1205,6 +1221,10 @@ struct X86CPU {
Notifier machine_done;
struct kvm_msrs *kvm_msr_buf;
+
+ int32_t socket_id;
+ int32_t core_id;
+ int32_t thread_id;
};
static inline X86CPU *x86_env_get_cpu(CPUX86State *env)
@@ -1419,11 +1439,13 @@ uint64_t cpu_get_tsc(CPUX86State *env);
/* XXX: This value should match the one returned by CPUID
* and in exec.c */
# if defined(TARGET_X86_64)
-# define PHYS_ADDR_MASK 0xffffffffffLL
+# define TCG_PHYS_ADDR_BITS 40
# else
-# define PHYS_ADDR_MASK 0xfffffffffLL
+# define TCG_PHYS_ADDR_BITS 36
# endif
+#define PHYS_ADDR_MASK MAKE_64BIT_MASK(0, TCG_PHYS_ADDR_BITS)
+
#define cpu_init(cpu_model) CPU(cpu_x86_init(cpu_model))
#define cpu_signal_handler cpu_x86_signal_handler
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 0a09be656e..9697e16fdd 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -1719,6 +1719,8 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
}
}
if (has_msr_mtrr) {
+ uint64_t phys_mask = MAKE_64BIT_MASK(0, cpu->phys_bits);
+
kvm_msr_entry_add(cpu, MSR_MTRRdefType, env->mtrr_deftype);
kvm_msr_entry_add(cpu, MSR_MTRRfix64K_00000, env->mtrr_fixed[0]);
kvm_msr_entry_add(cpu, MSR_MTRRfix16K_80000, env->mtrr_fixed[1]);
@@ -1732,10 +1734,15 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
kvm_msr_entry_add(cpu, MSR_MTRRfix4K_F0000, env->mtrr_fixed[9]);
kvm_msr_entry_add(cpu, MSR_MTRRfix4K_F8000, env->mtrr_fixed[10]);
for (i = 0; i < MSR_MTRRcap_VCNT; i++) {
+ /* The CPU GPs if we write to a bit above the physical limit of
+ * the host CPU (and KVM emulates that)
+ */
+ uint64_t mask = env->mtrr_var[i].mask;
+ mask &= phys_mask;
+
kvm_msr_entry_add(cpu, MSR_MTRRphysBase(i),
env->mtrr_var[i].base);
- kvm_msr_entry_add(cpu, MSR_MTRRphysMask(i),
- env->mtrr_var[i].mask);
+ kvm_msr_entry_add(cpu, MSR_MTRRphysMask(i), mask);
}
}
@@ -1973,6 +1980,7 @@ static int kvm_get_msrs(X86CPU *cpu)
CPUX86State *env = &cpu->env;
struct kvm_msr_entry *msrs = cpu->kvm_msr_buf->entries;
int ret, i;
+ uint64_t mtrr_top_bits;
kvm_msr_buf_reset(cpu);
@@ -2125,6 +2133,30 @@ static int kvm_get_msrs(X86CPU *cpu)
}
assert(ret == cpu->kvm_msr_buf->nmsrs);
+ /*
+ * MTRR masks: Each mask consists of 5 parts
+ * a 10..0: must be zero
+ * b 11 : valid bit
+ * c n-1.12: actual mask bits
+ * d 51..n: reserved must be zero
+ * e 63.52: reserved must be zero
+ *
+ * 'n' is the number of physical bits supported by the CPU and is
+ * apparently always <= 52. We know our 'n' but don't know what
+ * the destinations 'n' is; it might be smaller, in which case
+ * it masks (c) on loading. It might be larger, in which case
+ * we fill 'd' so that d..c is consistent irrespetive of the 'n'
+ * we're migrating to.
+ */
+
+ if (cpu->fill_mtrr_mask) {
+ QEMU_BUILD_BUG_ON(TARGET_PHYS_ADDR_SPACE_BITS > 52);
+ assert(cpu->phys_bits <= TARGET_PHYS_ADDR_SPACE_BITS);
+ mtrr_top_bits = MAKE_64BIT_MASK(cpu->phys_bits, 52 - cpu->phys_bits);
+ } else {
+ mtrr_top_bits = 0;
+ }
+
for (i = 0; i < ret; i++) {
uint32_t index = msrs[i].index;
switch (index) {
@@ -2323,7 +2355,8 @@ static int kvm_get_msrs(X86CPU *cpu)
break;
case MSR_MTRRphysBase(0) ... MSR_MTRRphysMask(MSR_MTRRcap_VCNT - 1):
if (index & 1) {
- env->mtrr_var[MSR_MTRRphysIndex(index)].mask = msrs[i].data;
+ env->mtrr_var[MSR_MTRRphysIndex(index)].mask = msrs[i].data |
+ mtrr_top_bits;
} else {
env->mtrr_var[MSR_MTRRphysIndex(index)].base = msrs[i].data;
}
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 2010b11b6c..e7e50d6bd9 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -284,6 +284,10 @@ qapi-schema += args-alternate.json
qapi-schema += args-any.json
qapi-schema += args-array-empty.json
qapi-schema += args-array-unknown.json
+qapi-schema += args-bad-boxed.json
+qapi-schema += args-boxed-anon.json
+qapi-schema += args-boxed-empty.json
+qapi-schema += args-boxed-string.json
qapi-schema += args-int.json
qapi-schema += args-invalid.json
qapi-schema += args-member-array-bad.json
@@ -317,6 +321,7 @@ qapi-schema += enum-wrong-data.json
qapi-schema += escape-outside-string.json
qapi-schema += escape-too-big.json
qapi-schema += escape-too-short.json
+qapi-schema += event-boxed-empty.json
qapi-schema += event-case.json
qapi-schema += event-nest-struct.json
qapi-schema += flat-union-array-branch.json
@@ -326,6 +331,7 @@ qapi-schema += flat-union-base-any.json
qapi-schema += flat-union-base-union.json
qapi-schema += flat-union-clash-member.json
qapi-schema += flat-union-empty.json
+qapi-schema += flat-union-incomplete-branch.json
qapi-schema += flat-union-inline.json
qapi-schema += flat-union-int-branch.json
qapi-schema += flat-union-invalid-branch-key.json
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include
index f88c0a7309..78af46837b 100644
--- a/tests/docker/Makefile.include
+++ b/tests/docker/Makefile.include
@@ -46,7 +46,8 @@ docker-image: ${DOCKER_TARGETS}
docker-image-%: $(DOCKER_FILES_DIR)/%.docker
$(call quiet-command,\
$(SRC_PATH)/tests/docker/docker.py build qemu:$* $< \
- $(if $V,,--quiet) $(if $(NOCACHE),--no-cache),\
+ $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \
+ $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\
" BUILD $*")
# Expand all the pre-requistes for each docker image and test combination
@@ -95,6 +96,7 @@ docker:
@echo ' DEBUG=1 Stop and drop to shell in the created container'
@echo ' before running the command.'
@echo ' NOCACHE=1 Ignore cache when build images.'
+ @echo ' EXECUTABLE=<path> Include executable in image.'
docker-run-%: CMD = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\1/')
docker-run-%: IMAGE = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\2/')
@@ -105,7 +107,10 @@ docker-run-%: docker-qemu-src
fi
$(if $(filter $(TESTS),$(CMD)),$(if $(filter $(IMAGES),$(IMAGE)), \
$(call quiet-command,\
- $(SRC_PATH)/tests/docker/docker.py run $(if $V,,--rm) \
+ if $(SRC_PATH)/tests/docker/docker.py images \
+ --format={{.Repository}}:{{.Tag}} | \
+ grep -qx qemu:$(IMAGE); then \
+ $(SRC_PATH)/tests/docker/docker.py run $(if $V,,--rm) \
-t \
$(if $(DEBUG),-i,--net=none) \
-e TARGET_LIST=$(TARGET_LIST) \
@@ -114,11 +119,10 @@ docker-run-%: docker-qemu-src
-e CCACHE_DIR=/var/tmp/ccache \
-v $$(realpath $(DOCKER_SRC_COPY)):/var/tmp/qemu:z$(COMMA)ro \
-v $(DOCKER_CCACHE_DIR):/var/tmp/ccache:z \
- -w /var/tmp/qemu \
qemu:$(IMAGE) \
- $(if $V,/bin/bash -x ,) \
- ./run \
+ /var/tmp/qemu/run \
$(CMD); \
+ fi \
, " RUN $(CMD) in $(IMAGE)")))
docker-clean:
diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index 0151362d17..222a1053fe 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -20,7 +20,10 @@ import atexit
import uuid
import argparse
import tempfile
-from shutil import copy
+import re
+from tarfile import TarFile, TarInfo
+from StringIO import StringIO
+from shutil import copy, rmtree
def _text_checksum(text):
"""Calculate a digest string unique to the text content"""
@@ -38,6 +41,54 @@ def _guess_docker_command():
raise Exception("Cannot find working docker command. Tried:\n%s" % \
commands_txt)
+def _copy_with_mkdir(src, root_dir, sub_path):
+ """Copy src into root_dir, creating sub_path as needed."""
+ dest_dir = os.path.normpath("%s/%s" % (root_dir, sub_path))
+ try:
+ os.makedirs(dest_dir)
+ except OSError:
+ # we can safely ignore already created directories
+ pass
+
+ dest_file = "%s/%s" % (dest_dir, os.path.basename(src))
+ copy(src, dest_file)
+
+
+def _get_so_libs(executable):
+ """Return a list of libraries associated with an executable.
+
+ The paths may be symbolic links which would need to be resolved to
+ ensure theright data is copied."""
+
+ libs = []
+ ldd_re = re.compile(r"(/.*/)(\S*)")
+ try:
+ ldd_output = subprocess.check_output(["ldd", executable])
+ for line in ldd_output.split("\n"):
+ search = ldd_re.search(line)
+ if search and len(search.groups()) == 2:
+ so_path = search.groups()[0]
+ so_lib = search.groups()[1]
+ libs.append("%s/%s" % (so_path, so_lib))
+ except subprocess.CalledProcessError:
+ print "%s had no associated libraries (static build?)" % (executable)
+
+ return libs
+
+def _copy_binary_with_libs(src, dest_dir):
+ """Copy a binary executable and all its dependant libraries.
+
+ This does rely on the host file-system being fairly multi-arch
+ aware so the file don't clash with the guests layout."""
+
+ _copy_with_mkdir(src, dest_dir, "/usr/bin")
+
+ libs = _get_so_libs(src)
+ if libs:
+ for l in libs:
+ so_path = os.path.dirname(l)
+ _copy_with_mkdir(l , dest_dir, so_path)
+
class Docker(object):
""" Running Docker commands """
def __init__(self):
@@ -45,9 +96,11 @@ class Docker(object):
self._instances = []
atexit.register(self._kill_instances)
- def _do(self, cmd, quiet=True, **kwargs):
+ def _do(self, cmd, quiet=True, infile=None, **kwargs):
if quiet:
kwargs["stdout"] = subprocess.PIPE
+ if infile:
+ kwargs["stdin"] = infile
return subprocess.call(self._command + cmd, **kwargs)
def _do_kill_instances(self, only_known, only_active=True):
@@ -87,22 +140,27 @@ class Docker(object):
labels = json.loads(resp)[0]["Config"].get("Labels", {})
return labels.get("com.qemu.dockerfile-checksum", "")
- def build_image(self, tag, dockerfile, df_path, quiet=True, argv=None):
+ def build_image(self, tag, docker_dir, dockerfile, quiet=True, argv=None):
if argv == None:
argv = []
- tmp_dir = tempfile.mkdtemp(prefix="docker_build")
- tmp_df = tempfile.NamedTemporaryFile(dir=tmp_dir, suffix=".docker")
+ tmp_df = tempfile.NamedTemporaryFile(dir=docker_dir, suffix=".docker")
tmp_df.write(dockerfile)
tmp_df.write("\n")
tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" %
_text_checksum(dockerfile))
tmp_df.flush()
+
self._do(["build", "-t", tag, "-f", tmp_df.name] + argv + \
- [tmp_dir],
+ [docker_dir],
quiet=quiet)
+ def update_image(self, tag, tarball, quiet=True):
+ "Update a tagged image using "
+
+ self._do(["build", "-t", tag, "-"], quiet=quiet, infile=tarball)
+
def image_matches_dockerfile(self, tag, dockerfile):
try:
checksum = self.get_image_dockerfile_checksum(tag)
@@ -121,6 +179,9 @@ class Docker(object):
self._instances.remove(label)
return ret
+ def command(self, cmd, argv, quiet):
+ return self._do([cmd] + argv, quiet=quiet)
+
class SubCommand(object):
"""A SubCommand template base class"""
name = None # Subcommand name
@@ -151,6 +212,10 @@ class BuildCommand(SubCommand):
""" Build docker image out of a dockerfile. Arguments: <tag> <dockerfile>"""
name = "build"
def args(self, parser):
+ parser.add_argument("--include-executable", "-e",
+ help="""Specify a binary that will be copied to the
+ container together with all its dependent
+ libraries""")
parser.add_argument("tag",
help="Image Tag")
parser.add_argument("dockerfile",
@@ -164,10 +229,80 @@ class BuildCommand(SubCommand):
if dkr.image_matches_dockerfile(tag, dockerfile):
if not args.quiet:
print "Image is up to date."
- return 0
+ else:
+ # Create a docker context directory for the build
+ docker_dir = tempfile.mkdtemp(prefix="docker_build")
+
+ # Is there a .pre file to run in the build context?
+ docker_pre = os.path.splitext(args.dockerfile)[0]+".pre"
+ if os.path.exists(docker_pre):
+ rc = subprocess.call(os.path.realpath(docker_pre),
+ cwd=docker_dir)
+ if rc == 3:
+ print "Skip"
+ return 0
+ elif rc != 0:
+ print "%s exited with code %d" % (docker_pre, rc)
+ return 1
+
+ # Do we include a extra binary?
+ if args.include_executable:
+ _copy_binary_with_libs(args.include_executable,
+ docker_dir)
+
+ dkr.build_image(tag, docker_dir, dockerfile,
+ quiet=args.quiet, argv=argv)
+
+ rmtree(docker_dir)
+
+ return 0
+
+class UpdateCommand(SubCommand):
+ """ Update a docker image with new executables. Arguments: <tag> <executable>"""
+ name = "update"
+ def args(self, parser):
+ parser.add_argument("tag",
+ help="Image Tag")
+ parser.add_argument("executable",
+ help="Executable to copy")
+
+ def run(self, args, argv):
+ # Create a temporary tarball with our whole build context and
+ # dockerfile for the update
+ tmp = tempfile.NamedTemporaryFile(suffix="dckr.tar.gz")
+ tmp_tar = TarFile(fileobj=tmp, mode='w')
+
+ # Add the executable to the tarball
+ bn = os.path.basename(args.executable)
+ ff = "/usr/bin/%s" % bn
+ tmp_tar.add(args.executable, arcname=ff)
+
+ # Add any associated libraries
+ libs = _get_so_libs(args.executable)
+ if libs:
+ for l in libs:
+ tmp_tar.add(os.path.realpath(l), arcname=l)
+
+ # Create a Docker buildfile
+ df = StringIO()
+ df.write("FROM %s\n" % args.tag)
+ df.write("ADD . /\n")
+ df.seek(0)
+
+ df_tar = TarInfo(name="Dockerfile")
+ df_tar.size = len(df.buf)
+ tmp_tar.addfile(df_tar, fileobj=df)
+
+ tmp_tar.close()
+
+ # reset the file pointers
+ tmp.flush()
+ tmp.seek(0)
+
+ # Run the build with our tarball context
+ dkr = Docker()
+ dkr.update_image(args.tag, tmp, quiet=args.quiet)
- dkr.build_image(tag, dockerfile, args.dockerfile,
- quiet=args.quiet, argv=argv)
return 0
class CleanCommand(SubCommand):
@@ -177,6 +312,12 @@ class CleanCommand(SubCommand):
Docker().clean()
return 0
+class ImagesCommand(SubCommand):
+ """Run "docker images" command"""
+ name = "images"
+ def run(self, args, argv):
+ return Docker().command("images", argv, args.quiet)
+
def main():
parser = argparse.ArgumentParser(description="A Docker helper",
usage="%s <subcommand> ..." % os.path.basename(sys.argv[0]))
diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker
new file mode 100644
index 0000000000..3a9125e497
--- /dev/null
+++ b/tests/docker/dockerfiles/debian-bootstrap.docker
@@ -0,0 +1,21 @@
+# Create Debian Bootstrap Image
+#
+# This is intended to be pre-poluated by:
+# - a first stage debootstrap (see debian-bootstrap.pre)
+# - a native qemu-$arch that binfmt_misc will run
+FROM scratch
+
+# Add everything from the context into the container
+ADD . /
+
+# Patch all mounts as docker already has stuff set up
+RUN sed -i 's/in_target mount/echo not for docker in_target mount/g' /debootstrap/functions
+
+# Run stage 2
+RUN /debootstrap/debootstrap --second-stage
+
+# At this point we can install additional packages if we want
+# Duplicate deb line as deb-src
+RUN cat /etc/apt/sources.list | sed "s/deb/deb-src/" >> /etc/apt/sources.list
+RUN apt-get update
+RUN apt-get -y build-dep qemu
diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre
new file mode 100755
index 0000000000..5d9c8d5ebc
--- /dev/null
+++ b/tests/docker/dockerfiles/debian-bootstrap.pre
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# Simple wrapper for debootstrap, run in the docker build context
+#
+FAKEROOT=`which fakeroot 2> /dev/null`
+
+exit_and_skip()
+{
+ exit 3
+}
+
+#
+# fakeroot is needed to run the bootstrap stage
+#
+if [ -z $FAKEROOT ]; then
+ echo "Please install fakeroot to enable bootstraping"
+ exit_and_skip
+fi
+
+# We check in order for
+#
+# - DEBOOTSTRAP_DIR pointing at a development checkout
+# - PATH for the debootstrap script (installed)
+#
+# If neither option works then we checkout debootstrap from its
+# upstream SCM and run it from there.
+#
+
+if [ -z $DEBOOTSTRAP_DIR ]; then
+ DEBOOTSTRAP=`which debootstrap 2> /dev/null`
+ if [ -z $DEBOOTSTRAP ]; then
+ echo "No debootstrap installed, attempting to install from SCM"
+ DEBOOTSTRAP_SOURCE=https://anonscm.debian.org/git/d-i/debootstrap.git
+ git clone ${DEBOOTSTRAP_SOURCE} ./debootstrap.git
+ export DEBOOTSTRAP_DIR=./debootstrap.git
+ DEBOOTSTRAP=./debootstrap.git/debootstrap
+ fi
+else
+ DEBOOTSTRAP=${DEBOOTSTRAP_DIR}/debootstrap
+ if [ ! -f $DEBOOTSTRAP ]; then
+ echo "Couldn't find script at ${DEBOOTSTRAP}"
+ exit_and_skip
+ fi
+fi
+
+#
+# Finally check to see if any qemu's are installed
+#
+BINFMT_DIR=/proc/sys/fs/binfmt_misc
+if [ ! -e $BINFMT_DIR ]; then
+ echo "binfmt_misc needs enabling for a QEMU bootstrap to work"
+ exit_and_skip
+else
+ # DEB_ARCH and QEMU arch names are not totally aligned
+ case "${DEB_ARCH}" in
+ amd64)
+ QEMU=qemu-i386
+ ;;
+ armel|armhf)
+ QEMU=qemu-arm
+ ;;
+ arm64)
+ QEMU=qemu-aarch64
+ ;;
+ powerpc)
+ QEMU=qemu-ppc
+ ;;
+ ppc64el)
+ QEMU=qemu-ppc64le
+ ;;
+ s390)
+ QEMU=qemu-s390x
+ ;;
+ *)
+ QEMU=qemu-${DEB_ARCH}
+ ;;
+ esac
+ if [ ! -e "${BINFMT_DIR}/$QEMU" ]; then
+ echo "No binfmt_misc rule to run $QEMU, can't bootstrap"
+ exit_and_skip
+ fi
+fi
+
+echo "Building a rootfs using ${FAKEROOT} and ${DEBOOTSTRAP} ${DEB_ARCH}/${DEB_TYPE}"
+
+${FAKEROOT} ${DEBOOTSTRAP} --variant=buildd --foreign --arch=$DEB_ARCH $DEB_TYPE . http://httpredir.debian.org/debian || exit 1
+exit 0
diff --git a/tests/docker/run b/tests/docker/run
index ec3d11934b..d85d49afc1 100755
--- a/tests/docker/run
+++ b/tests/docker/run
@@ -11,6 +11,14 @@
# or (at your option) any later version. See the COPYING file in
# the top-level directory.
+set -e
+
+if test -n "$V"; then
+ set -x
+fi
+
+BASE="$(dirname $(readlink -e $0))"
+
# Prepare the environment
. /etc/profile || true
export PATH=/usr/lib/ccache:$PATH
@@ -24,10 +32,10 @@ export TEST_DIR=/tmp/qemu-test
mkdir -p $TEST_DIR/{src,build,install}
# Extract the source tarballs
-tar -C $TEST_DIR/src -xzf qemu.tgz
+tar -C $TEST_DIR/src -xzf $BASE/qemu.tgz
for p in dtc pixman; do
- if test -f $p.tgz; then
- tar -C $TEST_DIR/src/$p -xzf $p.tgz
+ if test -f $BASE/$p.tgz; then
+ tar -C $TEST_DIR/src/$p -xzf $BASE/$p.tgz
export FEATURES="$FEATURES $p"
fi
done
@@ -55,4 +63,6 @@ elif test -n "$DEBUG"; then
echo
# Force error after shell exits
$SHELL && exit 1
+else
+ exit 1
fi
diff --git a/tests/qapi-schema/args-bad-boxed.err b/tests/qapi-schema/args-bad-boxed.err
new file mode 100644
index 0000000000..ad0d417321
--- /dev/null
+++ b/tests/qapi-schema/args-bad-boxed.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-bad-boxed.json:2: 'boxed' of command 'foo' should only use true value
diff --git a/tests/qapi-schema/args-bad-boxed.exit b/tests/qapi-schema/args-bad-boxed.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/args-bad-boxed.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-bad-boxed.json b/tests/qapi-schema/args-bad-boxed.json
new file mode 100644
index 0000000000..dea0cd0aa5
--- /dev/null
+++ b/tests/qapi-schema/args-bad-boxed.json
@@ -0,0 +1,2 @@
+# 'boxed' should only appear with value true
+{ 'command': 'foo', 'boxed': false }
diff --git a/tests/qapi-schema/args-bad-boxed.out b/tests/qapi-schema/args-bad-boxed.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/args-bad-boxed.out
diff --git a/tests/qapi-schema/args-boxed-anon.err b/tests/qapi-schema/args-boxed-anon.err
new file mode 100644
index 0000000000..f24f345218
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-anon.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-boxed-anon.json:2: 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/args-boxed-anon.exit b/tests/qapi-schema/args-boxed-anon.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-anon.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-boxed-anon.json b/tests/qapi-schema/args-boxed-anon.json
new file mode 100644
index 0000000000..95f60da2ed
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-anon.json
@@ -0,0 +1,2 @@
+# 'boxed' can only be used with named types
+{ 'command': 'foo', 'boxed': true, 'data': { 'string': 'str' } }
diff --git a/tests/qapi-schema/args-boxed-anon.out b/tests/qapi-schema/args-boxed-anon.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-anon.out
diff --git a/tests/qapi-schema/args-boxed-empty.err b/tests/qapi-schema/args-boxed-empty.err
new file mode 100644
index 0000000000..039603e85c
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-boxed-empty.json:3: Cannot use 'boxed' with empty type
diff --git a/tests/qapi-schema/args-boxed-empty.exit b/tests/qapi-schema/args-boxed-empty.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-boxed-empty.json b/tests/qapi-schema/args-boxed-empty.json
new file mode 100644
index 0000000000..52717e065f
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-empty.json
@@ -0,0 +1,3 @@
+# 'boxed' requires a non-empty type
+{ 'struct': 'Empty', 'data': {} }
+{ 'command': 'foo', 'boxed': true, 'data': 'Empty' }
diff --git a/tests/qapi-schema/args-boxed-empty.out b/tests/qapi-schema/args-boxed-empty.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-empty.out
diff --git a/tests/qapi-schema/args-boxed-string.err b/tests/qapi-schema/args-boxed-string.err
new file mode 100644
index 0000000000..d326b48aef
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-string.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-boxed-string.json:2: 'data' for command 'foo' cannot use built-in type 'str'
diff --git a/tests/qapi-schema/args-boxed-string.exit b/tests/qapi-schema/args-boxed-string.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-string.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/args-boxed-string.json b/tests/qapi-schema/args-boxed-string.json
new file mode 100644
index 0000000000..f91a1502e7
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-string.json
@@ -0,0 +1,2 @@
+# 'boxed' requires a complex (not built-in) type
+{ 'command': 'foo', 'boxed': true, 'data': 'str' }
diff --git a/tests/qapi-schema/args-boxed-string.out b/tests/qapi-schema/args-boxed-string.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/args-boxed-string.out
diff --git a/tests/qapi-schema/args-union.err b/tests/qapi-schema/args-union.err
index 1d693d74da..f8ad223dde 100644
--- a/tests/qapi-schema/args-union.err
+++ b/tests/qapi-schema/args-union.err
@@ -1 +1 @@
-tests/qapi-schema/args-union.json:4: 'data' for command 'oops' cannot use union type 'Uni'
+tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
diff --git a/tests/qapi-schema/args-union.json b/tests/qapi-schema/args-union.json
index 7bdcbb7f08..2fcaeaae16 100644
--- a/tests/qapi-schema/args-union.json
+++ b/tests/qapi-schema/args-union.json
@@ -1,4 +1,3 @@
-# we do not allow union arguments
-# TODO should we support this?
+# use of union arguments requires 'boxed':true
{ 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
{ 'command': 'oops', 'data': 'Uni' }
diff --git a/tests/qapi-schema/event-boxed-empty.err b/tests/qapi-schema/event-boxed-empty.err
new file mode 100644
index 0000000000..68ec6f2d2b
--- /dev/null
+++ b/tests/qapi-schema/event-boxed-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/event-boxed-empty.json:2: Use of 'boxed' requires 'data'
diff --git a/tests/qapi-schema/event-boxed-empty.exit b/tests/qapi-schema/event-boxed-empty.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/event-boxed-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/event-boxed-empty.json b/tests/qapi-schema/event-boxed-empty.json
new file mode 100644
index 0000000000..cb145f1433
--- /dev/null
+++ b/tests/qapi-schema/event-boxed-empty.json
@@ -0,0 +1,2 @@
+# 'boxed' requires a non-empty type
+{ 'event': 'FOO', 'boxed': true }
diff --git a/tests/qapi-schema/event-boxed-empty.out b/tests/qapi-schema/event-boxed-empty.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/event-boxed-empty.out
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index b6b4134a80..5a0f2bf805 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,4 +1,5 @@
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
event oops None
+ boxed=False
object q_empty
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.err b/tests/qapi-schema/flat-union-incomplete-branch.err
new file mode 100644
index 0000000000..e826bf0789
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-incomplete-branch.json:6: Union 'TestUnion' data missing 'value2' branch
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.exit b/tests/qapi-schema/flat-union-incomplete-branch.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.json b/tests/qapi-schema/flat-union-incomplete-branch.json
new file mode 100644
index 0000000000..25a411bc83
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.json
@@ -0,0 +1,9 @@
+# we require all branches of the union to be covered
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'union': 'TestUnion',
+ 'base': { 'type': 'TestEnum' },
+ 'discriminator': 'type',
+ 'data': { 'value1': 'TestTypeA' } }
diff --git a/tests/qapi-schema/flat-union-incomplete-branch.out b/tests/qapi-schema/flat-union-incomplete-branch.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-incomplete-branch.out
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 382ce2fa27..1d2722c02e 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,7 +1,7 @@
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
command fooA q_obj_fooA-arg -> None
- gen=True success_response=True
+ gen=True success_response=True boxed=False
object q_empty
object q_obj_fooA-arg
member bar1: str optional=False
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index ae3293a3ae..e8171c935f 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,7 +1,7 @@
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
command eins None -> None
- gen=True success_response=True
+ gen=True success_response=True boxed=False
object q_empty
command zwei None -> None
- gen=True success_response=True
+ gen=True success_response=True boxed=False
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index f571e1bb34..17194637ba 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -127,6 +127,8 @@
{ 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
'returns': 'int' }
{ 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
+{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
+{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
# For testing integer range flattening in opts-visitor. The following schema
# corresponds to the option format:
@@ -154,6 +156,8 @@
'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } }
{ 'event': 'EVENT_D',
'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } }
+{ 'event': 'EVENT_E', 'boxed': true, 'data': 'UserDefZero' }
+{ 'event': 'EVENT_F', 'boxed': true, 'data': 'UserDefAlternate' }
# test that we correctly compile downstream extensions, as well as munge
# ticklish names
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 19cd214f6b..9d99c4eebb 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,25 +1,39 @@
alternate AltIntNum
+ tag type
case i: int
case n: number
alternate AltNumInt
+ tag type
case n: number
case i: int
alternate AltNumStr
+ tag type
case n: number
case s: str
alternate AltStrBool
+ tag type
case s: str
case b: bool
alternate AltStrInt
+ tag type
case s: str
case i: int
alternate AltStrNum
+ tag type
case s: str
case n: number
event EVENT_A None
+ boxed=False
event EVENT_B None
+ boxed=False
event EVENT_C q_obj_EVENT_C-arg
+ boxed=False
event EVENT_D q_obj_EVENT_D-arg
+ boxed=False
+event EVENT_E UserDefZero
+ boxed=True
+event EVENT_F UserDefAlternate
+ boxed=True
object Empty1
object Empty2
base Empty1
@@ -50,6 +64,7 @@ object UserDefA
member boolean: bool optional=False
member a_b: int optional=True
alternate UserDefAlternate
+ tag type
case udfu: UserDefFlatUnion
case s: str
case i: int
@@ -72,6 +87,7 @@ object UserDefFlatUnion2
case value2: UserDefB
object UserDefNativeListUnion
member type: UserDefNativeListUnionKind optional=False
+ tag type
case integer: q_obj_intList-wrapper
case s8: q_obj_int8List-wrapper
case s16: q_obj_int16List-wrapper
@@ -116,7 +132,9 @@ object UserDefZero
object WrapAlternate
member alt: UserDefAlternate optional=False
event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
+ boxed=False
alternate __org.qemu_x-Alt
+ tag type
case __org.qemu_x-branch: str
case b: __org.qemu_x-Base
object __org.qemu_x-Base
@@ -130,6 +148,7 @@ object __org.qemu_x-Struct2
member array: __org.qemu_x-Union1List optional=False
object __org.qemu_x-Union1
member type: __org.qemu_x-Union1Kind optional=False
+ tag type
case __org.qemu_x-branch: q_obj_str-wrapper
enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
object __org.qemu_x-Union2
@@ -137,11 +156,15 @@ object __org.qemu_x-Union2
tag __org.qemu_x-member1
case __org.qemu_x-value: __org.qemu_x-Struct2
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
- gen=True success_response=True
+ gen=True success_response=True boxed=False
+command boxed-struct UserDefZero -> None
+ gen=True success_response=True boxed=True
+command boxed-union UserDefNativeListUnion -> None
+ gen=True success_response=True boxed=True
command guest-get-time q_obj_guest-get-time-arg -> int
- gen=True success_response=True
+ gen=True success_response=True boxed=False
command guest-sync q_obj_guest-sync-arg -> any
- gen=True success_response=True
+ gen=True success_response=True boxed=False
object q_empty
object q_obj_EVENT_C-arg
member a: int optional=True
@@ -202,10 +225,10 @@ object q_obj_user_def_cmd2-arg
member ud1a: UserDefOne optional=False
member ud1b: UserDefOne optional=True
command user_def_cmd None -> None
- gen=True success_response=True
+ gen=True success_response=True boxed=False
command user_def_cmd0 Empty2 -> Empty2
- gen=True success_response=True
+ gen=True success_response=True boxed=False
command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
- gen=True success_response=True
+ gen=True success_response=True boxed=False
command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
- gen=True success_response=True
+ gen=True success_response=True boxed=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 649677e017..ef74e2c4c8 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -36,19 +36,20 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_variants(variants)
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response):
+ gen, success_response, boxed):
print 'command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name)
- print ' gen=%s success_response=%s' % (gen, success_response)
+ print ' gen=%s success_response=%s boxed=%s' % \
+ (gen, success_response, boxed)
- def visit_event(self, name, info, arg_type):
+ def visit_event(self, name, info, arg_type, boxed):
print 'event %s %s' % (name, arg_type and arg_type.name)
+ print ' boxed=%s' % boxed
@staticmethod
def _print_variants(variants):
if variants:
- if variants.tag_name:
- print ' tag %s' % variants.tag_name
+ print ' tag %s' % variants.tag_member.name
for v in variants.variants:
print ' case %s: %s' % (v.name, v.type.name)
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 8ffeb045cd..5af1a468b8 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -59,6 +59,14 @@ QObject *qmp_guest_sync(QObject *arg, Error **errp)
return arg;
}
+void qmp_boxed_struct(UserDefZero *arg, Error **errp)
+{
+}
+
+void qmp_boxed_union(UserDefNativeListUnion *arg, Error **errp)
+{
+}
+
__org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
__org_qemu_x_StructList *b,
__org_qemu_x_Union2 *c,