From b98fcfd8840f290c406c32301340e96f00238a93 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 11 Jul 2017 10:08:55 +0200 Subject: [PATCH 01/55] build: add -Wexpansion-to-defined This warning is included in -Wall by clang, but not by GCC (which only enables it for -Wextra). Include it in the list of warnings we enable to minimize the differences between the compilers: Signed-off-by: Paolo Bonzini --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 902653ae03..dceeb72e5e 100755 --- a/configure +++ b/configure @@ -1583,7 +1583,7 @@ gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags" -gcc_flags="-Wno-initializer-overrides $gcc_flags" +gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags" gcc_flags="-Wno-string-plus-int $gcc_flags" # Note that we do not add -Werror to gcc_flags here, because that would # enable it for all configure tests. If a configure test failed due From c6a88ddaac4aa89765313952d5702181423df1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jun 2017 12:03:04 -0300 Subject: [PATCH 02/55] MAINTAINERS: update TCG entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit moved in a9ded601..244f1441 to accel/ Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20170629150308.22766-2-f4bug@amsat.org> Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- MAINTAINERS | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9529c9484c..74b39561d0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -84,14 +84,10 @@ M: Paolo Bonzini M: Peter Crosthwaite M: Richard Henderson S: Maintained -F: cpu-exec.c -F: cpu-exec-common.c F: cpus.c -F: cputlb.c F: exec.c F: softmmu_template.h -F: translate-all.* -F: translate-common.c +F: accel/tcg/ F: include/exec/cpu*.h F: include/exec/exec-all.h F: include/exec/helper*.h From 1962cb2001996528e4deda8476551eb838ea0758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jun 2017 12:03:05 -0300 Subject: [PATCH 03/55] MAINTAINERS: update KVM entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit moved in 92229a57 to accel/ Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20170629150308.22766-3-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 74b39561d0..55061e9bb3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -273,8 +273,8 @@ Overall M: Paolo Bonzini L: kvm@vger.kernel.org S: Supported -F: kvm-* F: */kvm.* +F: accel/kvm/ F: include/sysemu/kvm*.h ARM From 2c7ccb4e27bd980948720211641ea0812a70a328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jun 2017 12:03:06 -0300 Subject: [PATCH 04/55] MAINTAINERS: update Xen entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit moved in 56e2cd24..28b99f47 to hw/xen/ and hw/i386/xen/ Signed-off-by: Philippe Mathieu-Daudé Acked-by: Anthony PERARD Message-Id: <20170629150308.22766-4-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 55061e9bb3..5540460e63 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -323,7 +323,6 @@ M: Stefano Stabellini M: Anthony Perard L: xen-devel@lists.xenproject.org S: Supported -F: xen-* F: */xen* F: hw/9pfs/xen-9p-backend.c F: hw/char/xen_console.c From 752a2ae2734feb3daa7e91f323aca6734437c64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jun 2017 12:03:07 -0300 Subject: [PATCH 05/55] MAINTAINERS: update TCI entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit moved in 244f1441 to tcg/ Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20170629150308.22766-5-f4bug@amsat.org> Reviewed-by: Stefan Weil Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5540460e63..dd7e1e56af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1645,7 +1645,7 @@ TCI target M: Stefan Weil S: Maintained F: tcg/tci/ -F: tci.c +F: tcg/tci.c F: disas/tci.c Block drivers From 747969db2a6cd6e93561f7edc50d824a7d67346b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 Jun 2017 12:03:08 -0300 Subject: [PATCH 06/55] MAINTAINERS: add entry for "Unimplemented" device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also voluntary myself as reviewer Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20170629150308.22766-6-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index dd7e1e56af..b9aa878b84 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1155,6 +1155,13 @@ F: docs/specs/vmgenid.txt F: tests/vmgenid-test.c F: stubs/vmgenid.c +Unimplemented device +M: Peter Maydell +R: Philippe Mathieu-Daudé +S: Maintained +F: include/hw/misc/unimp.h +F: hw/misc/unimp.c + Subsystems ---------- Audio From bcdeb9be566ded2eb35233aaccf38742a21e5daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 6 Jul 2017 19:03:53 +0200 Subject: [PATCH 07/55] chardev: block during sync read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A sync read should block until all requested data is available (instead of retrying in qemu_chr_fe_read_all). Change the channel to blocking during sync_read. Signed-off-by: Marc-André Lureau Message-Id: <20170706170353.32601-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Marc-André Lureau --- chardev/char-socket.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index ccc499cfa1..7e6648b03a 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -454,7 +454,9 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) return 0; } + qio_channel_set_blocking(s->ioc, true, NULL); size = tcp_chr_recv(chr, (void *) buf, len); + qio_channel_set_blocking(s->ioc, false, NULL); if (size == 0) { /* connection closed */ tcp_chr_disconnect(chr); From 313e45b5fe45542602bfa801db7a13d485c29b04 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:48 +0300 Subject: [PATCH 08/55] char: move QemuOpts->ChardevBackend translation to a separate func MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse function will be used by the following patch Signed-off-by: Anton Nefedov Message-Id: <1499342940-56739-2-git-send-email-anton.nefedov@virtuozzo.com> Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- chardev/char.c | 81 +++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/chardev/char.c b/chardev/char.c index 2b679a2295..839eff6afd 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -556,17 +556,23 @@ help_string_append(const char *name, void *opaque) g_string_append_printf(str, "\n%s", name); } -Chardev *qemu_chr_new_from_opts(QemuOpts *opts, - Error **errp) +static const char *chardev_alias_translate(const char *name) +{ + int i; + for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { + if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { + return chardev_alias_table[i].typename; + } + } + return name; +} + +static ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) { Error *local_err = NULL; const ChardevClass *cc; - Chardev *chr; - int i; ChardevBackend *backend = NULL; - const char *name = qemu_opt_get(opts, "backend"); - const char *id = qemu_opts_id(opts); - char *bid = NULL; + const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend")); if (name == NULL) { error_setg(errp, "chardev: \"%s\" missing backend", @@ -574,7 +580,40 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } - if (is_help_option(name)) { + cc = char_get_class(name, errp); + if (cc == NULL) { + return NULL; + } + + backend = g_new0(ChardevBackend, 1); + backend->type = CHARDEV_BACKEND_KIND_NULL; + + if (cc->parse) { + cc->parse(opts, backend, &local_err); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_ChardevBackend(backend); + return NULL; + } + } else { + ChardevCommon *ccom = g_new0(ChardevCommon, 1); + qemu_chr_parse_common(opts, ccom); + backend->u.null.data = ccom; /* Any ChardevCommon member would work */ + } + + return backend; +} + +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) +{ + const ChardevClass *cc; + Chardev *chr = NULL; + ChardevBackend *backend = NULL; + const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend")); + const char *id = qemu_opts_id(opts); + char *bid = NULL; + + if (name && is_help_option(name)) { GString *str = g_string_new(""); chardev_name_foreach(help_string_append, str); @@ -589,38 +628,20 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } - for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { - if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { - name = chardev_alias_table[i].typename; - break; - } + backend = qemu_chr_parse_opts(opts, errp); + if (backend == NULL) { + return NULL; } cc = char_get_class(name, errp); if (cc == NULL) { - return NULL; + goto out; } - backend = g_new0(ChardevBackend, 1); - backend->type = CHARDEV_BACKEND_KIND_NULL; - if (qemu_opt_get_bool(opts, "mux", 0)) { bid = g_strdup_printf("%s-base", id); } - chr = NULL; - if (cc->parse) { - cc->parse(opts, backend, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - } else { - ChardevCommon *ccom = g_new0(ChardevCommon, 1); - qemu_chr_parse_common(opts, ccom); - backend->u.null.data = ccom; /* Any ChardevCommon member would work */ - } - chr = qemu_chardev_new(bid ? bid : id, object_class_get_name(OBJECT_CLASS(cc)), backend, errp); From 81517ba37a6cec59f92396b4722861868eb0a500 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:49 +0300 Subject: [PATCH 09/55] char: add backend hotswap handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontends should have an interface to setup the handler of a backend change. The interface will be used in the next commits Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-3-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- backends/rng-egd.c | 2 +- chardev/char-fe.c | 4 +++- chardev/char-mux.c | 1 + gdbstub.c | 2 +- hw/arm/pxa2xx.c | 3 ++- hw/arm/strongarm.c | 2 +- hw/char/bcm2835_aux.c | 2 +- hw/char/cadence_uart.c | 2 +- hw/char/debugcon.c | 2 +- hw/char/digic-uart.c | 2 +- hw/char/escc.c | 2 +- hw/char/etraxfs_ser.c | 2 +- hw/char/exynos4210_uart.c | 2 +- hw/char/grlib_apbuart.c | 2 +- hw/char/imx_serial.c | 2 +- hw/char/ipoctal232.c | 2 +- hw/char/lm32_juart.c | 2 +- hw/char/lm32_uart.c | 2 +- hw/char/mcf_uart.c | 2 +- hw/char/milkymist-uart.c | 2 +- hw/char/pl011.c | 2 +- hw/char/sclpconsole-lm.c | 2 +- hw/char/sclpconsole.c | 2 +- hw/char/serial.c | 2 +- hw/char/sh_serial.c | 2 +- hw/char/spapr_vty.c | 2 +- hw/char/stm32f2xx_usart.c | 3 ++- hw/char/terminal3270.c | 2 +- hw/char/virtio-console.c | 4 ++-- hw/char/xen_console.c | 2 +- hw/char/xilinx_uartlite.c | 2 +- hw/ipmi/ipmi_bmc_extern.c | 2 +- hw/mips/boston.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/misc/ivshmem.c | 2 +- hw/usb/ccid-card-passthru.c | 2 +- hw/usb/dev-serial.c | 2 +- hw/usb/redirect.c | 2 +- include/chardev/char-fe.h | 5 +++++ monitor.c | 4 ++-- net/colo-compare.c | 10 ++++++---- net/filter-mirror.c | 6 +++--- net/slirp.c | 2 +- net/vhost-user.c | 7 ++++--- qtest.c | 2 +- target/xtensa/xtensa-semi.c | 2 +- tests/test-char.c | 14 ++++++++++---- tests/vhost-user-test.c | 2 +- 48 files changed, 77 insertions(+), 58 deletions(-) diff --git a/backends/rng-egd.c b/backends/rng-egd.c index e7ce2cac80..d2b9ce6cbf 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -106,7 +106,7 @@ static void rng_egd_opened(RngBackend *b, Error **errp) /* FIXME we should resubmit pending requests when the CDS reconnects. */ qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read, - rng_egd_chr_read, NULL, s, NULL, true); + rng_egd_chr_read, NULL, NULL, s, NULL, true); } static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 3f90f0567c..7054863208 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -216,7 +216,7 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del) assert(b); if (b->chr) { - qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, NULL, true); if (b->chr->be == b) { b->chr->be = NULL; } @@ -235,6 +235,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, IOEventHandler *fd_event, + BackendChangeHandler *be_change, void *opaque, GMainContext *context, bool set_open) @@ -258,6 +259,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, b->chr_can_read = fd_can_read; b->chr_read = fd_read; b->chr_event = fd_event; + b->chr_be_change = be_change; b->opaque = opaque; if (cc->chr_update_read_handler) { cc->chr_update_read_handler(s, context); diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 08570b915e..4cda5e7458 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -278,6 +278,7 @@ void mux_chr_set_handlers(Chardev *chr, GMainContext *context) mux_chr_can_read, mux_chr_read, mux_chr_event, + NULL, chr, context, true); } diff --git a/gdbstub.c b/gdbstub.c index ec4e4b25be..e561244292 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2003,7 +2003,7 @@ int gdbserver_start(const char *device) if (chr) { qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, NULL, true); + gdb_chr_event, NULL, NULL, NULL, true); } s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 629e6c64e6..731ed08de7 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1970,7 +1970,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) PXA2xxFIrState *s = PXA2XX_FIR(dev); qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL, true); + pxa2xx_fir_rx, pxa2xx_fir_event, NULL, s, NULL, + true); } static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 7683edc9e5..1fa082c9c5 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1247,7 +1247,7 @@ static void strongarm_uart_realize(DeviceState *dev, Error **errp) strongarm_uart_can_receive, strongarm_uart_receive, strongarm_uart_event, - s, NULL, true); + NULL, s, NULL, true); } static void strongarm_uart_reset(DeviceState *dev) diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 4d46ad60ae..370dc7e296 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -279,7 +279,7 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) BCM2835AuxState *s = BCM2835_AUX(dev); qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive, - bcm2835_aux_receive, NULL, s, NULL, true); + bcm2835_aux_receive, NULL, NULL, s, NULL, true); } static Property bcm2835_aux_props[] = { diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 4a2c124104..3a9335ce1a 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -485,7 +485,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp) fifo_trigger_update, s); qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void cadence_uart_init(Object *obj) diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 762e3d8ada..9b56fb8abe 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -92,7 +92,7 @@ static void debugcon_realize_core(DebugconState *s, Error **errp) return; } - qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL, true); + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, s, NULL, true); } static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 34306e11ff..6ebcb87a40 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -146,7 +146,7 @@ static void digic_uart_realize(DeviceState *dev, Error **errp) DigicUartState *s = DIGIC_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void digic_uart_init(Object *obj) diff --git a/hw/char/escc.c b/hw/char/escc.c index 3f787632c7..3546df3f12 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -1016,7 +1016,7 @@ static void escc_realize(DeviceState *dev, Error **errp) if (qemu_chr_fe_get_driver(&s->chn[i].chr)) { s->chn[i].clock = s->frequency / 2; qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive, - serial_receive1, serial_event, + serial_receive1, serial_event, NULL, &s->chn[i], NULL, true); } } diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index c1fba9f50f..a184026410 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -233,7 +233,7 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&s->chr, serial_can_receive, serial_receive, - serial_event, s, NULL, true); + serial_event, NULL, s, NULL, true); } static void etraxfs_ser_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index b51d44a321..a84d2c3d0e 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -645,7 +645,7 @@ static void exynos4210_uart_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive, exynos4210_uart_receive, exynos4210_uart_event, - s, NULL, true); + NULL, s, NULL, true); } static Property exynos4210_uart_properties[] = { diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index 32d98edf49..12d0c99b31 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -247,7 +247,7 @@ static int grlib_apbuart_init(SysBusDevice *dev) grlib_apbuart_can_receive, grlib_apbuart_receive, grlib_apbuart_event, - uart, NULL, true); + NULL, uart, NULL, true); sysbus_init_irq(dev, &uart->irq); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index af250305be..70405ccf8b 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -315,7 +315,7 @@ static void imx_serial_realize(DeviceState *dev, Error **errp) DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr)); qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive, - imx_event, s, NULL, true); + imx_event, NULL, s, NULL, true); } static void imx_serial_init(Object *obj) diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 337a3e566a..cc8c8dcc78 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -545,7 +545,7 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) if (qemu_chr_fe_get_driver(&ch->dev)) { qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive, hostdev_receive, hostdev_event, - ch, NULL, true); + NULL, ch, NULL, true); DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); } else { DPRINTF("Could not redirect channel %u, no chardev set\n", i); diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index 3948dcd332..d75c835ad2 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -119,7 +119,7 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp) LM32JuartState *s = LM32_JUART(dev); qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx, - juart_event, s, NULL, true); + juart_event, NULL, s, NULL, true); } static const VMStateDescription vmstate_lm32_juart = { diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index cff8c38f90..c4a3b9b275 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -266,7 +266,7 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp) LM32UartState *s = LM32_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static const VMStateDescription vmstate_lm32_uart = { diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index fe12ad5ccb..56fa402b58 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -305,7 +305,7 @@ static void mcf_uart_realize(DeviceState *dev, Error **errp) mcf_uart_state *s = MCF_UART(dev); qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, mcf_uart_receive, - mcf_uart_event, s, NULL, true); + mcf_uart_event, NULL, s, NULL, true); } static Property mcf_uart_properties[] = { diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index e19d0f6520..548ee27bca 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -199,7 +199,7 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp) MilkymistUartState *s = MILKYMIST_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void milkymist_uart_init(Object *obj) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 33802f00c8..2aa277fc4f 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -329,7 +329,7 @@ static void pl011_realize(DeviceState *dev, Error **errp) PL011State *s = PL011(dev); qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive, - pl011_event, s, NULL, true); + pl011_event, NULL, s, NULL, true); } static void pl011_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 1b15046690..741c641fc0 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -313,7 +313,7 @@ static int console_init(SCLPEvent *event) console_available = true; qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, - chr_read, NULL, scon, NULL, true); + chr_read, NULL, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 4a107a268d..a92baa8554 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -228,7 +228,7 @@ static int console_init(SCLPEvent *event) } console_available = true; qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, - chr_read, NULL, scon, NULL, true); + chr_read, NULL, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/serial.c b/hw/char/serial.c index e1f12507bf..ab14ca447d 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -897,7 +897,7 @@ void serial_realize_core(SerialState *s, Error **errp) qemu_register_reset(serial_reset, s); qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, - serial_event, s, NULL, true); + serial_event, NULL, s, NULL, true); fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH); fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH); serial_reset(s); diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index ca9816d045..ad043bdcfc 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -400,7 +400,7 @@ void sh_serial_init(MemoryRegion *sysmem, qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, sh_serial_receive1, - sh_serial_event, s, NULL, true); + sh_serial_event, NULL, s, NULL, true); } s->eri = eri_source; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 8f02f3a612..ddd1b00694 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -84,7 +84,7 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) } qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive, - vty_receive, NULL, dev, NULL, true); + vty_receive, NULL, NULL, dev, NULL, true); } /* Forward declaration */ diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 59872e6d3b..268e435338 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -207,7 +207,8 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) STM32F2XXUsartState *s = STM32F2XX_USART(dev); qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive, - stm32f2xx_usart_receive, NULL, s, NULL, true); + stm32f2xx_usart_receive, NULL, NULL, + s, NULL, true); } static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index 7b10a04f18..5adf95b6e5 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -179,7 +179,7 @@ static void terminal_init(EmulatedCcw3270Device *dev, Error **errp) } terminal_available = true; qemu_chr_fe_set_handlers(&t->chr, terminal_can_read, - terminal_read, chr_event, t, NULL, true); + terminal_read, chr_event, NULL, t, NULL, true); } static int read_payload_3270(EmulatedCcw3270Device *dev, uint32_t cda, diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 0cb1668c8a..afb494925a 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -188,11 +188,11 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) */ if (k->is_console) { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - NULL, vcon, NULL, true); + NULL, NULL, vcon, NULL, true); virtio_serial_open(port); } else { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - chr_event, vcon, NULL, false); + chr_event, NULL, vcon, NULL, false); } } } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index f9af8cadf4..c4e8c72b99 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -246,7 +246,7 @@ static int con_initialise(struct XenDevice *xendev) xen_be_bind_evtchn(&con->xendev); qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, - xencons_receive, NULL, con, NULL, true); + xencons_receive, NULL, NULL, con, NULL, true); xen_pv_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 71ed2fc1be..2a8bc1e497 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -212,7 +212,7 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) XilinxUARTLite *s = XILINX_UARTLITE(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void xilinx_uartlite_init(Object *obj) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 329b03e17f..182de5853c 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -453,7 +453,7 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) } qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, ibe, NULL, true); + chr_event, NULL, ibe, NULL, true); } static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index a4677f7da4..146be2ae74 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -533,7 +533,7 @@ static void boston_mach_init(MachineState *machine) chr = qemu_chr_new("lcd", "vc:320x240"); qemu_chr_fe_init(&s->lcd_display, chr, NULL); qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, - boston_lcd_event, s, NULL, true); + boston_lcd_event, NULL, s, NULL, true); ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, PCI_DEVFN(0, 0), diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index dad2f37fb1..8cb9d3c3ce 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -570,7 +570,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, chr = qemu_chr_new("fpga", "vc:320x200"); qemu_chr_fe_init(&s->display, chr, NULL); qemu_chr_fe_set_handlers(&s->display, NULL, NULL, - malta_fgpa_display_event, s, NULL, true); + malta_fgpa_display_event, NULL, s, NULL, true); s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq, 230400, uart_chr, DEVICE_NATIVE_ENDIAN); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 2f0819d977..a857e50bd9 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -894,7 +894,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) } qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, - ivshmem_read, NULL, s, NULL, true); + ivshmem_read, NULL, NULL, s, NULL, true); if (ivshmem_setup_interrupts(s, errp) < 0) { error_prepend(errp, "Failed to initialize interrupts: "); diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index ac1725eeae..e9f58c9f67 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -348,7 +348,7 @@ static int passthru_initfn(CCIDCardState *base) qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, ccid_card_vscard_read, - ccid_card_vscard_event, card, NULL, true); + ccid_card_vscard_event, NULL, card, NULL, true); ccid_card_vscard_send_init(card); } else { error_report("missing chardev"); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index bfbf7cdce7..dc145a6529 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -502,7 +502,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) } qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read, - usb_serial_event, s, NULL, true); + usb_serial_event, NULL, s, NULL, true); usb_serial_handle_reset(dev); if (chr->be_open && !dev->attached) { diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index aa22d69216..115efb966b 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1399,7 +1399,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) /* Let the backend know we are ready */ qemu_chr_fe_set_handlers(&dev->cs, usbredir_chardev_can_read, usbredir_chardev_read, usbredir_chardev_event, - dev, NULL, true); + NULL, dev, NULL, true); dev->vmstate = qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 2cbb262f66..9f38060c7e 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -4,6 +4,7 @@ #include "chardev/char.h" typedef void IOEventHandler(void *opaque, int event); +typedef int BackendChangeHandler(void *opaque); /* This is the backend as seen by frontend, the actual backend is * Chardev */ @@ -12,6 +13,7 @@ struct CharBackend { IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; + BackendChangeHandler *chr_be_change; void *opaque; int tag; int fe_open; @@ -54,6 +56,8 @@ Chardev *qemu_chr_fe_get_driver(CharBackend *be); * receive * @fd_read: callback to receive data from char * @fd_event: event callback + * @be_change: backend change callback; passing NULL means hot backend change + * is not supported and will not be attempted * @opaque: an opaque pointer for the callbacks * @context: a main loop context or NULL for the default * @set_open: whether to call qemu_chr_fe_set_open() implicitely when @@ -68,6 +72,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, IOEventHandler *fd_event, + BackendChangeHandler *be_change, void *opaque, GMainContext *context, bool set_open); diff --git a/monitor.c b/monitor.c index 12935a7d71..534f4e3079 100644 --- a/monitor.c +++ b/monitor.c @@ -4110,12 +4110,12 @@ void monitor_init(Chardev *chr, int flags) if (monitor_is_qmp(mon)) { qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read, - monitor_qmp_event, mon, NULL, true); + monitor_qmp_event, NULL, mon, NULL, true); qemu_chr_fe_set_echo(&mon->chr, true); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); } else { qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read, - monitor_event, mon, NULL, true); + monitor_event, NULL, mon, NULL, true); } qemu_mutex_lock(&monitor_lock); diff --git a/net/colo-compare.c b/net/colo-compare.c index 6d500e1dc4..abfc23ce80 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -543,7 +543,7 @@ static void compare_pri_chr_in(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->pri_rs, buf, size); if (ret == -1) { - qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, + qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, NULL, NULL, NULL, true); error_report("colo-compare primary_in error"); } @@ -560,7 +560,7 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->sec_rs, buf, size); if (ret == -1) { - qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, + qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, NULL, NULL, NULL, true); error_report("colo-compare secondary_in error"); } @@ -588,9 +588,11 @@ static void *colo_compare_thread(void *opaque) s->worker_context = g_main_context_new(); qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read, - compare_pri_chr_in, NULL, s, s->worker_context, true); + compare_pri_chr_in, NULL, NULL, + s, s->worker_context, true); qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read, - compare_sec_chr_in, NULL, s, s->worker_context, true); + compare_sec_chr_in, NULL, NULL, + s, s->worker_context, true); s->compare_loop = g_main_loop_new(s->worker_context, FALSE); diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 52d978fce2..62a8265068 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -112,7 +112,7 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size) if (ret == -1) { qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, - NULL, NULL, true); + NULL, NULL, NULL, true); } } @@ -124,7 +124,7 @@ static void redirector_chr_event(void *opaque, int event) switch (event) { case CHR_EVENT_CLOSED: qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, - NULL, NULL, true); + NULL, NULL, NULL, true); break; default: break; @@ -245,7 +245,7 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp) qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read, redirector_chr_read, redirector_chr_event, - nf, NULL, true); + NULL, nf, NULL, true); } if (s->outdev) { diff --git a/net/slirp.c b/net/slirp.c index 6a6d727999..9fbc949e81 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -778,7 +778,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, fwd->slirp = s->slirp; qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read, - NULL, fwd, NULL, true); + NULL, NULL, fwd, NULL, true); } return 0; diff --git a/net/vhost-user.c b/net/vhost-user.c index a042ec6a34..36f32a2d84 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -211,7 +211,7 @@ static void chr_closed_bh(void *opaque) vhost_user_stop(queues, ncs); qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, - opaque, NULL, true); + NULL, opaque, NULL, true); if (err) { error_report_err(err); @@ -257,7 +257,7 @@ static void net_vhost_user_event(void *opaque, int event) g_source_remove(s->watch); s->watch = 0; - qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL, NULL, false); aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque); @@ -305,7 +305,8 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, return -1; } qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, - net_vhost_user_event, nc0->name, NULL, true); + net_vhost_user_event, NULL, nc0->name, NULL, + true); } while (!s->started); assert(s->vhost_net); diff --git a/qtest.c b/qtest.c index 9a5d1dc50d..88a09e9afc 100644 --- a/qtest.c +++ b/qtest.c @@ -691,7 +691,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) qemu_chr_fe_init(&qtest_chr, chr, errp); qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read, - qtest_event, &qtest_chr, NULL, true); + qtest_event, NULL, &qtest_chr, NULL, true); qemu_chr_fe_set_echo(&qtest_chr, true); inbuf = g_string_new(""); diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 32e2bd7f1d..7aa1d1357b 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -158,7 +158,7 @@ void xtensa_sim_open_console(Chardev *chr) static CharBackend console; qemu_chr_fe_init(&console, chr, &error_abort); - qemu_chr_fe_set_handlers(&console, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_fe_set_handlers(&console, NULL, NULL, NULL, NULL, NULL, NULL, true); xtensa_sim_console = &console; } diff --git a/tests/test-char.c b/tests/test-char.c index b962063e56..38aaa270ab 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -178,6 +178,7 @@ static void char_mux_test(void) fe_can_read, fe_read, fe_event, + NULL, &h1, NULL, true); @@ -186,6 +187,7 @@ static void char_mux_test(void) fe_can_read, fe_read, fe_event, + NULL, &h2, NULL, true); qemu_chr_fe_take_focus(&chr_be2); @@ -209,7 +211,8 @@ static void char_mux_test(void) h1.read_count = 0; /* remove first handler */ - qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, + NULL, NULL, true); qemu_chr_be_write(base, (void *)"hello", 6); g_assert_cmpint(h1.read_count, ==, 0); g_assert_cmpint(h2.read_count, ==, 0); @@ -307,13 +310,13 @@ static void char_socket_test(void) qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, - NULL, &d, NULL, true); + NULL, NULL, &d, NULL, true); chr_client = qemu_chr_new("client", tmp); qemu_chr_fe_init(&client_be, chr_client, &error_abort); qemu_chr_fe_set_handlers(&client_be, socket_can_read_hello, socket_read_hello, - NULL, &d, NULL, true); + NULL, NULL, &d, NULL, true); g_free(tmp); d.conn_expected = true; @@ -383,6 +386,7 @@ static void char_pipe_test(void) fe_can_read, fe_read, fe_event, + NULL, &fe, NULL, true); @@ -431,7 +435,7 @@ static void char_udp_test(void) d.chr = chr; qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, socket_can_read_hello, socket_read_hello, - NULL, &d, NULL, true); + NULL, NULL, &d, NULL, true); ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5); g_assert_cmpint(ret, ==, 5); @@ -523,6 +527,7 @@ static void char_file_test(void) fe_can_read, fe_read, fe_event, + NULL, &fe, NULL, true); g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); @@ -583,6 +588,7 @@ static void char_null_test(void) fe_can_read, fe_read, fe_event, + NULL, NULL, NULL, true); ret = qemu_chr_fe_write(&be, (void *)"buf", 4); diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index b3cc045765..d4da09f147 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -464,7 +464,7 @@ static void test_server_create_chr(TestServer *server, const gchar *opt) qemu_chr_fe_init(&server->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, - chr_event, server, NULL, true); + chr_event, NULL, server, NULL, true); } static void test_server_listen(TestServer *server) From 7bb86085e61befc95551055e26645cb23c3f0b86 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:50 +0300 Subject: [PATCH 10/55] char: chardevice hotswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a possibility to change a char device without a frontend removal. Ideally, it would have to happen transparently to a frontend, i.e. frontend would continue its regular operation. However, backends are not stateless and are set up by the frontends via qemu_chr_fe_<> functions, and it's not (generally) possible to replay that setup entirely in a backend code, as different chardevs respond to the setup calls differently, so do frontends work differently basing on those setup responses. Moreover, some frontend can generally get and save the backend pointer (qemu_chr_fe_get_driver()), and it will become invalid after backend change. So, a frontend which would like to support chardev hotswap has to register a "backend change" handler, and redo its backend setup there. Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <1499342940-56739-4-git-send-email-anton.nefedov@virtuozzo.com> Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- chardev/char.c | 83 ++++++++++++++++++++++++++++++++++++++++++ include/chardev/char.h | 9 +++++ qapi-schema.json | 40 ++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/chardev/char.c b/chardev/char.c index 839eff6afd..d6b9d89e93 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -951,6 +951,89 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, return ret; } +ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, + Error **errp) +{ + CharBackend *be; + const ChardevClass *cc; + Chardev *chr, *chr_new; + bool closed_sent = false; + ChardevReturn *ret; + + chr = qemu_chr_find(id); + if (!chr) { + error_setg(errp, "Chardev '%s' does not exist", id); + return NULL; + } + + if (CHARDEV_IS_MUX(chr)) { + error_setg(errp, "Mux device hotswap not supported yet"); + return NULL; + } + + if (qemu_chr_replay(chr)) { + error_setg(errp, + "Chardev '%s' cannot be changed in record/replay mode", id); + return NULL; + } + + be = chr->be; + if (!be) { + /* easy case */ + object_unparent(OBJECT(chr)); + return qmp_chardev_add(id, backend, errp); + } + + if (!be->chr_be_change) { + error_setg(errp, "Chardev user does not support chardev hotswap"); + return NULL; + } + + cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp); + if (!cc) { + return NULL; + } + + chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), + backend, errp); + if (!chr_new) { + return NULL; + } + chr_new->label = g_strdup(id); + + if (chr->be_open && !chr_new->be_open) { + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + closed_sent = true; + } + + chr->be = NULL; + qemu_chr_fe_init(be, chr_new, &error_abort); + + if (be->chr_be_change(be->opaque) < 0) { + error_setg(errp, "Chardev '%s' change failed", chr_new->label); + chr_new->be = NULL; + qemu_chr_fe_init(be, chr, &error_abort); + if (closed_sent) { + qemu_chr_be_event(chr, CHR_EVENT_OPENED); + } + object_unref(OBJECT(chr_new)); + return NULL; + } + + object_unparent(OBJECT(chr)); + object_property_add_child(get_chardevs_root(), chr_new->label, + OBJECT(chr_new), &error_abort); + object_unref(OBJECT(chr_new)); + + ret = g_new0(ChardevReturn, 1); + if (CHARDEV_IS_PTY(chr_new)) { + ret->pty = g_strdup(chr_new->filename + 4); + ret->has_pty = true; + } + + return ret; +} + void qmp_chardev_remove(const char *id, Error **errp) { Chardev *chr; diff --git a/include/chardev/char.h b/include/chardev/char.h index 8a9ade4931..22fd734999 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -92,6 +92,15 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); */ Chardev *qemu_chr_new(const char *label, const char *filename); +/** + * @qemu_chr_change: + * + * Change an existing character backend + * + * @opts the new backend options + */ +void qemu_chr_change(QemuOpts *opts, Error **errp); + /** * @qemu_chr_cleanup: * diff --git a/qapi-schema.json b/qapi-schema.json index 485767f1ab..ab438ead70 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5089,6 +5089,46 @@ 'backend' : 'ChardevBackend' }, 'returns': 'ChardevReturn' } +## +# @chardev-change: +# +# Change a character device backend +# +# @id: the chardev's ID, must exist +# @backend: new backend type and parameters +# +# Returns: ChardevReturn. +# +# Since: 2.10 +# +# Example: +# +# -> { "execute" : "chardev-change", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } +# +# -> {"execute" : "chardev-change", +# "arguments" : { +# "id" : "charchannel2", +# "backend" : { +# "type" : "socket", +# "data" : { +# "addr" : { +# "type" : "unix" , +# "data" : { +# "path" : "/tmp/charchannel2.socket" +# } +# }, +# "server" : true, +# "wait" : false }}}} +# <- {"return": {}} +# +## +{ 'command': 'chardev-change', 'data': {'id' : 'str', + 'backend' : 'ChardevBackend' }, + 'returns': 'ChardevReturn' } + ## # @chardev-remove: # From 7c44a2a9d178118193febd71cfc6943dd21bdde9 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:51 +0300 Subject: [PATCH 11/55] char: forbid direct chardevice access for hotswap devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_chr_fe_get_driver() is unsafe, frontends with hotswap support should not access CharDriver ptr directly as CharDriver might change. Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-5-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- chardev/char-fe.c | 7 +++++++ include/chardev/char-fe.h | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 7054863208..de5ba3c119 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -179,9 +179,16 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) Chardev *qemu_chr_fe_get_driver(CharBackend *be) { + /* this is unsafe for the users that support chardev hotswap */ + assert(be->chr_be_change == NULL); return be->chr; } +bool qemu_chr_fe_backend_connected(CharBackend *be) +{ + return !!be->chr; +} + bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp) { int tag = 0; diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 9f38060c7e..588abed606 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -46,9 +46,19 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del); * * Returns the driver associated with a CharBackend or NULL if no * associated Chardev. + * Note: avoid this function as the driver should never be accessed directly, + * especially by the frontends that support chardevice hotswap. + * Consider qemu_chr_fe_backend_connected() to check for driver existence */ Chardev *qemu_chr_fe_get_driver(CharBackend *be); +/** + * @qemu_chr_fe_backend_connected: + * + * Returns true if there is a chardevice associated with @be. + */ +bool qemu_chr_fe_backend_connected(CharBackend *be); + /** * @qemu_chr_fe_set_handlers: * @b: a CharBackend From 3065070153ef4235659d821c0d6fa28fd0630c57 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:52 +0300 Subject: [PATCH 12/55] char: avoid chardevice direct access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit frontends should avoid accessing CharDriver struct where possible Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-6-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- chardev/char-fe.c | 5 +++++ hw/arm/strongarm.c | 2 +- hw/char/cadence_uart.c | 2 +- hw/char/debugcon.c | 2 +- hw/char/escc.c | 6 +++--- hw/char/exynos4210_uart.c | 2 +- hw/char/grlib_apbuart.c | 2 +- hw/char/ipoctal232.c | 2 +- hw/char/parallel.c | 2 +- hw/char/sclpconsole-lm.c | 2 +- hw/char/sclpconsole.c | 2 +- hw/char/serial.c | 2 +- hw/char/sh_serial.c | 2 +- hw/char/spapr_vty.c | 2 +- hw/char/terminal3270.c | 2 +- hw/char/virtio-console.c | 5 ++--- hw/char/xen_console.c | 2 +- hw/ipmi/ipmi_bmc_extern.c | 2 +- hw/misc/ivshmem.c | 4 ++-- hw/usb/ccid-card-passthru.c | 4 ++-- hw/usb/dev-serial.c | 5 ++--- hw/usb/redirect.c | 5 ++--- include/chardev/char-fe.h | 7 +++++++ net/filter-mirror.c | 2 +- 24 files changed, 41 insertions(+), 32 deletions(-) diff --git a/chardev/char-fe.c b/chardev/char-fe.c index de5ba3c119..f3af6ae584 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -189,6 +189,11 @@ bool qemu_chr_fe_backend_connected(CharBackend *be) return !!be->chr; } +bool qemu_chr_fe_backend_open(CharBackend *be) +{ + return be->chr && be->chr->be_open; +} + bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp) { int tag = 0; diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 1fa082c9c5..6a45dcc009 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1106,7 +1106,7 @@ static void strongarm_uart_tx(void *opaque) if (s->utcr3 & UTCR3_LBM) /* loopback */ { strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1); - } else if (qemu_chr_fe_get_driver(&s->chr)) { + } else if (qemu_chr_fe_backend_connected(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx_fifo[s->tx_start], 1); diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 3a9335ce1a..6143494060 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -279,7 +279,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, int ret; /* instant drain the fifo when there's no back-end */ - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { s->tx_count = 0; return FALSE; } diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 9b56fb8abe..95ccec6f8b 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -87,7 +87,7 @@ static const MemoryRegionOps debugcon_ops = { static void debugcon_realize_core(DebugconState *s, Error **errp) { - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create debugcon device, empty char device"); return; } diff --git a/hw/char/escc.c b/hw/char/escc.c index 3546df3f12..89ae9eb997 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -417,7 +417,7 @@ static void escc_update_parameters(ChannelState *s) int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; - if (!qemu_chr_fe_get_driver(&s->chr) || s->type != ser) + if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != ser) return; if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { @@ -557,7 +557,7 @@ static void escc_mem_write(void *opaque, hwaddr addr, trace_escc_mem_writeb_data(CHN_C(s), val); s->tx = val; if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx, 1); @@ -1013,7 +1013,7 @@ static void escc_realize(DeviceState *dev, Error **errp) ESCC_SIZE << s->it_shift); for (i = 0; i < 2; i++) { - if (qemu_chr_fe_get_driver(&s->chn[i].chr)) { + if (qemu_chr_fe_backend_connected(&s->chn[i].chr)) { s->chn[i].clock = s->frequency / 2; qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive, serial_receive1, serial_event, NULL, diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index a84d2c3d0e..3957e78abf 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -380,7 +380,7 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, break; case UTXH: - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | UTRSTAT_Tx_BUFFER_EMPTY); ch = (uint8_t)val; diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index 12d0c99b31..bac11bec58 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -201,7 +201,7 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr, case DATA_OFFSET: case DATA_OFFSET + 3: /* When only one byte write */ /* Transmit when character device available and transmitter enabled */ - if (qemu_chr_fe_get_driver(&uart->chr) && + if (qemu_chr_fe_backend_connected(&uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { c = value & 0xFF; /* XXX this blocks entire thread. Rewrite to use diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index cc8c8dcc78..5e09caf851 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -542,7 +542,7 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) ch->ipoctal = s; /* Redirect IP-Octal channels to host character devices */ - if (qemu_chr_fe_get_driver(&ch->dev)) { + if (qemu_chr_fe_backend_connected(&ch->dev)) { qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive, hostdev_receive, hostdev_event, NULL, ch, NULL, true); diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 75a1a2f55e..055789f791 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -513,7 +513,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) int base; uint8_t dummy; - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create parallel device, empty char device"); return; } diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 741c641fc0..c500bdaf29 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -195,7 +195,7 @@ static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) { SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); - if (!qemu_chr_fe_get_driver(&scon->chr)) { + if (!qemu_chr_fe_backend_connected(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index a92baa8554..d0265dfa7a 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -163,7 +163,7 @@ static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, { SCLPConsole *scon = SCLP_CONSOLE(event); - if (!qemu_chr_fe_get_driver(&scon->chr)) { + if (!qemu_chr_fe_backend_connected(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } diff --git a/hw/char/serial.c b/hw/char/serial.c index ab14ca447d..b4fb8f3856 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -886,7 +886,7 @@ static void serial_reset(void *opaque) void serial_realize_core(SerialState *s, Error **errp) { - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create serial device, empty char device"); return; } diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index ad043bdcfc..835b5378a0 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -110,7 +110,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, } return; case 0x0c: /* FTDR / TDR */ - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { ch = val; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index ddd1b00694..0fa416ca6b 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -78,7 +78,7 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - if (!qemu_chr_fe_get_driver(&dev->chardev)) { + if (!qemu_chr_fe_backend_connected(&dev->chardev)) { error_setg(errp, "chardev property not set"); return; } diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index 5adf95b6e5..28f599111d 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -239,7 +239,7 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd, return 0; } } - if (!qemu_chr_fe_get_driver(&t->chr)) { + if (!qemu_chr_fe_backend_connected(&t->chr)) { /* We just say we consumed all data if there's no backend. */ return count; } diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index afb494925a..4d3967c3bf 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -49,7 +49,7 @@ static ssize_t flush_buf(VirtIOSerialPort *port, VirtConsole *vcon = VIRTIO_CONSOLE(port); ssize_t ret; - if (!qemu_chr_fe_get_driver(&vcon->chr)) { + if (!qemu_chr_fe_backend_connected(&vcon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -168,7 +168,6 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtConsole *vcon = VIRTIO_CONSOLE(dev); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); - Chardev *chr = qemu_chr_fe_get_driver(&vcon->chr); if (port->id == 0 && !k->is_console) { error_setg(errp, "Port number 0 on virtio-serial devices reserved " @@ -176,7 +175,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) return; } - if (chr) { + if (qemu_chr_fe_backend_connected(&vcon->chr)) { /* * For consoles we don't block guest data transfer just * because nothing is connected - we'll just let it go diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index c4e8c72b99..3643dfe067 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -150,7 +150,7 @@ static void xencons_send(struct XenConsole *con) ssize_t len, size; size = con->buffer.size - con->buffer.consumed; - if (qemu_chr_fe_get_driver(&con->chr)) { + if (qemu_chr_fe_backend_connected(&con->chr)) { len = qemu_chr_fe_write(&con->chr, con->buffer.data + con->buffer.consumed, size); diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 182de5853c..abab3bba4f 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -447,7 +447,7 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - if (!qemu_chr_fe_get_driver(&ibe->chr)) { + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { error_setg(errp, "IPMI external bmc requires chardev attribute"); return; } diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index a857e50bd9..7f678d0197 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1128,7 +1128,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); - if (!qemu_chr_fe_get_driver(&s->server_chr)) { + if (!qemu_chr_fe_backend_connected(&s->server_chr)) { error_setg(errp, "You must specify a 'chardev'"); return; } @@ -1257,7 +1257,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) " or ivshmem-doorbell instead"); } - if (!!qemu_chr_fe_get_driver(&s->server_chr) + !!s->shmobj != 1) { + if (qemu_chr_fe_backend_connected(&s->server_chr) + !!s->shmobj != 1) { error_setg(errp, "You must specify either 'shm' or 'chardev'"); return; } diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index e9f58c9f67..45d96b03c6 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -322,7 +322,7 @@ static void passthru_apdu_from_guest( { PassthruState *card = PASSTHRU_CCID_CARD(base); - if (!qemu_chr_fe_get_driver(&card->cs)) { + if (!qemu_chr_fe_backend_connected(&card->cs)) { printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); return; } @@ -343,7 +343,7 @@ static int passthru_initfn(CCIDCardState *base) card->vscard_in_pos = 0; card->vscard_in_hdr = 0; - if (qemu_chr_fe_get_driver(&card->cs)) { + if (qemu_chr_fe_backend_connected(&card->cs)) { DPRINTF(card, D_INFO, "initing chardev\n"); qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index dc145a6529..94b5c34afe 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -484,13 +484,12 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) { USBSerialState *s = USB_SERIAL_DEV(dev); Error *local_err = NULL; - Chardev *chr = qemu_chr_fe_get_driver(&s->cs); usb_desc_create_serial(dev); usb_desc_init(dev); dev->auto_attach = 0; - if (!chr) { + if (!qemu_chr_fe_backend_connected(&s->cs)) { error_setg(errp, "Property chardev is required"); return; } @@ -505,7 +504,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) usb_serial_event, NULL, s, NULL, true); usb_serial_handle_reset(dev); - if (chr->be_open && !dev->attached) { + if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { usb_device_attach(dev, &error_abort); } } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 115efb966b..c597145471 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -273,10 +273,9 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, static int usbredir_write(void *priv, uint8_t *data, int count) { USBRedirDevice *dev = priv; - Chardev *chr = qemu_chr_fe_get_driver(&dev->cs); int r; - if (!chr->be_open) { + if (!qemu_chr_fe_backend_open(&dev->cs)) { return 0; } @@ -1366,7 +1365,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) USBRedirDevice *dev = USB_REDIRECT(udev); int i; - if (!qemu_chr_fe_get_driver(&dev->cs)) { + if (!qemu_chr_fe_backend_connected(&dev->cs)) { error_setg(errp, QERR_MISSING_PARAMETER, "chardev"); return; } diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 588abed606..71cd069478 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -59,6 +59,13 @@ Chardev *qemu_chr_fe_get_driver(CharBackend *be); */ bool qemu_chr_fe_backend_connected(CharBackend *be); +/** + * @qemu_chr_fe_backend_open: + * + * Returns true if chardevice associated with @be is open. + */ +bool qemu_chr_fe_backend_open(CharBackend *be); + /** * @qemu_chr_fe_set_handlers: * @b: a CharBackend diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 62a8265068..6043549e5f 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -163,7 +163,7 @@ static ssize_t filter_redirector_receive_iov(NetFilterState *nf, MirrorState *s = FILTER_REDIRECTOR(nf); int ret; - if (qemu_chr_fe_get_driver(&s->chr_out)) { + if (qemu_chr_fe_backend_connected(&s->chr_out)) { ret = filter_send(&s->chr_out, iov, iovcnt); if (ret) { error_report("filter redirector send failed(%s)", strerror(-ret)); From 12f043c240580b5b4b2d67d8deb29537d679e3a2 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:53 +0300 Subject: [PATCH 13/55] test-char: destroy chardev-udp after test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this is only not a problem if the test is last in a suite, otherwise it makes the following main_loop() calls to fail Signed-off-by: Anton Nefedov Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-7-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- tests/test-char.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test-char.c b/tests/test-char.c index 38aaa270ab..536536e2d8 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -450,6 +450,7 @@ static void char_udp_test(void) close(sock); g_free(tmp); + qemu_chr_fe_deinit(&be, true); } #ifdef HAVE_CHARDEV_SERIAL From 92ddfade9f619977d47399bd360c03626629b1e2 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:54 +0300 Subject: [PATCH 14/55] test-char: split char_udp_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit makes it possible to test the existing chardev-udp Signed-off-by: Anton Nefedov Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-8-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- tests/test-char.c | 56 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/tests/test-char.c b/tests/test-char.c index 536536e2d8..e0bde5e76e 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -407,16 +407,11 @@ static void char_pipe_test(void) } #endif -static void char_udp_test(void) +static int make_udp_socket(int *port) { - struct sockaddr_in addr = { 0, }, other; - SocketIdleData d = { 0, }; - Chardev *chr; - CharBackend be; + struct sockaddr_in addr = { 0, }; socklen_t alen = sizeof(addr); int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0); - char buf[10]; - char *tmp; g_assert_cmpint(sock, >, 0); addr.sin_family = AF_INET ; @@ -427,19 +422,41 @@ static void char_udp_test(void) ret = getsockname(sock, (struct sockaddr *)&addr, &alen); g_assert_cmpint(ret, ==, 0); - tmp = g_strdup_printf("udp:127.0.0.1:%d", - ntohs(addr.sin_port)); - chr = qemu_chr_new("client", tmp); - g_assert_nonnull(chr); + *port = ntohs(addr.sin_port); + return sock; +} + +static void char_udp_test_internal(Chardev *reuse_chr, int sock) +{ + struct sockaddr_in other; + SocketIdleData d = { 0, }; + Chardev *chr; + CharBackend *be; + socklen_t alen = sizeof(other); + int ret; + char buf[10]; + char *tmp = NULL; + + if (reuse_chr) { + chr = reuse_chr; + be = chr->be; + } else { + int port; + sock = make_udp_socket(&port); + tmp = g_strdup_printf("udp:127.0.0.1:%d", port); + chr = qemu_chr_new("client", tmp); + g_assert_nonnull(chr); + + be = g_alloca(sizeof(CharBackend)); + qemu_chr_fe_init(be, chr, &error_abort); + } d.chr = chr; - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, socket_can_read_hello, socket_read_hello, + qemu_chr_fe_set_handlers(be, socket_can_read_hello, socket_read_hello, NULL, NULL, &d, NULL, true); ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5); g_assert_cmpint(ret, ==, 5); - alen = sizeof(addr); ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&other, &alen); g_assert_cmpint(ret, ==, 5); @@ -448,9 +465,16 @@ static void char_udp_test(void) main_loop(); - close(sock); + if (!reuse_chr) { + close(sock); + qemu_chr_fe_deinit(be, true); + } g_free(tmp); - qemu_chr_fe_deinit(&be, true); +} + +static void char_udp_test(void) +{ + char_udp_test_internal(NULL, 0); } #ifdef HAVE_CHARDEV_SERIAL From ae69e4823dedde369027522b646ee62a7ff66530 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:55 +0300 Subject: [PATCH 15/55] test-char: split char_file_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit makes it possible to test the existing chardev-file Signed-off-by: Anton Nefedov Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-9-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- tests/test-char.c | 137 ++++++++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 53 deletions(-) diff --git a/tests/test-char.c b/tests/test-char.c index e0bde5e76e..c654eeabd7 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -503,82 +503,110 @@ static void char_serial_test(void) } #endif -static void char_file_test(void) +#ifndef _WIN32 +static void char_file_fifo_test(void) { + Chardev *chr; + CharBackend be; char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *fifo = g_build_filename(tmp_path, "fifo", NULL); char *out = g_build_filename(tmp_path, "out", NULL); - char *contents = NULL; - ChardevFile file = { .out = out }; + ChardevFile file = { .in = fifo, + .has_in = true, + .out = out }; ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, .u.file.data = &file }; + FeHandler fe = { 0, }; + int fd, ret; + + if (mkfifo(fifo, 0600) < 0) { + abort(); + } + + fd = open(fifo, O_RDWR); + ret = write(fd, "fifo-in", 8); + g_assert_cmpint(ret, ==, 8); + + chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, + &error_abort); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_handlers(&be, + fe_can_read, + fe_read, + fe_event, + NULL, + &fe, NULL, true); + + g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); + qmp_chardev_send_break("label-foo", NULL); + g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); + qmp_chardev_send_break("label-file", NULL); + g_assert_cmpint(fe.last_event, ==, CHR_EVENT_BREAK); + + main_loop(); + + close(fd); + + g_assert_cmpint(fe.read_count, ==, 8); + g_assert_cmpstr(fe.read_buf, ==, "fifo-in"); + + qemu_chr_fe_deinit(&be, true); + + g_unlink(fifo); + g_free(fifo); + g_unlink(out); + g_free(out); + g_rmdir(tmp_path); + g_free(tmp_path); +} +#endif + +static void char_file_test_internal(Chardev *ext_chr, const char *filepath) +{ + char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *out; Chardev *chr; + char *contents = NULL; + ChardevFile file = {}; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; gsize length; int ret; - chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, - &error_abort); + if (ext_chr) { + chr = ext_chr; + out = g_strdup(filepath); + file.out = out; + } else { + out = g_build_filename(tmp_path, "out", NULL); + file.out = out; + chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, + &error_abort); + } ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6); g_assert_cmpint(ret, ==, 6); - object_unref(OBJECT(chr)); ret = g_file_get_contents(out, &contents, &length, NULL); g_assert(ret == TRUE); g_assert_cmpint(length, ==, 6); g_assert(strncmp(contents, "hello!", 6) == 0); - g_free(contents); -#ifndef _WIN32 - { - CharBackend be; - FeHandler fe = { 0, }; - char *fifo = g_build_filename(tmp_path, "fifo", NULL); - int fd; - - if (mkfifo(fifo, 0600) < 0) { - abort(); - } - - fd = open(fifo, O_RDWR); - ret = write(fd, "fifo-in", 8); - g_assert_cmpint(ret, ==, 8); - - file.in = fifo; - file.has_in = true; - chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, - &error_abort); - - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, - fe_can_read, - fe_read, - fe_event, - NULL, - &fe, NULL, true); - - g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); - qmp_chardev_send_break("label-foo", NULL); - g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); - qmp_chardev_send_break("label-file", NULL); - g_assert_cmpint(fe.last_event, ==, CHR_EVENT_BREAK); - - main_loop(); - - close(fd); - - g_assert_cmpint(fe.read_count, ==, 8); - g_assert_cmpstr(fe.read_buf, ==, "fifo-in"); - qemu_chr_fe_deinit(&be, true); - g_unlink(fifo); - g_free(fifo); + if (!ext_chr) { + object_unref(OBJECT(chr)); + g_unlink(out); } -#endif - - g_unlink(out); + g_free(contents); g_rmdir(tmp_path); g_free(tmp_path); g_free(out); } +static void char_file_test(void) +{ + char_file_test_internal(NULL, NULL); +} + static void char_null_test(void) { Error *err = NULL; @@ -656,6 +684,9 @@ int main(int argc, char **argv) g_test_add_func("/char/pipe", char_pipe_test); #endif g_test_add_func("/char/file", char_file_test); +#ifndef _WIN32 + g_test_add_func("/char/file-fifo", char_file_fifo_test); +#endif g_test_add_func("/char/socket", char_socket_test); g_test_add_func("/char/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL From 7b5a9e453e4086c2e6a3b9830ee7e4b3583e2d14 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:56 +0300 Subject: [PATCH 16/55] test-char: add hotswap test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anton Nefedov Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-10-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- tests/test-char.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/test-char.c b/tests/test-char.c index c654eeabd7..7ac25ff73f 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -658,6 +658,76 @@ static void char_invalid_test(void) g_assert_null(chr); } +static int chardev_change(void *opaque) +{ + return 0; +} + +static int chardev_change_denied(void *opaque) +{ + return -1; +} + +static void char_hotswap_test(void) +{ + char *chr_args; + Chardev *chr; + CharBackend be; + + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *filename = g_build_filename(tmp_path, "file", NULL); + ChardevFile file = { .out = filename }; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; + ChardevReturn *ret; + + int port; + int sock = make_udp_socket(&port); + g_assert_cmpint(sock, >, 0); + + chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); + + chr = qemu_chr_new("chardev", chr_args); + qemu_chr_fe_init(&be, chr, &error_abort); + + /* check that chardev operates correctly */ + char_udp_test_internal(chr, sock); + + /* set the handler that denies the hotswap */ + qemu_chr_fe_set_handlers(&be, NULL, NULL, + NULL, chardev_change_denied, NULL, NULL, true); + + /* now, change is denied and has to keep the old backend operating */ + ret = qmp_chardev_change("chardev", &backend, NULL); + g_assert(!ret); + g_assert(be.chr == chr); + + char_udp_test_internal(chr, sock); + + /* now allow the change */ + qemu_chr_fe_set_handlers(&be, NULL, NULL, + NULL, chardev_change, NULL, NULL, true); + + /* has to succeed now */ + ret = qmp_chardev_change("chardev", &backend, &error_abort); + g_assert(be.chr != chr); + + close(sock); + chr = be.chr; + + /* run the file chardev test */ + char_file_test_internal(chr, filename); + + object_unparent(OBJECT(chr)); + + qapi_free_ChardevReturn(ret); + g_unlink(filename); + g_free(filename); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(chr_args); +} + int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -692,6 +762,7 @@ int main(int argc, char **argv) #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); #endif + g_test_add_func("/char/hotswap", char_hotswap_test); return g_test_run(); } From 75b601602be0462acb4e0298e738df86b1de61a1 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:57 +0300 Subject: [PATCH 17/55] hmp: add hmp analogue for qmp-chardev-change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Acked-by: Dr. David Alan Gilbert Message-Id: <1499342940-56739-11-git-send-email-anton.nefedov@virtuozzo.com> Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- chardev/char.c | 2 +- hmp-commands.hx | 18 +++++++++++++++++- hmp.c | 34 ++++++++++++++++++++++++++++++++++ hmp.h | 1 + include/chardev/char.h | 10 ++++++++++ tests/test-hmp.c | 1 + 6 files changed, 64 insertions(+), 2 deletions(-) diff --git a/chardev/char.c b/chardev/char.c index d6b9d89e93..c34b44abc9 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -567,7 +567,7 @@ static const char *chardev_alias_translate(const char *name) return name; } -static ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) +ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) { Error *local_err = NULL; const ChardevClass *cc; diff --git a/hmp-commands.hx b/hmp-commands.hx index 75f8bac01b..b3a8707dad 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1726,7 +1726,23 @@ ETEXI STEXI @item chardev-add args @findex chardev-add -chardev_add accepts the same parameters as the -chardev command line switch. +chardev-add accepts the same parameters as the -chardev command line switch. + +ETEXI + + { + .name = "chardev-change", + .args_type = "id:s,args:s", + .params = "id args", + .help = "change chardev", + .cmd = hmp_chardev_change, + }, + +STEXI +@item chardev-change args +@findex chardev-change +chardev-change accepts existing chardev @var{id} and then the same arguments +as the -chardev command line switch (except for "id"). ETEXI diff --git a/hmp.c b/hmp.c index 6d32c40723..d970ea9855 100644 --- a/hmp.c +++ b/hmp.c @@ -2197,6 +2197,40 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_chardev_change(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + const char *id; + Error *err = NULL; + ChardevBackend *backend = NULL; + ChardevReturn *ret = NULL; + QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, + true); + if (!opts) { + error_setg(&err, "Parsing chardev args failed"); + goto end; + } + + id = qdict_get_str(qdict, "id"); + if (qemu_opts_id(opts)) { + error_setg(&err, "Unexpected 'id' parameter"); + goto end; + } + + backend = qemu_chr_parse_opts(opts, &err); + if (!backend) { + goto end; + } + + ret = qmp_chardev_change(id, backend, &err); + +end: + qapi_free_ChardevReturn(ret); + qapi_free_ChardevBackend(backend); + qemu_opts_del(opts); + hmp_handle_error(mon, &err); +} + void hmp_chardev_remove(Monitor *mon, const QDict *qdict) { Error *local_err = NULL; diff --git a/hmp.h b/hmp.h index 214b2617e7..1ff455295e 100644 --- a/hmp.h +++ b/hmp.h @@ -102,6 +102,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict); +void hmp_chardev_change(Monitor *mon, const QDict *qdict); void hmp_chardev_remove(Monitor *mon, const QDict *qdict); void hmp_chardev_send_break(Monitor *mon, const QDict *qdict); void hmp_qemu_io(Monitor *mon, const QDict *qdict); diff --git a/include/chardev/char.h b/include/chardev/char.h index 22fd734999..1604ea9143 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -80,6 +80,16 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, */ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); +/** + * @qemu_chr_parse_opts: + * + * Parse the options to the ChardevBackend struct. + * + * Returns: a new backend or NULL on error + */ +ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, + Error **errp); + /** * @qemu_chr_new: * diff --git a/tests/test-hmp.c b/tests/test-hmp.c index 6dfa0c36e2..d77b3c8710 100644 --- a/tests/test-hmp.c +++ b/tests/test-hmp.c @@ -23,6 +23,7 @@ static const char *hmp_cmds[] = { "boot_set ndc", "chardev-add null,id=testchardev1", "chardev-send-break testchardev2", + "chardev-change testchardev1 ringbuf", "chardev-remove testchardev1", "commit all", "cpu-add 1", From af50855c251bd08e90fd915a2d58c870df0a78e5 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:58 +0300 Subject: [PATCH 18/55] virtio-console: chardev hotswap support In case of a backend change, the handler functions and the watch have to be reset. Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <1499342940-56739-12-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- hw/char/virtio-console.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 4d3967c3bf..198b2a89c0 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -163,6 +163,30 @@ static void chr_event(void *opaque, int event) } } +static int chr_be_change(void *opaque) +{ + VirtConsole *vcon = opaque; + VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); + VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); + + if (k->is_console) { + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + NULL, chr_be_change, vcon, NULL, true); + } else { + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + chr_event, chr_be_change, vcon, NULL, false); + } + + if (vcon->watch) { + g_source_remove(vcon->watch); + vcon->watch = qemu_chr_fe_add_watch(&vcon->chr, + G_IO_OUT | G_IO_HUP, + chr_write_unblocked, vcon); + } + + return 0; +} + static void virtconsole_realize(DeviceState *dev, Error **errp) { VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); @@ -187,11 +211,13 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) */ if (k->is_console) { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - NULL, NULL, vcon, NULL, true); + NULL, chr_be_change, + vcon, NULL, true); virtio_serial_open(port); } else { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - chr_event, NULL, vcon, NULL, false); + chr_event, chr_be_change, + vcon, NULL, false); } } } From 757358425e3d6b22ac312f8f69dcaf44ed523ad0 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:08:59 +0300 Subject: [PATCH 19/55] serial: move TIOCM update to a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit will be used by the following patch Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Marc-André Lureau Message-Id: <1499342940-56739-13-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index b4fb8f3856..09157f981f 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -312,6 +312,24 @@ static void serial_write_fcr(SerialState *s, uint8_t val) } } +static void serial_update_tiocm(SerialState *s) +{ + int flags; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); + + flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); + + if (s->mcr & UART_MCR_RTS) { + flags |= CHR_TIOCM_RTS; + } + if (s->mcr & UART_MCR_DTR) { + flags |= CHR_TIOCM_DTR; + } + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); +} + static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -426,24 +444,13 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, break; case 4: { - int flags; int old_mcr = s->mcr; s->mcr = val & 0x1f; if (val & UART_MCR_LOOP) break; if (s->poll_msl >= 0 && old_mcr != s->mcr) { - - qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); - - flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); - - if (val & UART_MCR_RTS) - flags |= CHR_TIOCM_RTS; - if (val & UART_MCR_DTR) - flags |= CHR_TIOCM_DTR; - - qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); + serial_update_tiocm(s); /* Update the modem status after a one-character-send wait-time, since there may be a response from the device/computer at the other end of the serial line */ timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time); From 1a29cc8f5ebd657e159dbe4be340102595846d42 Mon Sep 17 00:00:00 2001 From: Anton Nefedov Date: Thu, 6 Jul 2017 15:09:00 +0300 Subject: [PATCH 20/55] serial: chardev hotswap support This allows to change the port's backend runtime, e.g. change it from file to a socket making it possible to establish a debug session with WinDbg > qemu-system [..] -chardev file,id=charchannel2,path=/tmp/charchannel2 \ -device isa-serial,chardev=charchannel2,id=channel2 QEMU 2.9.50 monitor - type 'help' for more information (qemu) chardev-change charchannel2 \ socket,host=127.0.0.1,port=4242,server,nowait For a backend change, a number of ioctls has to be replayed to sync the current setup of a frontend to a backend tty. This is hopefully enough so we don't have to track, store and replay the whole original control byte sequence. Signed-off-by: Anton Nefedov Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <1499342940-56739-14-git-send-email-anton.nefedov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/hw/char/serial.c b/hw/char/serial.c index 09157f981f..9aec6c60d8 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -891,6 +891,34 @@ static void serial_reset(void *opaque) s->msr &= ~UART_MSR_ANY_DELTA; } +static int serial_be_change(void *opaque) +{ + SerialState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, + serial_event, serial_be_change, s, NULL, true); + + serial_update_parameters(s); + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &s->last_break_enable); + + s->poll_msl = (s->ier & UART_IER_MSI) ? 1 : 0; + serial_update_msl(s); + + if (s->poll_msl >= 0 && !(s->mcr & UART_MCR_LOOP)) { + serial_update_tiocm(s); + } + + if (s->watch_tag > 0) { + g_source_remove(s->watch_tag); + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); + } + + return 0; +} + void serial_realize_core(SerialState *s, Error **errp) { if (!qemu_chr_fe_backend_connected(&s->chr)) { @@ -904,7 +932,7 @@ void serial_realize_core(SerialState *s, Error **errp) qemu_register_reset(serial_reset, s); qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, - serial_event, NULL, s, NULL, true); + serial_event, serial_be_change, s, NULL, true); fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH); fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH); serial_reset(s); From 04bf2526ce87f21b32c9acba1c5518708c243ad0 Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Wed, 12 Jul 2017 18:08:40 +0530 Subject: [PATCH 21/55] exec: use qemu_ram_ptr_length to access guest ram When accessing guest's ram block during DMA operation, use 'qemu_ram_ptr_length' to get ram block pointer. It ensures that DMA operation of given length is possible; And avoids any OOB memory access situations. Reported-by: Alex Signed-off-by: Prasad J Pandit Message-Id: <20170712123840.29328-1-ppandit@redhat.com> Signed-off-by: Paolo Bonzini --- exec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exec.c b/exec.c index a083ff89ad..ad103ce483 100644 --- a/exec.c +++ b/exec.c @@ -2929,7 +2929,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr, } } else { /* RAM case */ - ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l); memcpy(ptr, buf, l); invalidate_and_set_dirty(mr, addr1, l); } @@ -3020,7 +3020,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr, } } else { /* RAM case */ - ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l); memcpy(buf, ptr, l); } From 406bc339b0505fcfc2ffcbca1f05a3756e338a65 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Wed, 12 Jul 2017 17:51:42 -0400 Subject: [PATCH 22/55] Revert "exec.c: Fix breakpoint invalidation race" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have proper locking after MTTCG patches have landed, we can revert the commit. This reverts commit a9353fe897ca2687e5b3385ed39e3db3927a90e0. CC: Peter Maydell CC: Alex Bennée Signed-off-by: Pranith Kumar Message-Id: <20170712215143.19594-1-bobby.prani@gmail.com> Signed-off-by: Paolo Bonzini --- exec.c | 25 +++++++++++++++++++------ include/exec/exec-all.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/exec.c b/exec.c index ad103ce483..2e8bc43421 100644 --- a/exec.c +++ b/exec.c @@ -775,15 +775,28 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) #endif } +#if defined(CONFIG_USER_ONLY) static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { - /* Flush the whole TB as this will not have race conditions - * even if we don't have proper locking yet. - * Ideally we would just invalidate the TBs for the - * specified PC. - */ - tb_flush(cpu); + mmap_lock(); + tb_lock(); + tb_invalidate_phys_page_range(pc, pc + 1, 0); + tb_unlock(); + mmap_unlock(); } +#else +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +{ + MemTxAttrs attrs; + hwaddr phys = cpu_get_phys_page_attrs_debug(cpu, pc, &attrs); + int asidx = cpu_asidx_from_attrs(cpu, attrs); + if (phys != -1) { + /* Locks grabbed by tb_invalidate_phys_addr */ + tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, + phys | (pc & ~TARGET_PAGE_MASK)); + } +} +#endif #if defined(CONFIG_USER_ONLY) void cpu_watchpoint_remove_all(CPUState *cpu, int mask) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8096d64a1d..bf8da2aa5a 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -290,6 +290,9 @@ static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } +static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) +{ +} #endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ From cb58a6d3611b2d0ff4ed29bb6b659c95a23cb2d5 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Wed, 12 Jul 2017 17:51:43 -0400 Subject: [PATCH 23/55] mttcg/i386: Patch instruction using async_safe_* framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In mttcg, calling pause_all_vcpus() during execution from the generated TBs causes a deadlock if some vCPU is waiting for exclusive execution in start_exclusive(). Fix this by using the aync_safe_* framework instead of pausing vcpus for patching instructions. CC: Paolo Bonzini CC: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Pranith Kumar Message-Id: <20170712215143.19594-2-bobby.prani@gmail.com> [Get rid completely of the TCG-specific code. - Paolo] Signed-off-by: Paolo Bonzini --- hw/i386/kvmvapic.c | 115 +++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index 0d9ef77580..fc962c5fbc 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -383,8 +383,7 @@ static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte) cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1); } -static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, - uint32_t target) +static void patch_call(X86CPU *cpu, target_ulong ip, uint32_t target) { uint32_t offset; @@ -393,16 +392,59 @@ static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1); } +typedef struct PatchInfo { + VAPICHandlers *handler; + target_ulong ip; +} PatchInfo; + +static void do_patch_instruction(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *x86_cpu = X86_CPU(cs); + PatchInfo *info = (PatchInfo *) data.host_ptr; + VAPICHandlers *handlers = info->handler; + target_ulong ip = info->ip; + uint8_t opcode[2]; + uint32_t imm32 = 0; + + cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); + + switch (opcode[0]) { + case 0x89: /* mov r32 to r/m32 */ + patch_byte(x86_cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ + patch_call(x86_cpu, ip + 1, handlers->set_tpr); + break; + case 0x8b: /* mov r/m32 to r32 */ + patch_byte(x86_cpu, ip, 0x90); + patch_call(x86_cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); + break; + case 0xa1: /* mov abs to eax */ + patch_call(x86_cpu, ip, handlers->get_tpr[0]); + break; + case 0xa3: /* mov eax to abs */ + patch_call(x86_cpu, ip, handlers->set_tpr_eax); + break; + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + patch_byte(x86_cpu, ip, 0x68); /* push imm32 */ + cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); + cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); + patch_call(x86_cpu, ip + 5, handlers->set_tpr); + break; + case 0xff: /* push r/m32 */ + patch_byte(x86_cpu, ip, 0x50); /* push eax */ + patch_call(x86_cpu, ip + 1, handlers->get_tpr_stack); + break; + default: + abort(); + } + + g_free(info); +} + static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) { CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; VAPICHandlers *handlers; - uint8_t opcode[2]; - uint32_t imm32 = 0; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - uint32_t current_flags = 0; + PatchInfo *info; if (smp_cpus == 1) { handlers = &s->rom_state.up; @@ -410,60 +452,11 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) handlers = &s->rom_state.mp; } - if (tcg_enabled()) { - cpu_restore_state(cs, cs->mem_io_pc); - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - /* Account this instruction, because we will exit the tb. - This is the first instruction in the block. Therefore - there is no need in restoring CPU state. */ - if (use_icount) { - --cs->icount_decr.u16.low; - } - } + info = g_new(PatchInfo, 1); + info->handler = handlers; + info->ip = ip; - pause_all_vcpus(); - - cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); - - switch (opcode[0]) { - case 0x89: /* mov r32 to r/m32 */ - patch_byte(cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ - patch_call(s, cpu, ip + 1, handlers->set_tpr); - break; - case 0x8b: /* mov r/m32 to r32 */ - patch_byte(cpu, ip, 0x90); - patch_call(s, cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); - break; - case 0xa1: /* mov abs to eax */ - patch_call(s, cpu, ip, handlers->get_tpr[0]); - break; - case 0xa3: /* mov eax to abs */ - patch_call(s, cpu, ip, handlers->set_tpr_eax); - break; - case 0xc7: /* mov imm32, r/m32 (c7/0) */ - patch_byte(cpu, ip, 0x68); /* push imm32 */ - cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); - cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); - patch_call(s, cpu, ip + 5, handlers->set_tpr); - break; - case 0xff: /* push r/m32 */ - patch_byte(cpu, ip, 0x50); /* push eax */ - patch_call(s, cpu, ip + 1, handlers->get_tpr_stack); - break; - default: - abort(); - } - - resume_all_vcpus(); - - if (tcg_enabled()) { - /* Both tb_lock and iothread_mutex will be reset when - * longjmps back into the cpu_exec loop. */ - tb_lock(); - tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1); - cpu_loop_exit_noexc(cs); - } + async_safe_run_on_cpu(cs, do_patch_instruction, RUN_ON_CPU_HOST_PTR(info)); } void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, From 118e226884a762f3ef21351c05043e32be44c1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 12 Jul 2017 11:52:13 +0100 Subject: [PATCH 24/55] gdbstub: modernise DEBUG_GDB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the a gdb_debug helper which compiles away to nothing when not used but still ensures the format strings are checked. There is some minor code motion for the incorrect checksum message to report it before we attempt to send the reply. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Greg Kurz Message-Id: <20170712105216.747-2-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- gdbstub.c | 77 ++++++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index e561244292..39659802ad 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -272,7 +272,20 @@ static int gdb_signal_to_target (int sig) return -1; } -//#define DEBUG_GDB +/* #define DEBUG_GDB */ + +#ifdef DEBUG_GDB +# define DEBUG_GDB_GATE 1 +#else +# define DEBUG_GDB_GATE 0 +#endif + +#define gdb_debug(fmt, ...) do { \ + if (DEBUG_GDB_GATE) { \ + fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); \ + } \ +} while (0) + typedef struct GDBRegisterState { int base_reg; @@ -548,9 +561,7 @@ static int put_packet_binary(GDBState *s, const char *buf, int len) /* return -1 if error, 0 if OK */ static int put_packet(GDBState *s, const char *buf) { -#ifdef DEBUG_GDB - printf("reply='%s'\n", buf); -#endif + gdb_debug("reply='%s'\n", buf); return put_packet_binary(s, buf, strlen(buf)); } @@ -956,9 +967,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) uint8_t *registers; target_ulong addr, len; -#ifdef DEBUG_GDB - printf("command='%s'\n", line_buf); -#endif + + gdb_debug("command='%s'\n", line_buf); + p = line_buf; ch = *p++; switch(ch) { @@ -1519,17 +1530,14 @@ static void gdb_read_byte(GDBState *s, int ch) /* Waiting for a response to the last packet. If we see the start of a new command then abandon the previous response. */ if (ch == '-') { -#ifdef DEBUG_GDB - printf("Got NACK, retransmitting\n"); -#endif + gdb_debug("Got NACK, retransmitting\n"); put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + } else if (ch == '+') { + gdb_debug("Got ACK\n"); + } else { + gdb_debug("Got '%c' when expecting ACK/NACK\n", ch); } -#ifdef DEBUG_GDB - else if (ch == '+') - printf("Got ACK\n"); - else - printf("Got '%c' when expecting ACK/NACK\n", ch); -#endif + if (ch == '+' || ch == '$') s->last_packet_len = 0; if (ch != '$') @@ -1550,9 +1558,7 @@ static void gdb_read_byte(GDBState *s, int ch) s->line_sum = 0; s->state = RS_GETLINE; } else { -#ifdef DEBUG_GDB - printf("gdbstub received garbage between packets: 0x%x\n", ch); -#endif + gdb_debug("received garbage between packets: 0x%x\n", ch); } break; case RS_GETLINE: @@ -1568,9 +1574,7 @@ static void gdb_read_byte(GDBState *s, int ch) /* end of command, start of checksum*/ s->state = RS_CHKSUM1; } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { -#ifdef DEBUG_GDB - printf("gdbstub command buffer overrun, dropping command\n"); -#endif + gdb_debug("command buffer overrun, dropping command\n"); s->state = RS_IDLE; } else { /* unescaped command character */ @@ -1584,9 +1588,7 @@ static void gdb_read_byte(GDBState *s, int ch) s->state = RS_CHKSUM1; } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { /* command buffer overrun */ -#ifdef DEBUG_GDB - printf("gdbstub command buffer overrun, dropping command\n"); -#endif + gdb_debug("command buffer overrun, dropping command\n"); s->state = RS_IDLE; } else { /* parse escaped character and leave escape state */ @@ -1598,25 +1600,18 @@ static void gdb_read_byte(GDBState *s, int ch) case RS_GETLINE_RLE: if (ch < ' ') { /* invalid RLE count encoding */ -#ifdef DEBUG_GDB - printf("gdbstub got invalid RLE count: 0x%x\n", ch); -#endif + gdb_debug("got invalid RLE count: 0x%x\n", ch); s->state = RS_GETLINE; } else { /* decode repeat length */ int repeat = (unsigned char)ch - ' ' + 3; if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { /* that many repeats would overrun the command buffer */ -#ifdef DEBUG_GDB - printf("gdbstub command buffer overrun," - " dropping command\n"); -#endif + gdb_debug("command buffer overrun, dropping command\n"); s->state = RS_IDLE; } else if (s->line_buf_index < 1) { /* got a repeat but we have nothing to repeat */ -#ifdef DEBUG_GDB - printf("gdbstub got invalid RLE sequence\n"); -#endif + gdb_debug("got invalid RLE sequence\n"); s->state = RS_GETLINE; } else { /* repeat the last character */ @@ -1631,9 +1626,7 @@ static void gdb_read_byte(GDBState *s, int ch) case RS_CHKSUM1: /* get high hex digit of checksum */ if (!isxdigit(ch)) { -#ifdef DEBUG_GDB - printf("gdbstub got invalid command checksum digit\n"); -#endif + gdb_debug("got invalid command checksum digit\n"); s->state = RS_GETLINE; break; } @@ -1644,21 +1637,17 @@ static void gdb_read_byte(GDBState *s, int ch) case RS_CHKSUM2: /* get low hex digit of checksum */ if (!isxdigit(ch)) { -#ifdef DEBUG_GDB - printf("gdbstub got invalid command checksum digit\n"); -#endif + gdb_debug("got invalid command checksum digit\n"); s->state = RS_GETLINE; break; } s->line_csum |= fromhex(ch); if (s->line_csum != (s->line_sum & 0xff)) { + gdb_debug("got command packet with incorrect checksum\n"); /* send NAK reply */ reply = '-'; put_buffer(s, &reply, 1); -#ifdef DEBUG_GDB - printf("gdbstub got command packet with incorrect checksum\n"); -#endif s->state = RS_IDLE; } else { /* send ACK reply */ From d2a6c8570b15413f43891fbb3c41521c68ef8297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 12 Jul 2017 11:52:14 +0100 Subject: [PATCH 25/55] gdbstub: rename cpu_index -> cpu_gdb_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to make it clear the index is purely a gdbstub function and should not be confused with the value of cpu->cpu_index. At the same time we move the function from the header to gdbstub itself which will help with later changes. Signed-off-by: Alex Bennée Reviewed-by: Greg Kurz Reviewed-by: Claudio Imbrenda Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20170712105216.747-3-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- gdbstub.c | 26 ++++++++++++++++++++------ include/exec/gdbstub.h | 9 --------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 39659802ad..b1efe0bccb 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -56,6 +56,20 @@ static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } +/* Return the GDB index for a given vCPU state. + * + * For user mode this is simply the thread id. In system mode GDB + * numbers CPUs from 1 as 0 is reserved as an "any cpu" index. + */ +static inline int cpu_gdb_index(CPUState *cpu) +{ +#if defined(CONFIG_USER_ONLY) + return cpu->host_tid; +#else + return cpu->cpu_index + 1; +#endif +} + enum { GDB_SIGNAL_0 = 0, GDB_SIGNAL_INT = 2, @@ -838,7 +852,7 @@ static CPUState *find_cpu(uint32_t thread_id) CPUState *cpu; CPU_FOREACH(cpu) { - if (cpu_index(cpu) == thread_id) { + if (cpu_gdb_index(cpu) == thread_id) { return cpu; } } @@ -926,7 +940,7 @@ static int gdb_handle_vcont(GDBState *s, const char *p) idx = tmp; /* 0 means any thread, so we pick the first valid CPU */ if (!idx) { - idx = cpu_index(first_cpu); + idx = cpu_gdb_index(first_cpu); } /* @@ -976,7 +990,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - cpu_index(s->c_cpu)); + cpu_gdb_index(s->c_cpu)); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state @@ -1244,7 +1258,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%x", cpu_gdb_index(s->query_cpu)); put_packet(s, buf); s->query_cpu = CPU_NEXT(s->query_cpu); } else @@ -1401,7 +1415,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } snprintf(buf, sizeof(buf), "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, cpu_index(cpu), type, + GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type, (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; @@ -1435,7 +1449,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_gdb_index(cpu)); send_packet: put_packet(s, buf); diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index f9708bbcd6..9aa7756d92 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -58,15 +58,6 @@ void gdb_register_coprocessor(CPUState *cpu, gdb_reg_cb get_reg, gdb_reg_cb set_reg, int num_regs, const char *xml, int g_pos); -static inline int cpu_index(CPUState *cpu) -{ -#if defined(CONFIG_USER_ONLY) - return cpu->host_tid; -#else - return cpu->cpu_index + 1; -#endif -} - /* The GDB remote protocol transfers values in target byte order. This means * we can use the raw memory access routines to access the value buffer. * Conveniently, these also handle the case where the buffer is mis-aligned. From bd88c780e6a17188f7fd676f3a056e5db21500e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 12 Jul 2017 11:52:15 +0100 Subject: [PATCH 26/55] qom/cpu: remove host_tid field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was only used by the gdbstub and even then was only being set for subsequent threads. Rather the continue duplicating the number just make the gdbstub get the information from TaskState structure. Now the tid is correctly reported for all threads the bug I was seeing with "vCont;C04:0;c" packets is fixed as the correct tid is reported to gdb. I moved cpu_gdb_index into the gdbstub to facilitate easy access to the TaskState which is used elsewhere in gdbstub. To prevent BSD failing to build I've included ts_tid into its TaskStruct but not populated it - which was the same state as the old cpu->host_tid. I'll leave it up to the BSD maintainers to actually populate this properly if they want a working gdbstub with user-threads. Signed-off-by: Alex Bennée Reviewed-by: Greg Kurz Reviewed-by: Claudio Imbrenda Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20170712105216.747-4-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- bsd-user/qemu.h | 2 ++ gdbstub.c | 3 ++- include/qom/cpu.h | 2 -- linux-user/syscall.c | 1 - 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index b550cee0cb..19b2b8fecb 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -85,6 +85,8 @@ struct emulated_sigtable { /* NOTE: we force a big alignment so that the stack stored after is aligned too */ typedef struct TaskState { + pid_t ts_tid; /* tid (or pid) of this task */ + struct TaskState *next; int used; /* non zero if used */ struct image_info *info; diff --git a/gdbstub.c b/gdbstub.c index b1efe0bccb..378fdd874d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -64,7 +64,8 @@ static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, static inline int cpu_gdb_index(CPUState *cpu) { #if defined(CONFIG_USER_ONLY) - return cpu->host_tid; + TaskState *ts = (TaskState *) cpu->opaque; + return ts->ts_tid; #else return cpu->cpu_index + 1; #endif diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 7bfd50cc32..d3c783b4b5 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -265,7 +265,6 @@ struct qemu_work_item; * @cpu_index: CPU index (informative). * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU. - * @host_tid: Host thread ID. * @running: #true if CPU is currently running (lockless). * @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end; * valid under cpu_list_lock. @@ -319,7 +318,6 @@ struct CPUState { HANDLE hThread; #endif int thread_id; - uint32_t host_tid; bool running, has_waiter; struct QemuCond *halt_cond; bool thread_kicked; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 925ae11ea6..003943b736 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6219,7 +6219,6 @@ static void *clone_func(void *arg) thread_cpu = cpu; ts = (TaskState *)cpu->opaque; info->tid = gettid(); - cpu->host_tid = info->tid; task_settid(ts); if (info->child_tidptr) put_user_u32(info->tid, info->child_tidptr); From 5a6a1ad181c658b810041d852b290ac836965aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 12 Jul 2017 11:52:16 +0100 Subject: [PATCH 27/55] gdbstub: don't fail on vCont; C04:0; c packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The thread-id of 0 means any CPU but we then ignore the fact we find the first_cpu in this case who can have an index of 0. Instead of bailing out just test if we have managed to match up thread-id to a CPU. Otherwise you get: gdb_handle_packet: command='vCont;C04:0;c' put_packet: reply='E22' The actual reason for gdb sending vCont;C04:0;c was fixed in a previous commit where we ensure the first_cpu's tid is correctly reported to gdb however we should still behave correctly next time it does send 0. Signed-off-by: Alex Bennée Reviewed-by: Greg Kurz Reviewed-by: Claudio Imbrenda Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20170712105216.747-5-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- gdbstub.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 378fdd874d..f936ddd32d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -938,23 +938,16 @@ static int gdb_handle_vcont(GDBState *s, const char *p) if (res) { goto out; } - idx = tmp; - /* 0 means any thread, so we pick the first valid CPU */ - if (!idx) { - idx = cpu_gdb_index(first_cpu); - } - /* - * If we are in user mode, the thread specified is actually a - * thread id, and not an index. We need to find the actual - * CPU first, and only then we can use its index. - */ - cpu = find_cpu(idx); + /* 0 means any thread, so we pick the first valid CPU */ + cpu = tmp ? find_cpu(tmp) : first_cpu; + /* invalid CPU/thread specified */ - if (!idx || !cpu) { + if (!cpu) { res = -EINVAL; goto out; } + /* only use if no previous match occourred */ if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; From 98fab4c163adb980568afa40824208edbcd6d70c Mon Sep 17 00:00:00 2001 From: Peng Hao Date: Wed, 12 Jul 2017 23:41:59 +0800 Subject: [PATCH 28/55] chardev: fix parallel device can't be reconnect Parallel device don't register be->chr_can_read function, but remote disconnect event is handled in chr_read.So connected parallel device can not detect remote disconnect event. The chardevs with chr_can_read=NULL has the same problem. Signed-off-by: Peng Hao Reviewed-by: Wang Yechao Reviewed-by: Jiang Biao Message-Id: <1499874119-67558-1-git-send-email-peng.hao2@zte.com.cn> Signed-off-by: Paolo Bonzini --- hw/char/parallel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 055789f791..f79dc76543 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -503,6 +503,10 @@ static const VMStateDescription vmstate_parallel_isa = { } }; +static int parallel_can_receive(void *opaque) +{ + return 1; +} static void parallel_isa_realizefn(DeviceState *dev, Error **errp) { @@ -535,6 +539,8 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) isa_init_irq(isadev, &s->irq, isa->isairq); qemu_register_reset(parallel_reset, s); + qemu_chr_fe_set_handlers(&s->chr, parallel_can_receive, NULL, + NULL, NULL, s, NULL, true); if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { s->hw_driver = 1; s->status = dummy; From 3df9d748067f5a7f01b98ddc63597c98c8244a95 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 11 Jul 2017 13:56:19 +1000 Subject: [PATCH 29/55] memory/iommu: QOM'fy IOMMU MemoryRegion This defines new QOM object - IOMMUMemoryRegion - with MemoryRegion as a parent. This moves IOMMU-related fields from MR to IOMMU MR. However to avoid dymanic QOM casting in fast path (address_space_translate, etc), this adds an @is_iommu boolean flag to MR and provides new helper to do simple cast to IOMMU MR - memory_region_get_iommu. The flag is set in the instance init callback. This defines memory_region_is_iommu as memory_region_get_iommu()!=NULL. This switches MemoryRegion to IOMMUMemoryRegion in most places except the ones where MemoryRegion may be an alias. Signed-off-by: Alexey Kardashevskiy Reviewed-by: David Gibson Message-Id: <20170711035620.4232-2-aik@ozlabs.ru> Acked-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- exec.c | 12 ++-- hw/alpha/typhoon.c | 8 ++- hw/dma/rc4030.c | 8 +-- hw/i386/amd_iommu.c | 9 +-- hw/i386/intel_iommu.c | 17 +++--- hw/mips/mips_jazz.c | 2 +- hw/pci-host/apb.c | 6 +- hw/ppc/spapr_iommu.c | 16 +++--- hw/s390x/s390-pci-bus.c | 6 +- hw/s390x/s390-pci-bus.h | 2 +- hw/s390x/s390-pci-inst.c | 8 +-- hw/vfio/common.c | 12 ++-- hw/vfio/spapr.c | 3 +- include/exec/memory.h | 55 +++++++++++------- include/hw/i386/intel_iommu.h | 2 +- include/hw/mips/mips.h | 2 +- include/hw/ppc/spapr.h | 3 +- include/hw/vfio/vfio-common.h | 2 +- include/qemu/typedefs.h | 1 + memory.c | 105 ++++++++++++++++++++++------------ 20 files changed, 170 insertions(+), 109 deletions(-) diff --git a/exec.c b/exec.c index 2e8bc43421..2aa8be59ef 100644 --- a/exec.c +++ b/exec.c @@ -480,19 +480,19 @@ static MemoryRegionSection address_space_do_translate(AddressSpace *as, { IOMMUTLBEntry iotlb; MemoryRegionSection *section; - MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; for (;;) { AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch); section = address_space_translate_internal(d, addr, &addr, plen, is_mmio); - mr = section->mr; - if (!mr->iommu_ops) { + iommu_mr = memory_region_get_iommu(section->mr); + if (!iommu_mr) { break; } - iotlb = mr->iommu_ops->translate(mr, addr, is_write ? - IOMMU_WO : IOMMU_RO); + iotlb = iommu_mr->iommu_ops->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO); addr = ((iotlb.translated_addr & ~iotlb.addr_mask) | (addr & iotlb.addr_mask)); *plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1); @@ -588,7 +588,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, section = address_space_translate_internal(d, addr, xlat, plen, false); - assert(!section->mr->iommu_ops); + assert(!memory_region_is_iommu(section->mr)); return section; } #endif diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index c1cf7802a4..dd47b4569f 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -41,7 +41,7 @@ typedef struct TyphoonPchip { MemoryRegion reg_conf; AddressSpace iommu_as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; uint64_t ctl; TyphoonWindow win[4]; @@ -663,7 +663,8 @@ static bool window_translate(TyphoonWindow *win, hwaddr addr, /* Handle PCI-to-system address translation. */ /* TODO: A translation failure here ought to set PCI error codes on the Pchip and generate a machine check interrupt. */ -static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flag) { TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); @@ -893,7 +894,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Host memory as seen from the PCI side, via the IOMMU. */ memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, "iommu-typhoon", UINT64_MAX); - address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci"); + address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&s->pchip.iommu), + "pchip0-pci"); pci_setup_iommu(b, typhoon_pci_dma_iommu, s); /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index edf9432051..32c06760ef 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -90,7 +90,7 @@ typedef struct rc4030State qemu_irq jazz_bus_irq; /* whole DMA memory region, root of DMA address space */ - MemoryRegion dma_mr; + IOMMUMemoryRegion dma_mr; AddressSpace dma_as; MemoryRegion iomem_chipset; @@ -488,7 +488,7 @@ static const MemoryRegionOps jazzio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static IOMMUTLBEntry rc4030_dma_translate(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { rc4030State *s = container_of(iommu, rc4030State, dma_mr); @@ -679,7 +679,7 @@ static void rc4030_realize(DeviceState *dev, Error **errp) memory_region_init_iommu(&s->dma_mr, o, &rc4030_dma_ops, "rc4030.dma", UINT32_MAX); - address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma"); + address_space_init(&s->dma_as, MEMORY_REGION(&s->dma_mr), "rc4030-dma"); } static void rc4030_unrealize(DeviceState *dev, Error **errp) @@ -717,7 +717,7 @@ static void rc4030_register_types(void) type_init(rc4030_register_types) -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr) +DeviceState *rc4030_init(rc4030_dma **dmas, IOMMUMemoryRegion **dma_mr) { DeviceState *dev; diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index d93ffc2a15..7a67c573af 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -52,7 +52,7 @@ struct AMDVIAddressSpace { uint8_t bus_num; /* bus number */ uint8_t devfn; /* device function */ AMDVIState *iommu_state; /* AMDVI - one per machine */ - MemoryRegion iommu; /* Device's address translation region */ + IOMMUMemoryRegion iommu; /* Device's address translation region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */ AddressSpace as; /* device's corresponding address space */ }; @@ -987,7 +987,7 @@ static inline bool amdvi_is_interrupt_addr(hwaddr addr) return addr >= AMDVI_INT_ADDR_FIRST && addr <= AMDVI_INT_ADDR_LAST; } -static IOMMUTLBEntry amdvi_translate(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); @@ -1046,7 +1046,8 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) memory_region_init_iommu(&iommu_as[devfn]->iommu, OBJECT(s), &s->iommu_ops, "amd-iommu", UINT64_MAX); - address_space_init(&iommu_as[devfn]->as, &iommu_as[devfn]->iommu, + address_space_init(&iommu_as[devfn]->as, + MEMORY_REGION(&iommu_as[devfn]->iommu), "amd-iommu"); } return &iommu_as[devfn]->as; @@ -1067,7 +1068,7 @@ static const MemoryRegionOps mmio_mem_ops = { } }; -static void amdvi_iommu_notify_flag_changed(MemoryRegion *iommu, +static void amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 88dc042b5c..11dba0e274 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -972,9 +972,9 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) /* Turn off first then on the other */ if (use_iommu) { memory_region_set_enabled(&as->sys_alias, false); - memory_region_set_enabled(&as->iommu, true); + memory_region_set_enabled(MEMORY_REGION(&as->iommu), true); } else { - memory_region_set_enabled(&as->iommu, false); + memory_region_set_enabled(MEMORY_REGION(&as->iommu), false); memory_region_set_enabled(&as->sys_alias, true); } @@ -1366,7 +1366,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, void *private) { - memory_region_notify_iommu((MemoryRegion *)private, *entry); + memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); return 0; } @@ -2264,7 +2264,7 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } } -static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); @@ -2303,7 +2303,7 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, return iotlb; } -static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, +static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -2736,7 +2736,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, &vtd_dev_as->sys_alias, 1); memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, - &vtd_dev_as->iommu, 1); + MEMORY_REGION(&vtd_dev_as->iommu), + 1); vtd_switch_address_space(vtd_dev_as); } return vtd_dev_as; @@ -2816,9 +2817,9 @@ static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) return 0; } -static void vtd_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) { - VTDAddressSpace *vtd_as = container_of(mr, VTDAddressSpace, iommu); + VTDAddressSpace *vtd_as = container_of(iommu_mr, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; uint8_t bus_n = pci_bus_num(vtd_as->bus); VTDContextEntry ce; diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 1cef581878..1f69322c15 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -130,7 +130,7 @@ static void mips_jazz_init(MachineState *machine, CPUMIPSState *env; qemu_irq *i8259; rc4030_dma *dmas; - MemoryRegion *rc4030_dma_mr; + IOMMUMemoryRegion *rc4030_dma_mr; MemoryRegion *isa_mem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); MemoryRegion *rtc = g_new(MemoryRegion, 1); diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index 326f5ef024..76a56ae29b 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -123,7 +123,7 @@ do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0) typedef struct IOMMUState { AddressSpace iommu_as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; uint64_t regs[IOMMU_NREGS]; } IOMMUState; @@ -208,7 +208,7 @@ static AddressSpace *pbm_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) } /* Called from RCU critical section */ -static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry pbm_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); @@ -699,7 +699,7 @@ PCIBus *pci_apb_init(hwaddr special_base, memory_region_init_iommu(&is->iommu, OBJECT(dev), &pbm_iommu_ops, "iommu-apb", UINT64_MAX); - address_space_init(&is->iommu_as, &is->iommu, "pbm-as"); + address_space_init(&is->iommu_as, MEMORY_REGION(&is->iommu), "pbm-as"); pci_setup_iommu(phb->bus, pbm_pci_dma_iommu, is); /* APB secondary busses */ diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 8656a54a3e..6b6ced5710 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -110,7 +110,8 @@ static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) } /* Called from RCU critical section */ -static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flag) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); @@ -150,14 +151,14 @@ static void spapr_tce_table_pre_save(void *opaque) tcet->bus_offset, tcet->page_shift); } -static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu) +static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); return 1ULL << tcet->page_shift; } -static void spapr_tce_notify_flag_changed(MemoryRegion *iommu, +static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -348,9 +349,10 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, &tcet->fd, tcet->need_vfio); - memory_region_set_size(&tcet->iommu, + memory_region_set_size(MEMORY_REGION(&tcet->iommu), (uint64_t)tcet->nb_table << tcet->page_shift); - memory_region_add_subregion(&tcet->root, tcet->bus_offset, &tcet->iommu); + memory_region_add_subregion(&tcet->root, tcet->bus_offset, + MEMORY_REGION(&tcet->iommu)); } void spapr_tce_table_disable(sPAPRTCETable *tcet) @@ -359,8 +361,8 @@ void spapr_tce_table_disable(sPAPRTCETable *tcet) return; } - memory_region_del_subregion(&tcet->root, &tcet->iommu); - memory_region_set_size(&tcet->iommu, 0); + memory_region_del_subregion(&tcet->root, MEMORY_REGION(&tcet->iommu)); + memory_region_set_size(MEMORY_REGION(&tcet->iommu), 0); spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table); tcet->fd = -1; diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 5651483781..e4fc82cbe1 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -356,7 +356,7 @@ out: return pte; } -static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *mr, hwaddr addr, +static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, IOMMUAccessFlags flag) { uint64_t pte; @@ -525,14 +525,14 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu) memory_region_init_iommu(&iommu->iommu_mr, OBJECT(&iommu->mr), &s390_iommu_ops, name, iommu->pal + 1); iommu->enabled = true; - memory_region_add_subregion(&iommu->mr, 0, &iommu->iommu_mr); + memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr)); g_free(name); } void s390_pci_iommu_disable(S390PCIIOMMU *iommu) { iommu->enabled = false; - memory_region_del_subregion(&iommu->mr, &iommu->iommu_mr); + memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr)); object_unparent(OBJECT(&iommu->iommu_mr)); } diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index cf142a3e68..6a599ed353 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -266,7 +266,7 @@ typedef struct S390PCIIOMMU { S390PCIBusDevice *pbdev; AddressSpace as; MemoryRegion mr; - MemoryRegion iommu_mr; + IOMMUMemoryRegion iommu_mr; bool enabled; uint64_t g_iota; uint64_t pba; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 8bc7c98682..a53c29c487 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -563,7 +563,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) S390PCIIOMMU *iommu; hwaddr start, end; IOMMUTLBEntry entry; - MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; cpu_synchronize_state(CPU(cpu)); @@ -622,9 +622,9 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - mr = &iommu->iommu_mr; + iommu_mr = &iommu->iommu_mr; while (start < end) { - entry = mr->iommu_ops->translate(mr, start, IOMMU_NONE); + entry = iommu_mr->iommu_ops->translate(iommu_mr, start, IOMMU_NONE); if (!entry.translated_addr) { pbdev->state = ZPCI_FS_ERROR; @@ -635,7 +635,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - memory_region_notify_iommu(mr, entry); + memory_region_notify_iommu(iommu_mr, entry); start += entry.addr_mask + 1; } diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 29923e4990..c1bb6d429a 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -479,6 +479,7 @@ static void vfio_listener_region_add(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); trace_vfio_listener_region_add_iommu(iova, end); /* @@ -488,7 +489,7 @@ static void vfio_listener_region_add(MemoryListener *listener, * device emulation the VFIO iommu handles to use). */ giommu = g_malloc0(sizeof(*giommu)); - giommu->iommu = section->mr; + giommu->iommu = iommu_mr; giommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; giommu->container = container; @@ -501,7 +502,7 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend)); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); - memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); + memory_region_register_iommu_notifier(section->mr, &giommu->n); memory_region_iommu_replay(giommu->iommu, &giommu->n); return; @@ -569,9 +570,9 @@ static void vfio_listener_region_del(MemoryListener *listener, VFIOGuestIOMMU *giommu; QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (giommu->iommu == section->mr && + if (MEMORY_REGION(giommu->iommu) == section->mr && giommu->n.start == section->offset_within_region) { - memory_region_unregister_iommu_notifier(giommu->iommu, + memory_region_unregister_iommu_notifier(section->mr, &giommu->n); QLIST_REMOVE(giommu, giommu_next); g_free(giommu); @@ -1163,7 +1164,8 @@ static void vfio_disconnect_container(VFIOGroup *group) QLIST_REMOVE(container, next); QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { - memory_region_unregister_iommu_notifier(giommu->iommu, &giommu->n); + memory_region_unregister_iommu_notifier( + MEMORY_REGION(giommu->iommu), &giommu->n); QLIST_REMOVE(giommu, giommu_next); g_free(giommu); } diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 4409bcc0d7..32fd6a9b54 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -143,7 +143,8 @@ int vfio_spapr_create_window(VFIOContainer *container, hwaddr *pgsize) { int ret; - unsigned pagesize = memory_region_iommu_get_min_page_size(section->mr); + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + unsigned pagesize = memory_region_iommu_get_min_page_size(iommu_mr); unsigned entries, pages; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; diff --git a/include/exec/memory.h b/include/exec/memory.h index 8503685455..92980abfad 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -35,6 +35,10 @@ #define MEMORY_REGION(obj) \ OBJECT_CHECK(MemoryRegion, (obj), TYPE_MEMORY_REGION) +#define TYPE_IOMMU_MEMORY_REGION "qemu:iommu-memory-region" +#define IOMMU_MEMORY_REGION(obj) \ + OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_IOMMU_MEMORY_REGION) + typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegionMmio MemoryRegionMmio; @@ -198,16 +202,16 @@ struct MemoryRegionIOMMUOps { * set flag to IOMMU_NONE to mean that we don't need any * read/write permission checks, like, when for region replay. */ - IOMMUTLBEntry (*translate)(MemoryRegion *iommu, hwaddr addr, + IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag); /* Returns minimum supported page size */ - uint64_t (*get_min_page_size)(MemoryRegion *iommu); + uint64_t (*get_min_page_size)(IOMMUMemoryRegion *iommu); /* Called when IOMMU Notifier flag changed */ - void (*notify_flag_changed)(MemoryRegion *iommu, + void (*notify_flag_changed)(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old_flags, IOMMUNotifierFlag new_flags); /* Set this up to provide customized IOMMU replay function */ - void (*replay)(MemoryRegion *iommu, IOMMUNotifier *notifier); + void (*replay)(IOMMUMemoryRegion *iommu, IOMMUNotifier *notifier); }; typedef struct CoalescedMemoryRange CoalescedMemoryRange; @@ -227,9 +231,9 @@ struct MemoryRegion { bool flush_coalesced_mmio; bool global_locking; uint8_t dirty_log_mask; + bool is_iommu; RAMBlock *ram_block; Object *owner; - const MemoryRegionIOMMUOps *iommu_ops; const MemoryRegionOps *ops; void *opaque; @@ -252,6 +256,12 @@ struct MemoryRegion { const char *name; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; +}; + +struct IOMMUMemoryRegion { + MemoryRegion parent_obj; + + const MemoryRegionIOMMUOps *iommu_ops; QLIST_HEAD(, IOMMUNotifier) iommu_notify; IOMMUNotifierFlag iommu_notify_flags; }; @@ -618,13 +628,13 @@ static inline void memory_region_init_reservation(MemoryRegion *mr, * An IOMMU region translates addresses and forwards accesses to a target * memory region. * - * @mr: the #MemoryRegion to be initialized + * @iommu_mr: the #IOMMUMemoryRegion to be initialized * @owner: the object that tracks the region's reference count * @ops: a function that translates addresses into the @target region * @name: used for debugging; not visible to the user or ABI * @size: size of the region. */ -void memory_region_init_iommu(MemoryRegion *mr, +void memory_region_init_iommu(IOMMUMemoryRegion *iommu_mr, struct Object *owner, const MemoryRegionIOMMUOps *ops, const char *name, @@ -679,20 +689,25 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) } /** - * memory_region_is_iommu: check whether a memory region is an iommu + * memory_region_get_iommu: check whether a memory region is an iommu * - * Returns %true is a memory region is an iommu. + * Returns pointer to IOMMUMemoryRegion if a memory region is an iommu, + * otherwise NULL. * * @mr: the memory region being queried */ -static inline bool memory_region_is_iommu(MemoryRegion *mr) +static inline IOMMUMemoryRegion *memory_region_get_iommu(MemoryRegion *mr) { if (mr->alias) { - return memory_region_is_iommu(mr->alias); + return memory_region_get_iommu(mr->alias); } - return mr->iommu_ops; + if (mr->is_iommu) { + return (IOMMUMemoryRegion *) mr; + } + return NULL; } +#define memory_region_is_iommu(mr) (memory_region_get_iommu(mr) != NULL) /** * memory_region_iommu_get_min_page_size: get minimum supported page size @@ -700,9 +715,9 @@ static inline bool memory_region_is_iommu(MemoryRegion *mr) * * Returns minimum supported page size for an iommu. * - * @mr: the memory region being queried + * @iommu_mr: the memory region being queried */ -uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr); +uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr); /** * memory_region_notify_iommu: notify a change in an IOMMU translation entry. @@ -716,12 +731,12 @@ uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr); * Note: for any IOMMU implementation, an in-place mapping change * should be notified with an UNMAP followed by a MAP. * - * @mr: the memory region that was changed + * @iommu_mr: the memory region that was changed * @entry: the new entry in the IOMMU translation table. The entry * replaces all old entries for the same virtual I/O address range. * Deleted entries have .@perm == 0. */ -void memory_region_notify_iommu(MemoryRegion *mr, +void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, IOMMUTLBEntry entry); /** @@ -756,18 +771,18 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, * a notifier with the minimum page granularity returned by * mr->iommu_ops->get_page_size(). * - * @mr: the memory region to observe + * @iommu_mr: the memory region to observe * @n: the notifier to which to replay iommu mappings */ -void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n); +void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); /** * memory_region_iommu_replay_all: replay existing IOMMU translations * to all the notifiers registered. * - * @mr: the memory region to observe + * @iommu_mr: the memory region to observe */ -void memory_region_iommu_replay_all(MemoryRegion *mr); +void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr); /** * memory_region_unregister_iommu_notifier: unregister a notifier for diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 3e51876b75..45fba4ff97 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -83,7 +83,7 @@ struct VTDAddressSpace { PCIBus *bus; uint8_t devfn; AddressSpace as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; MemoryRegion root; MemoryRegion sys_alias; MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h index 16412dc150..2f6774d540 100644 --- a/include/hw/mips/mips.h +++ b/include/hw/mips/mips.h @@ -19,6 +19,6 @@ typedef struct rc4030DMAState *rc4030_dma; void rc4030_dma_read(void *dma, uint8_t *buf, int len); void rc4030_dma_write(void *dma, uint8_t *buf, int len); -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr); +DeviceState *rc4030_init(rc4030_dma **dmas, IOMMUMemoryRegion **dma_mr); #endif diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index a184ffab0e..b353dfff2e 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -594,7 +594,8 @@ struct sPAPRTCETable { bool bypass; bool need_vfio; int fd; - MemoryRegion root, iommu; + MemoryRegion root; + IOMMUMemoryRegion iommu; struct VIOsPAPRDevice *vdev; /* for @bypass migration compatibility only */ QLIST_ENTRY(sPAPRTCETable) list; }; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0b475a3596..f3a2ac9fee 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -95,7 +95,7 @@ typedef struct VFIOContainer { typedef struct VFIOGuestIOMMU { VFIOContainer *container; - MemoryRegion *iommu; + IOMMUMemoryRegion *iommu; hwaddr iommu_offset; IOMMUNotifier n; QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 2706aabedf..b19159104c 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -45,6 +45,7 @@ typedef struct MachineState MachineState; typedef struct MemoryListener MemoryListener; typedef struct MemoryMappingList MemoryMappingList; typedef struct MemoryRegion MemoryRegion; +typedef struct IOMMUMemoryRegion IOMMUMemoryRegion; typedef struct MemoryRegionCache MemoryRegionCache; typedef struct MemoryRegionSection MemoryRegionSection; typedef struct MigrationIncomingState MigrationIncomingState; diff --git a/memory.c b/memory.c index 1044bbaf0c..45e10e2e3b 100644 --- a/memory.c +++ b/memory.c @@ -977,12 +977,11 @@ static char *memory_region_escape_name(const char *name) return escaped; } -void memory_region_init(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size) +static void memory_region_do_init(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size) { - object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION); mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); @@ -1006,6 +1005,15 @@ void memory_region_init(MemoryRegion *mr, } } +void memory_region_init(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size) +{ + object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION); + memory_region_do_init(mr, owner, name, size); +} + static void memory_region_get_addr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -1092,6 +1100,13 @@ static void memory_region_initfn(Object *obj) NULL, NULL, &error_abort); } +static void iommu_memory_region_initfn(Object *obj) +{ + MemoryRegion *mr = MEMORY_REGION(obj); + + mr->is_iommu = true; +} + static uint64_t unassigned_mem_read(void *opaque, hwaddr addr, unsigned size) { @@ -1491,17 +1506,22 @@ void memory_region_init_rom_device(MemoryRegion *mr, mr->ram_block = qemu_ram_alloc(size, mr, errp); } -void memory_region_init_iommu(MemoryRegion *mr, +void memory_region_init_iommu(IOMMUMemoryRegion *iommu_mr, Object *owner, const MemoryRegionIOMMUOps *ops, const char *name, uint64_t size) { - memory_region_init(mr, owner, name, size); - mr->iommu_ops = ops, + struct MemoryRegion *mr; + + object_initialize(iommu_mr, sizeof(*iommu_mr), TYPE_IOMMU_MEMORY_REGION); + mr = MEMORY_REGION(iommu_mr); + memory_region_do_init(mr, owner, name, size); + iommu_mr = IOMMU_MEMORY_REGION(mr); + iommu_mr->iommu_ops = ops, mr->terminates = true; /* then re-forwards */ - QLIST_INIT(&mr->iommu_notify); - mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE; + QLIST_INIT(&iommu_mr->iommu_notify); + iommu_mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE; } static void memory_region_finalize(Object *obj) @@ -1596,63 +1616,67 @@ bool memory_region_is_logging(MemoryRegion *mr, uint8_t client) return memory_region_get_dirty_log_mask(mr) & (1 << client); } -static void memory_region_update_iommu_notify_flags(MemoryRegion *mr) +static void memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr) { IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE; IOMMUNotifier *iommu_notifier; - IOMMU_NOTIFIER_FOREACH(iommu_notifier, mr) { + IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { flags |= iommu_notifier->notifier_flags; } - if (flags != mr->iommu_notify_flags && - mr->iommu_ops->notify_flag_changed) { - mr->iommu_ops->notify_flag_changed(mr, mr->iommu_notify_flags, - flags); + if (flags != iommu_mr->iommu_notify_flags && + iommu_mr->iommu_ops->notify_flag_changed) { + iommu_mr->iommu_ops->notify_flag_changed(iommu_mr, + iommu_mr->iommu_notify_flags, + flags); } - mr->iommu_notify_flags = flags; + iommu_mr->iommu_notify_flags = flags; } void memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n) { + IOMMUMemoryRegion *iommu_mr; + if (mr->alias) { memory_region_register_iommu_notifier(mr->alias, n); return; } /* We need to register for at least one bitfield */ + iommu_mr = IOMMU_MEMORY_REGION(mr); assert(n->notifier_flags != IOMMU_NOTIFIER_NONE); assert(n->start <= n->end); - QLIST_INSERT_HEAD(&mr->iommu_notify, n, node); - memory_region_update_iommu_notify_flags(mr); + QLIST_INSERT_HEAD(&iommu_mr->iommu_notify, n, node); + memory_region_update_iommu_notify_flags(iommu_mr); } -uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr) +uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr) { - assert(memory_region_is_iommu(mr)); - if (mr->iommu_ops && mr->iommu_ops->get_min_page_size) { - return mr->iommu_ops->get_min_page_size(mr); + if (iommu_mr->iommu_ops && iommu_mr->iommu_ops->get_min_page_size) { + return iommu_mr->iommu_ops->get_min_page_size(iommu_mr); } return TARGET_PAGE_SIZE; } -void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) { + MemoryRegion *mr = MEMORY_REGION(iommu_mr); hwaddr addr, granularity; IOMMUTLBEntry iotlb; /* If the IOMMU has its own replay callback, override */ - if (mr->iommu_ops->replay) { - mr->iommu_ops->replay(mr, n); + if (iommu_mr->iommu_ops->replay) { + iommu_mr->iommu_ops->replay(iommu_mr, n); return; } - granularity = memory_region_iommu_get_min_page_size(mr); + granularity = memory_region_iommu_get_min_page_size(iommu_mr); for (addr = 0; addr < memory_region_size(mr); addr += granularity) { - iotlb = mr->iommu_ops->translate(mr, addr, IOMMU_NONE); + iotlb = iommu_mr->iommu_ops->translate(iommu_mr, addr, IOMMU_NONE); if (iotlb.perm != IOMMU_NONE) { n->notify(n, &iotlb); } @@ -1665,24 +1689,27 @@ void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) } } -void memory_region_iommu_replay_all(MemoryRegion *mr) +void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr) { IOMMUNotifier *notifier; - IOMMU_NOTIFIER_FOREACH(notifier, mr) { - memory_region_iommu_replay(mr, notifier); + IOMMU_NOTIFIER_FOREACH(notifier, iommu_mr) { + memory_region_iommu_replay(iommu_mr, notifier); } } void memory_region_unregister_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n) { + IOMMUMemoryRegion *iommu_mr; + if (mr->alias) { memory_region_unregister_iommu_notifier(mr->alias, n); return; } QLIST_REMOVE(n, node); - memory_region_update_iommu_notify_flags(mr); + iommu_mr = IOMMU_MEMORY_REGION(mr); + memory_region_update_iommu_notify_flags(iommu_mr); } void memory_region_notify_one(IOMMUNotifier *notifier, @@ -1710,14 +1737,14 @@ void memory_region_notify_one(IOMMUNotifier *notifier, } } -void memory_region_notify_iommu(MemoryRegion *mr, +void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, IOMMUTLBEntry entry) { IOMMUNotifier *iommu_notifier; - assert(memory_region_is_iommu(mr)); + assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr))); - IOMMU_NOTIFIER_FOREACH(iommu_notifier, mr) { + IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { memory_region_notify_one(iommu_notifier, &entry); } } @@ -2825,9 +2852,17 @@ static const TypeInfo memory_region_info = { .instance_finalize = memory_region_finalize, }; +static const TypeInfo iommu_memory_region_info = { + .parent = TYPE_MEMORY_REGION, + .name = TYPE_IOMMU_MEMORY_REGION, + .instance_size = sizeof(IOMMUMemoryRegion), + .instance_init = iommu_memory_region_initfn, +}; + static void memory_register_types(void) { type_register_static(&memory_region_info); + type_register_static(&iommu_memory_region_info); } type_init(memory_register_types) From 1221a4746769f70231beab4db8da1c937e60340c Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 11 Jul 2017 13:56:20 +1000 Subject: [PATCH 30/55] memory/iommu: introduce IOMMUMemoryRegionClass This finishes QOM'fication of IOMMUMemoryRegion by introducing a IOMMUMemoryRegionClass. This also provides a fastpath analog for IOMMU_MEMORY_REGION_GET_CLASS(). This makes IOMMUMemoryRegion an abstract class. Signed-off-by: Alexey Kardashevskiy Message-Id: <20170711035620.4232-3-aik@ozlabs.ru> Acked-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- exec.c | 6 +++-- hw/alpha/typhoon.c | 23 ++++++++++++++---- hw/dma/rc4030.c | 26 +++++++++++++++----- hw/i386/amd_iommu.c | 24 +++++++++++++++---- hw/i386/amd_iommu.h | 5 ++-- hw/i386/intel_iommu.c | 25 +++++++++++++++---- hw/pci-host/apb.c | 23 ++++++++++++++---- hw/ppc/spapr_iommu.c | 26 ++++++++++++++------ hw/s390x/s390-pci-bus.c | 23 +++++++++++++----- hw/s390x/s390-pci-bus.h | 1 + hw/s390x/s390-pci-inst.c | 5 +++- include/exec/memory.h | 45 +++++++++++++++++++++++++++-------- include/hw/i386/intel_iommu.h | 3 ++- include/hw/ppc/spapr.h | 4 ++++ memory.c | 36 ++++++++++++++++------------ 15 files changed, 205 insertions(+), 70 deletions(-) diff --git a/exec.c b/exec.c index 2aa8be59ef..b3c080f731 100644 --- a/exec.c +++ b/exec.c @@ -481,6 +481,7 @@ static MemoryRegionSection address_space_do_translate(AddressSpace *as, IOMMUTLBEntry iotlb; MemoryRegionSection *section; IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; for (;;) { AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch); @@ -490,9 +491,10 @@ static MemoryRegionSection address_space_do_translate(AddressSpace *as, if (!iommu_mr) { break; } + imrc = memory_region_get_iommu_class_nocheck(iommu_mr); - iotlb = iommu_mr->iommu_ops->translate(iommu_mr, addr, is_write ? - IOMMU_WO : IOMMU_RO); + iotlb = imrc->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO); addr = ((iotlb.translated_addr & ~iotlb.addr_mask) | (addr & iotlb.addr_mask)); *plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1); diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index dd47b4569f..ae11e012c7 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -17,6 +17,7 @@ #define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" +#define TYPE_TYPHOON_IOMMU_MEMORY_REGION "typhoon-iommu-memory-region" typedef struct TyphoonCchip { MemoryRegion region; @@ -725,10 +726,6 @@ static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, return ret; } -static const MemoryRegionIOMMUOps typhoon_iommu_ops = { - .translate = typhoon_translate_iommu, -}; - static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { TyphoonState *s = opaque; @@ -892,7 +889,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qdev_init_nofail(dev); /* Host memory as seen from the PCI side, via the IOMMU. */ - memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, + memory_region_init_iommu(&s->pchip.iommu, sizeof(s->pchip.iommu), + TYPE_TYPHOON_IOMMU_MEMORY_REGION, OBJECT(s), "iommu-typhoon", UINT64_MAX); address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&s->pchip.iommu), "pchip0-pci"); @@ -953,9 +951,24 @@ static const TypeInfo typhoon_pcihost_info = { .class_init = typhoon_pcihost_class_init, }; +static void typhoon_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = typhoon_translate_iommu; +} + +static const TypeInfo typhoon_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_TYPHOON_IOMMU_MEMORY_REGION, + .class_init = typhoon_iommu_memory_region_class_init, +}; + static void typhoon_register_types(void) { type_register_static(&typhoon_pcihost_info); + type_register_static(&typhoon_iommu_memory_region_info); } type_init(typhoon_register_types) diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 32c06760ef..5d4833eeca 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -54,6 +54,8 @@ typedef struct dma_pagetable_entry { #define RC4030(obj) \ OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030) +#define TYPE_RC4030_IOMMU_MEMORY_REGION "rc4030-iommu-memory-region" + typedef struct rc4030State { SysBusDevice parent; @@ -516,10 +518,6 @@ static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps rc4030_dma_ops = { - .translate = rc4030_dma_translate, -}; - static void rc4030_reset(DeviceState *dev) { rc4030State *s = RC4030(dev); @@ -677,8 +675,9 @@ static void rc4030_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, "rc4030.jazzio", 0x00001000); - memory_region_init_iommu(&s->dma_mr, o, &rc4030_dma_ops, - "rc4030.dma", UINT32_MAX); + memory_region_init_iommu(&s->dma_mr, sizeof(s->dma_mr), + TYPE_RC4030_IOMMU_MEMORY_REGION, + o, "rc4030.dma", UINT32_MAX); address_space_init(&s->dma_as, MEMORY_REGION(&s->dma_mr), "rc4030-dma"); } @@ -710,9 +709,24 @@ static const TypeInfo rc4030_info = { .class_init = rc4030_class_init, }; +static void rc4030_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = rc4030_dma_translate; +} + +static const TypeInfo rc4030_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_RC4030_IOMMU_MEMORY_REGION, + .class_init = rc4030_iommu_memory_region_class_init, +}; + static void rc4030_register_types(void) { type_register_static(&rc4030_info); + type_register_static(&rc4030_iommu_memory_region_info); } type_init(rc4030_register_types) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 7a67c573af..334938a280 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1044,8 +1044,11 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; - memory_region_init_iommu(&iommu_as[devfn]->iommu, OBJECT(s), - &s->iommu_ops, "amd-iommu", UINT64_MAX); + memory_region_init_iommu(&iommu_as[devfn]->iommu, + sizeof(iommu_as[devfn]->iommu), + TYPE_AMD_IOMMU_MEMORY_REGION, + OBJECT(s), + "amd-iommu", UINT64_MAX); address_space_init(&iommu_as[devfn]->as, MEMORY_REGION(&iommu_as[devfn]->iommu), "amd-iommu"); @@ -1086,8 +1089,6 @@ static void amdvi_init(AMDVIState *s) { amdvi_iotlb_reset(s); - s->iommu_ops.translate = amdvi_translate; - s->iommu_ops.notify_flag_changed = amdvi_iommu_notify_flag_changed; s->devtab_len = 0; s->cmdbuf_len = 0; s->cmdbuf_head = 0; @@ -1228,10 +1229,25 @@ static const TypeInfo amdviPCI = { .instance_size = sizeof(AMDVIPCIState), }; +static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = amdvi_translate; + imrc->notify_flag_changed = amdvi_iommu_notify_flag_changed; +} + +static const TypeInfo amdvi_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_AMD_IOMMU_MEMORY_REGION, + .class_init = amdvi_iommu_memory_region_class_init, +}; + static void amdviPCI_register_types(void) { type_register_static(&amdviPCI); type_register_static(&amdvi); + type_register_static(&amdvi_iommu_memory_region_info); } type_init(amdviPCI_register_types); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 0d3dc6a9f2..d370ae3549 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -220,6 +220,8 @@ #define TYPE_AMD_IOMMU_PCI "AMDVI-PCI" +#define TYPE_AMD_IOMMU_MEMORY_REGION "amd-iommu-iommu-memory-region" + typedef struct AMDVIAddressSpace AMDVIAddressSpace; /* functions to steal PCI config space */ @@ -276,9 +278,6 @@ typedef struct AMDVIState { uint8_t romask[AMDVI_MMIO_SIZE]; /* MMIO read/only mask */ bool mmio_enabled; - /* IOMMU function */ - MemoryRegionIOMMUOps iommu_ops; - /* for each served device */ AMDVIAddressSpace **address_spaces[PCI_BUS_MAX]; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 11dba0e274..e398746b4b 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2718,8 +2718,9 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) * vtd_sys_alias and intel_iommu regions. IR region is always * enabled. */ - memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), - &s->iommu_ops, "intel_iommu_dmar", + memory_region_init_iommu(&vtd_dev_as->iommu, sizeof(vtd_dev_as->iommu), + TYPE_INTEL_IOMMU_MEMORY_REGION, OBJECT(s), + "intel_iommu_dmar", UINT64_MAX); memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s), "vtd_sys_alias", get_system_memory(), @@ -2857,9 +2858,6 @@ static void vtd_init(IntelIOMMUState *s) memset(s->w1cmask, 0, DMAR_REG_SIZE); memset(s->womask, 0, DMAR_REG_SIZE); - s->iommu_ops.translate = vtd_iommu_translate; - s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed; - s->iommu_ops.replay = vtd_iommu_replay; s->root = 0; s->root_extended = false; s->dmar_enabled = false; @@ -3074,9 +3072,26 @@ static const TypeInfo vtd_info = { .class_init = vtd_class_init, }; +static void vtd_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = vtd_iommu_translate; + imrc->notify_flag_changed = vtd_iommu_notify_flag_changed; + imrc->replay = vtd_iommu_replay; +} + +static const TypeInfo vtd_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_INTEL_IOMMU_MEMORY_REGION, + .class_init = vtd_iommu_memory_region_class_init, +}; + static void vtd_register_types(void) { type_register_static(&vtd_info); + type_register_static(&vtd_iommu_memory_region_info); } type_init(vtd_register_types) diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index 76a56ae29b..96e5d0b60d 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -133,6 +133,8 @@ typedef struct IOMMUState { #define APB_DEVICE(obj) \ OBJECT_CHECK(APBState, (obj), TYPE_APB) +#define TYPE_APB_IOMMU_MEMORY_REGION "pbm-iommu-memory-region" + typedef struct APBState { PCIHostState parent_obj; @@ -322,10 +324,6 @@ static IOMMUTLBEntry pbm_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, return ret; } -static MemoryRegionIOMMUOps pbm_iommu_ops = { - .translate = pbm_translate_iommu, -}; - static void iommu_config_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -697,7 +695,8 @@ PCIBus *pci_apb_init(hwaddr special_base, is = &d->iommu; memset(is, 0, sizeof(IOMMUState)); - memory_region_init_iommu(&is->iommu, OBJECT(dev), &pbm_iommu_ops, + memory_region_init_iommu(&is->iommu, sizeof(is->iommu), + TYPE_APB_IOMMU_MEMORY_REGION, OBJECT(dev), "iommu-apb", UINT64_MAX); address_space_init(&is->iommu_as, MEMORY_REGION(&is->iommu), "pbm-as"); pci_setup_iommu(phb->bus, pbm_pci_dma_iommu, is); @@ -860,11 +859,25 @@ static const TypeInfo pbm_pci_bridge_info = { .class_init = pbm_pci_bridge_class_init, }; +static void pbm_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = pbm_translate_iommu; +} + +static const TypeInfo pbm_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_APB_IOMMU_MEMORY_REGION, + .class_init = pbm_iommu_memory_region_class_init, +}; + static void pbm_register_types(void) { type_register_static(&pbm_host_info); type_register_static(&pbm_pci_host_info); type_register_static(&pbm_pci_bridge_info); + type_register_static(&pbm_iommu_memory_region_info); } type_init(pbm_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 6b6ced5710..d07b4b460d 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -248,12 +248,6 @@ static const VMStateDescription vmstate_spapr_tce_table = { } }; -static MemoryRegionIOMMUOps spapr_iommu_ops = { - .translate = spapr_tce_translate_iommu, - .get_min_page_size = spapr_tce_get_min_page_size, - .notify_flag_changed = spapr_tce_notify_flag_changed, -}; - static int spapr_tce_table_realize(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); @@ -266,7 +260,9 @@ static int spapr_tce_table_realize(DeviceState *dev) memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX); snprintf(tmp, sizeof(tmp), "tce-iommu-%x", tcet->liobn); - memory_region_init_iommu(&tcet->iommu, tcetobj, &spapr_iommu_ops, tmp, 0); + memory_region_init_iommu(&tcet->iommu, sizeof(tcet->iommu), + TYPE_SPAPR_IOMMU_MEMORY_REGION, + tcetobj, tmp, 0); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); @@ -639,9 +635,25 @@ static TypeInfo spapr_tce_table_info = { .class_init = spapr_tce_table_class_init, }; +static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = spapr_tce_translate_iommu; + imrc->get_min_page_size = spapr_tce_get_min_page_size; + imrc->notify_flag_changed = spapr_tce_notify_flag_changed; +} + +static const TypeInfo spapr_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SPAPR_IOMMU_MEMORY_REGION, + .class_init = spapr_iommu_memory_region_class_init, +}; + static void register_types(void) { type_register_static(&spapr_tce_table_info); + type_register_static(&spapr_iommu_memory_region_info); } type_init(register_types); diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index e4fc82cbe1..903b3cadc5 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -407,10 +407,6 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps s390_iommu_ops = { - .translate = s390_translate_iommu, -}; - static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus, int devfn) { @@ -522,8 +518,9 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { void s390_pci_iommu_enable(S390PCIIOMMU *iommu) { char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid); - memory_region_init_iommu(&iommu->iommu_mr, OBJECT(&iommu->mr), - &s390_iommu_ops, name, iommu->pal + 1); + memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr), + TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr), + name, iommu->pal + 1); iommu->enabled = true; memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr)); g_free(name); @@ -1058,12 +1055,26 @@ static TypeInfo s390_pci_iommu_info = { .instance_size = sizeof(S390PCIIOMMU), }; +static void s390_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = s390_translate_iommu; +} + +static const TypeInfo s390_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_S390_IOMMU_MEMORY_REGION, + .class_init = s390_iommu_memory_region_class_init, +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); type_register_static(&s390_pcibus_info); type_register_static(&s390_pci_device_info); type_register_static(&s390_pci_iommu_info); + type_register_static(&s390_iommu_memory_region_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 6a599ed353..67af2c12ff 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -24,6 +24,7 @@ #define TYPE_S390_PCI_BUS "s390-pcibus" #define TYPE_S390_PCI_DEVICE "zpci" #define TYPE_S390_PCI_IOMMU "s390-pci-iommu" +#define TYPE_S390_IOMMU_MEMORY_REGION "s390-iommu-memory-region" #define FH_MASK_ENABLE 0x80000000 #define FH_MASK_INSTANCE 0x7f000000 #define FH_MASK_SHM 0x00ff0000 diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index a53c29c487..b7beb8c36a 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -564,6 +564,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) hwaddr start, end; IOMMUTLBEntry entry; IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; cpu_synchronize_state(CPU(cpu)); @@ -623,8 +624,10 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) } iommu_mr = &iommu->iommu_mr; + imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + while (start < end) { - entry = iommu_mr->iommu_ops->translate(iommu_mr, start, IOMMU_NONE); + entry = imrc->translate(iommu_mr, start, IOMMU_NONE); if (!entry.translated_addr) { pbdev->state = ZPCI_FS_ERROR; diff --git a/include/exec/memory.h b/include/exec/memory.h index 92980abfad..b7966014fe 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -25,6 +25,7 @@ #include "qemu/notify.h" #include "qom/object.h" #include "qemu/rcu.h" +#include "hw/qdev-core.h" #define RAM_ADDR_INVALID (~(ram_addr_t)0) @@ -38,6 +39,12 @@ #define TYPE_IOMMU_MEMORY_REGION "qemu:iommu-memory-region" #define IOMMU_MEMORY_REGION(obj) \ OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_IOMMU_MEMORY_REGION) +#define IOMMU_MEMORY_REGION_CLASS(klass) \ + OBJECT_CLASS_CHECK(IOMMUMemoryRegionClass, (klass), \ + TYPE_IOMMU_MEMORY_REGION) +#define IOMMU_MEMORY_REGION_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IOMMUMemoryRegionClass, (obj), \ + TYPE_IOMMU_MEMORY_REGION) typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegionMmio MemoryRegionMmio; @@ -193,9 +200,10 @@ struct MemoryRegionOps { const MemoryRegionMmio old_mmio; }; -typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps; +typedef struct IOMMUMemoryRegionClass { + /* private */ + struct DeviceClass parent_class; -struct MemoryRegionIOMMUOps { /* * Return a TLB entry that contains a given address. Flag should * be the access permission of this translation operation. We can @@ -212,7 +220,7 @@ struct MemoryRegionIOMMUOps { IOMMUNotifierFlag new_flags); /* Set this up to provide customized IOMMU replay function */ void (*replay)(IOMMUMemoryRegion *iommu, IOMMUNotifier *notifier); -}; +} IOMMUMemoryRegionClass; typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; @@ -261,7 +269,6 @@ struct MemoryRegion { struct IOMMUMemoryRegion { MemoryRegion parent_obj; - const MemoryRegionIOMMUOps *iommu_ops; QLIST_HEAD(, IOMMUNotifier) iommu_notify; IOMMUNotifierFlag iommu_notify_flags; }; @@ -622,21 +629,24 @@ static inline void memory_region_init_reservation(MemoryRegion *mr, } /** - * memory_region_init_iommu: Initialize a memory region that translates - * addresses + * memory_region_init_iommu: Initialize a memory region of a custom type + * that translates addresses * * An IOMMU region translates addresses and forwards accesses to a target * memory region. * - * @iommu_mr: the #IOMMUMemoryRegion to be initialized + * @typename: QOM class name + * @_iommu_mr: the #IOMMUMemoryRegion to be initialized + * @instance_size: the IOMMUMemoryRegion subclass instance size * @owner: the object that tracks the region's reference count * @ops: a function that translates addresses into the @target region * @name: used for debugging; not visible to the user or ABI * @size: size of the region. */ -void memory_region_init_iommu(IOMMUMemoryRegion *iommu_mr, - struct Object *owner, - const MemoryRegionIOMMUOps *ops, +void memory_region_init_iommu(void *_iommu_mr, + size_t instance_size, + const char *mrtypename, + Object *owner, const char *name, uint64_t size); @@ -707,6 +717,21 @@ static inline IOMMUMemoryRegion *memory_region_get_iommu(MemoryRegion *mr) return NULL; } +/** + * memory_region_get_iommu_class_nocheck: returns iommu memory region class + * if an iommu or NULL if not + * + * Returns pointer to IOMMUMemoryRegioniClass if a memory region is an iommu, + * otherwise NULL. This is fast path avoinding QOM checking, use with caution. + * + * @mr: the memory region being queried + */ +static inline IOMMUMemoryRegionClass *memory_region_get_iommu_class_nocheck( + IOMMUMemoryRegion *iommu_mr) +{ + return (IOMMUMemoryRegionClass *) (((Object *)iommu_mr)->class); +} + #define memory_region_is_iommu(mr) (memory_region_get_iommu(mr) != NULL) /** diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 45fba4ff97..08d8a26d13 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -32,6 +32,8 @@ #define INTEL_IOMMU_DEVICE(obj) \ OBJECT_CHECK(IntelIOMMUState, (obj), TYPE_INTEL_IOMMU_DEVICE) +#define TYPE_INTEL_IOMMU_MEMORY_REGION "intel-iommu-iommu-memory-region" + /* DMAR Hardware Unit Definition address (IOMMU unit) */ #define Q35_HOST_BRIDGE_IOMMU_ADDR 0xfed90000ULL @@ -289,7 +291,6 @@ struct IntelIOMMUState { uint32_t context_cache_gen; /* Should be in [1,MAX] */ GHashTable *iotlb; /* IOTLB */ - MemoryRegionIOMMUOps iommu_ops; GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ /* list of registered notifiers */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index b353dfff2e..5f708eec23 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -582,6 +582,10 @@ typedef struct sPAPRTCETable sPAPRTCETable; #define SPAPR_TCE_TABLE(obj) \ OBJECT_CHECK(sPAPRTCETable, (obj), TYPE_SPAPR_TCE_TABLE) +#define TYPE_SPAPR_IOMMU_MEMORY_REGION "spapr-iommu-memory-region" +#define SPAPR_IOMMU_MEMORY_REGION(obj) \ + OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_SPAPR_IOMMU_MEMORY_REGION) + struct sPAPRTCETable { DeviceState parent; uint32_t liobn; diff --git a/memory.c b/memory.c index 45e10e2e3b..69f697c20e 100644 --- a/memory.c +++ b/memory.c @@ -1506,19 +1506,20 @@ void memory_region_init_rom_device(MemoryRegion *mr, mr->ram_block = qemu_ram_alloc(size, mr, errp); } -void memory_region_init_iommu(IOMMUMemoryRegion *iommu_mr, +void memory_region_init_iommu(void *_iommu_mr, + size_t instance_size, + const char *mrtypename, Object *owner, - const MemoryRegionIOMMUOps *ops, const char *name, uint64_t size) { + struct IOMMUMemoryRegion *iommu_mr; struct MemoryRegion *mr; - object_initialize(iommu_mr, sizeof(*iommu_mr), TYPE_IOMMU_MEMORY_REGION); - mr = MEMORY_REGION(iommu_mr); + object_initialize(_iommu_mr, instance_size, mrtypename); + mr = MEMORY_REGION(_iommu_mr); memory_region_do_init(mr, owner, name, size); iommu_mr = IOMMU_MEMORY_REGION(mr); - iommu_mr->iommu_ops = ops, mr->terminates = true; /* then re-forwards */ QLIST_INIT(&iommu_mr->iommu_notify); iommu_mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE; @@ -1620,16 +1621,16 @@ static void memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr) { IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE; IOMMUNotifier *iommu_notifier; + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { flags |= iommu_notifier->notifier_flags; } - if (flags != iommu_mr->iommu_notify_flags && - iommu_mr->iommu_ops->notify_flag_changed) { - iommu_mr->iommu_ops->notify_flag_changed(iommu_mr, - iommu_mr->iommu_notify_flags, - flags); + if (flags != iommu_mr->iommu_notify_flags && imrc->notify_flag_changed) { + imrc->notify_flag_changed(iommu_mr, + iommu_mr->iommu_notify_flags, + flags); } iommu_mr->iommu_notify_flags = flags; @@ -1655,8 +1656,10 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr) { - if (iommu_mr->iommu_ops && iommu_mr->iommu_ops->get_min_page_size) { - return iommu_mr->iommu_ops->get_min_page_size(iommu_mr); + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + + if (imrc->get_min_page_size) { + return imrc->get_min_page_size(iommu_mr); } return TARGET_PAGE_SIZE; } @@ -1664,19 +1667,20 @@ uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr) void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) { MemoryRegion *mr = MEMORY_REGION(iommu_mr); + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); hwaddr addr, granularity; IOMMUTLBEntry iotlb; /* If the IOMMU has its own replay callback, override */ - if (iommu_mr->iommu_ops->replay) { - iommu_mr->iommu_ops->replay(iommu_mr, n); + if (imrc->replay) { + imrc->replay(iommu_mr, n); return; } granularity = memory_region_iommu_get_min_page_size(iommu_mr); for (addr = 0; addr < memory_region_size(mr); addr += granularity) { - iotlb = iommu_mr->iommu_ops->translate(iommu_mr, addr, IOMMU_NONE); + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE); if (iotlb.perm != IOMMU_NONE) { n->notify(n, &iotlb); } @@ -2855,8 +2859,10 @@ static const TypeInfo memory_region_info = { static const TypeInfo iommu_memory_region_info = { .parent = TYPE_MEMORY_REGION, .name = TYPE_IOMMU_MEMORY_REGION, + .class_size = sizeof(IOMMUMemoryRegionClass), .instance_size = sizeof(IOMMUMemoryRegion), .instance_init = iommu_memory_region_initfn, + .abstract = true, }; static void memory_register_types(void) From 004a89fce9b5ec1b027bf4d104d83aa682db7b7a Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:41 -0500 Subject: [PATCH 31/55] nbd: Create struct for tracking export info The NBD Protocol is introducing some additional information about exports, such as minimum request size and alignment, as well as an advertised maximum request size. It will be easier to feed this information back to the block layer if we gather all the information into a struct, rather than adding yet more pointer parameters during negotiation. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-2-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- block/nbd-client.c | 18 ++++++++---------- block/nbd-client.h | 3 +-- block/nbd.c | 2 +- include/block/nbd.h | 15 +++++++++++---- nbd/client.c | 44 ++++++++++++++++++++++---------------------- qemu-nbd.c | 10 ++++------ 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/block/nbd-client.c b/block/nbd-client.c index 208f907095..aab1e320b2 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -242,7 +242,7 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, ssize_t ret; if (flags & BDRV_REQ_FUA) { - assert(client->nbdflags & NBD_FLAG_SEND_FUA); + assert(client->info.flags & NBD_FLAG_SEND_FUA); request.flags |= NBD_CMD_FLAG_FUA; } @@ -270,12 +270,12 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, }; NBDReply reply; - if (!(client->nbdflags & NBD_FLAG_SEND_WRITE_ZEROES)) { + if (!(client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { return -ENOTSUP; } if (flags & BDRV_REQ_FUA) { - assert(client->nbdflags & NBD_FLAG_SEND_FUA); + assert(client->info.flags & NBD_FLAG_SEND_FUA); request.flags |= NBD_CMD_FLAG_FUA; } if (!(flags & BDRV_REQ_MAY_UNMAP)) { @@ -299,7 +299,7 @@ int nbd_client_co_flush(BlockDriverState *bs) NBDReply reply; ssize_t ret; - if (!(client->nbdflags & NBD_FLAG_SEND_FLUSH)) { + if (!(client->info.flags & NBD_FLAG_SEND_FLUSH)) { return 0; } @@ -327,7 +327,7 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) NBDReply reply; ssize_t ret; - if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) { + if (!(client->info.flags & NBD_FLAG_SEND_TRIM)) { return 0; } @@ -385,19 +385,17 @@ int nbd_client_init(BlockDriverState *bs, qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, - &client->nbdflags, tlscreds, hostname, - &client->ioc, - &client->size, errp); + &client->ioc, &client->info, errp); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); return ret; } - if (client->nbdflags & NBD_FLAG_SEND_FUA) { + if (client->info.flags & NBD_FLAG_SEND_FUA) { bs->supported_write_flags = BDRV_REQ_FUA; bs->supported_zero_flags |= BDRV_REQ_FUA; } - if (client->nbdflags & NBD_FLAG_SEND_WRITE_ZEROES) { + if (client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) { bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; } diff --git a/block/nbd-client.h b/block/nbd-client.h index 49636bc621..df80771357 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -20,8 +20,7 @@ typedef struct NBDClientSession { QIOChannelSocket *sioc; /* The master data channel */ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ - uint16_t nbdflags; - off_t size; + NBDExportInfo info; CoMutex send_mutex; CoQueue free_sema; diff --git a/block/nbd.c b/block/nbd.c index d529305330..4a9048c280 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -492,7 +492,7 @@ static int64_t nbd_getlength(BlockDriverState *bs) { BDRVNBDState *s = bs->opaque; - return s->client.size; + return s->client.info.size; } static void nbd_detach_aio_context(BlockDriverState *bs) diff --git a/include/block/nbd.h b/include/block/nbd.h index 6d75d5a670..90923747d3 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -123,13 +123,20 @@ enum { * aren't overflowing some other buffer. */ #define NBD_MAX_NAME_SIZE 256 +/* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */ +struct NBDExportInfo { + uint64_t size; + uint16_t flags; +}; +typedef struct NBDExportInfo NBDExportInfo; + ssize_t nbd_rwv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length, bool do_read, Error **errp); -int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, - QIOChannel **outioc, - off_t *size, Error **errp); -int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, + QIOChannel **outioc, NBDExportInfo *info, + Error **errp); +int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp); ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request); ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp); diff --git a/nbd/client.c b/nbd/client.c index 9c52b9b885..12502739bb 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -425,13 +425,13 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, } -int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, - QIOChannel **outioc, - off_t *size, Error **errp) + QIOChannel **outioc, NBDExportInfo *info, + Error **errp) { char buf[256]; - uint64_t magic, s; + uint64_t magic; int rc; bool zeroes = true; @@ -532,17 +532,17 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, } /* Read the response */ - if (nbd_read(ioc, &s, sizeof(s), errp) < 0) { + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "Failed to read export length"); goto fail; } - *size = be64_to_cpu(s); + be64_to_cpus(&info->size); - if (nbd_read(ioc, flags, sizeof(*flags), errp) < 0) { + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { error_prepend(errp, "Failed to read export flags"); goto fail; } - be16_to_cpus(flags); + be16_to_cpus(&info->flags); } else if (magic == NBD_CLIENT_MAGIC) { uint32_t oldflags; @@ -555,11 +555,11 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, goto fail; } - if (nbd_read(ioc, &s, sizeof(s), errp) < 0) { + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "Failed to read export length"); goto fail; } - *size = be64_to_cpu(s); + be64_to_cpus(&info->size); if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { error_prepend(errp, "Failed to read export flags"); @@ -570,13 +570,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); goto fail; } - *flags = oldflags; + info->flags = oldflags; } else { error_setg(errp, "Bad magic received"); goto fail; } - trace_nbd_receive_negotiate_size_flags(*size, *flags); + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); if (zeroes && nbd_drop(ioc, 124, errp) < 0) { error_prepend(errp, "Failed to read reserved block"); goto fail; @@ -588,13 +588,13 @@ fail: } #ifdef __linux__ -int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, +int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp) { - unsigned long sectors = size / BDRV_SECTOR_SIZE; - if (size / BDRV_SECTOR_SIZE != sectors) { - error_setg(errp, "Export size %lld too large for 32-bit kernel", - (long long) size); + unsigned long sectors = info->size / BDRV_SECTOR_SIZE; + if (info->size / BDRV_SECTOR_SIZE != sectors) { + error_setg(errp, "Export size %" PRIu64 " too large for 32-bit kernel", + info->size); return -E2BIG; } @@ -615,8 +615,8 @@ int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, } trace_nbd_init_set_size(sectors); - if (size % BDRV_SECTOR_SIZE) { - trace_nbd_init_trailing_bytes(size % BDRV_SECTOR_SIZE); + if (info->size % BDRV_SECTOR_SIZE) { + trace_nbd_init_trailing_bytes(info->size % BDRV_SECTOR_SIZE); } if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { @@ -625,9 +625,9 @@ int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, return -serrno; } - if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) flags) < 0) { + if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) info->flags) < 0) { if (errno == ENOTTY) { - int read_only = (flags & NBD_FLAG_READ_ONLY) != 0; + int read_only = (info->flags & NBD_FLAG_READ_ONLY) != 0; trace_nbd_init_set_readonly(); if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { @@ -685,7 +685,7 @@ int nbd_disconnect(int fd) } #else -int nbd_init(int fd, QIOChannelSocket *ioc, uint16_t flags, off_t size, +int nbd_init(int fd, QIOChannelSocket *ioc, NBDExportInfo *info, Error **errp) { error_setg(errp, "nbd_init is only supported on Linux"); diff --git a/qemu-nbd.c b/qemu-nbd.c index 4dd3fd4732..c8bd47fe77 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -255,8 +255,7 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { char *device = arg; - off_t size; - uint16_t nbdflags; + NBDExportInfo info; QIOChannelSocket *sioc; int fd; int ret; @@ -271,9 +270,8 @@ static void *nbd_client_thread(void *arg) goto out; } - ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags, - NULL, NULL, NULL, - &size, &local_error); + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, + NULL, NULL, NULL, &info, &local_error); if (ret < 0) { if (local_error) { error_report_err(local_error); @@ -288,7 +286,7 @@ static void *nbd_client_thread(void *arg) goto out_socket; } - ret = nbd_init(fd, sioc, nbdflags, size, &local_error); + ret = nbd_init(fd, sioc, &info, &local_error); if (ret < 0) { error_report_err(local_error); goto out_fd; From 37ec36f622d46b7d13421533efe686af2326771e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:42 -0500 Subject: [PATCH 32/55] nbd: Don't bother tracing an NBD_OPT_ABORT response failure We really don't care if our spec-compliant reply to NBD_OPT_ABORT was received, so shave off some lines of code by not even tracing it. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-3-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/server.c | 15 ++++----------- nbd/trace-events | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 9b0c588146..e15385b777 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -376,7 +376,6 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) { uint32_t flags; bool fixedNewstyle = false; - Error *local_err = NULL; /* Client sends: [ 0 .. 3] client flags @@ -479,7 +478,9 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) if (ret < 0) { return ret; } - /* Let the client keep trying, unless they asked to quit */ + /* Let the client keep trying, unless they asked to + * quit. In this mode, we've already sent an error, so + * we can't ack the abort. */ if (option == NBD_OPT_ABORT) { return 1; } @@ -498,15 +499,7 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) /* NBD spec says we must try to reply before * disconnecting, but that we must also tolerate * guests that don't wait for our reply. */ - nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, option, - &local_err); - - if (local_err != NULL) { - const char *error = error_get_pretty(local_err); - trace_nbd_opt_abort_reply_failed(error); - error_free(local_err); - } - + nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, option, NULL); return 1; case NBD_OPT_EXPORT_NAME: diff --git a/nbd/trace-events b/nbd/trace-events index 4b233b8510..c6f898e0d4 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -40,7 +40,6 @@ nbd_negotiate_options_newstyle(void) "Client supports fixed newstyle handshake" nbd_negotiate_options_no_zeroes(void) "Client supports no zeroes at handshake end" nbd_negotiate_options_check_magic(uint64_t magic) "Checking opts magic 0x%" PRIx64 nbd_negotiate_options_check_option(uint32_t option) "Checking option 0x%" PRIx32 -nbd_opt_abort_reply_failed(const char *error) "Reply to NBD_OPT_ABORT request failed: %s" nbd_negotiate_begin(void) "Beginning negotiation" nbd_negotiate_old_style(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags %x" nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags %x" From 3736cc5be31f0399999e37d8b28ca9a3ed0b4ccb Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:43 -0500 Subject: [PATCH 33/55] nbd: Expose and debug more NBD constants The NBD protocol has several constants defined in various extensions that we are about to implement. Expose them to the code, along with an easy way to map various constants to strings during diagnostic messages. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-4-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- include/block/nbd.h | 35 +++++++++++++---- nbd/client.c | 52 ++++++++++++++++--------- nbd/common.c | 92 +++++++++++++++++++++++++++++++++++++++++++++ nbd/nbd-internal.h | 10 ++--- nbd/server.c | 18 +++++---- nbd/trace-events | 12 +++--- 6 files changed, 174 insertions(+), 45 deletions(-) diff --git a/include/block/nbd.h b/include/block/nbd.h index 90923747d3..4a22eca98b 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device @@ -83,18 +83,37 @@ typedef struct NBDReply NBDReply; #define NBD_FLAG_C_FIXED_NEWSTYLE (1 << 0) /* Fixed newstyle protocol. */ #define NBD_FLAG_C_NO_ZEROES (1 << 1) /* End handshake without zeroes. */ -/* Reply types. */ +/* Option requests. */ +#define NBD_OPT_EXPORT_NAME (1) +#define NBD_OPT_ABORT (2) +#define NBD_OPT_LIST (3) +/* #define NBD_OPT_PEEK_EXPORT (4) not in use */ +#define NBD_OPT_STARTTLS (5) +#define NBD_OPT_INFO (6) +#define NBD_OPT_GO (7) +#define NBD_OPT_STRUCTURED_REPLY (8) + +/* Option reply types. */ #define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) #define NBD_REP_ACK (1) /* Data sending finished. */ #define NBD_REP_SERVER (2) /* Export description. */ +#define NBD_REP_INFO (3) /* NBD_OPT_INFO/GO. */ -#define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ -#define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ -#define NBD_REP_ERR_INVALID NBD_REP_ERR(3) /* Invalid length */ -#define NBD_REP_ERR_PLATFORM NBD_REP_ERR(4) /* Not compiled in */ -#define NBD_REP_ERR_TLS_REQD NBD_REP_ERR(5) /* TLS required */ -#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ +#define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ +#define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ +#define NBD_REP_ERR_INVALID NBD_REP_ERR(3) /* Invalid length */ +#define NBD_REP_ERR_PLATFORM NBD_REP_ERR(4) /* Not compiled in */ +#define NBD_REP_ERR_TLS_REQD NBD_REP_ERR(5) /* TLS required */ +#define NBD_REP_ERR_UNKNOWN NBD_REP_ERR(6) /* Export unknown */ +#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ +#define NBD_REP_ERR_BLOCK_SIZE_REQD NBD_REP_ERR(8) /* Need INFO_BLOCK_SIZE */ + +/* Info types, used during NBD_REP_INFO */ +#define NBD_INFO_EXPORT 0 +#define NBD_INFO_NAME 1 +#define NBD_INFO_DESCRIPTION 2 +#define NBD_INFO_BLOCK_SIZE 3 /* Request flags, sent from client to server during transmission phase */ #define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ diff --git a/nbd/client.c b/nbd/client.c index 12502739bb..a443e51aa0 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device Client Side @@ -104,7 +104,7 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, if (len == -1) { req.length = len = strlen(data); } - trace_nbd_send_option_request(opt, len); + trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); stq_be_p(&req.magic, NBD_OPTS_MAGIC); stl_be_p(&req.option, opt); @@ -154,7 +154,9 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, be32_to_cpus(&reply->type); be32_to_cpus(&reply->length); - trace_nbd_receive_option_reply(reply->option, reply->type, reply->length); + trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), + reply->type, nbd_rep_lookup(reply->type), + reply->length); if (reply->magic != NBD_REP_MAGIC) { error_setg(errp, "Unexpected option reply magic"); @@ -188,12 +190,16 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, if (reply->length) { if (reply->length > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "server's error message is too long"); + error_setg(errp, "server error 0x%" PRIx32 + " (%s) message is too long", + reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg = g_malloc(reply->length + 1); if (nbd_read(ioc, msg, reply->length, errp) < 0) { - error_prepend(errp, "failed to read option error message"); + error_prepend(errp, "failed to read option error 0x%" PRIx32 + " (%s) message", + reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg[reply->length] = '\0'; @@ -201,38 +207,48 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, switch (reply->type) { case NBD_REP_ERR_UNSUP: - trace_nbd_reply_err_unsup(reply->option); + trace_nbd_reply_err_unsup(reply->option, nbd_opt_lookup(reply->option)); result = 0; goto cleanup; case NBD_REP_ERR_POLICY: - error_setg(errp, "Denied by server for option %" PRIx32, - reply->option); + error_setg(errp, "Denied by server for option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_INVALID: - error_setg(errp, "Invalid data length for option %" PRIx32, - reply->option); + error_setg(errp, "Invalid data length for option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_PLATFORM: - error_setg(errp, "Server lacks support for option %" PRIx32, - reply->option); + error_setg(errp, "Server lacks support for option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_TLS_REQD: - error_setg(errp, "TLS negotiation required before option %" PRIx32, - reply->option); + error_setg(errp, "TLS negotiation required before option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); + break; + + case NBD_REP_ERR_UNKNOWN: + error_setg(errp, "Requested export not available for option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_SHUTDOWN: - error_setg(errp, "Server shutting down before option %" PRIx32, - reply->option); + error_setg(errp, "Server shutting down before option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); + break; + + case NBD_REP_ERR_BLOCK_SIZE_REQD: + error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; default: - error_setg(errp, "Unknown error code when asking for option %" PRIx32, - reply->option); + error_setg(errp, "Unknown error code when asking for option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; } diff --git a/nbd/common.c b/nbd/common.c index 4dab41e2c0..a2f28f2eec 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -101,3 +101,95 @@ void nbd_tls_handshake(QIOTask *task, data->complete = true; g_main_loop_quit(data->loop); } + + +const char *nbd_opt_lookup(uint32_t opt) +{ + switch (opt) { + case NBD_OPT_EXPORT_NAME: + return "export name"; + case NBD_OPT_ABORT: + return "abort"; + case NBD_OPT_LIST: + return "list"; + case NBD_OPT_STARTTLS: + return "starttls"; + case NBD_OPT_INFO: + return "info"; + case NBD_OPT_GO: + return "go"; + case NBD_OPT_STRUCTURED_REPLY: + return "structured reply"; + default: + return ""; + } +} + + +const char *nbd_rep_lookup(uint32_t rep) +{ + switch (rep) { + case NBD_REP_ACK: + return "ack"; + case NBD_REP_SERVER: + return "server"; + case NBD_REP_INFO: + return "info"; + case NBD_REP_ERR_UNSUP: + return "unsupported"; + case NBD_REP_ERR_POLICY: + return "denied by policy"; + case NBD_REP_ERR_INVALID: + return "invalid"; + case NBD_REP_ERR_PLATFORM: + return "platform lacks support"; + case NBD_REP_ERR_TLS_REQD: + return "TLS required"; + case NBD_REP_ERR_UNKNOWN: + return "export unknown"; + case NBD_REP_ERR_SHUTDOWN: + return "server shutting down"; + case NBD_REP_ERR_BLOCK_SIZE_REQD: + return "block size required"; + default: + return ""; + } +} + + +const char *nbd_info_lookup(uint16_t info) +{ + switch (info) { + case NBD_INFO_EXPORT: + return "export"; + case NBD_INFO_NAME: + return "name"; + case NBD_INFO_DESCRIPTION: + return "description"; + case NBD_INFO_BLOCK_SIZE: + return "block size"; + default: + return ""; + } +} + + +const char *nbd_cmd_lookup(uint16_t cmd) +{ + switch (cmd) { + case NBD_CMD_READ: + return "read"; + case NBD_CMD_WRITE: + return "write"; + case NBD_CMD_DISC: + return "discard"; + case NBD_CMD_FLUSH: + return "flush"; + case NBD_CMD_TRIM: + return "trim"; + case NBD_CMD_WRITE_ZEROES: + return "write zeroes"; + default: + return ""; + } +} diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index cf6ecbf358..bf95601ff5 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -57,12 +57,6 @@ #define NBD_SET_TIMEOUT _IO(0xab, 9) #define NBD_SET_FLAGS _IO(0xab, 10) -#define NBD_OPT_EXPORT_NAME (1) -#define NBD_OPT_ABORT (2) -#define NBD_OPT_LIST (3) -#define NBD_OPT_PEEK_EXPORT (4) -#define NBD_OPT_STARTTLS (5) - /* NBD errors are based on errno numbers, so there is a 1:1 mapping, * but only a limited set of errno values is specified in the protocol. * Everything else is squashed to EINVAL. @@ -133,6 +127,10 @@ struct NBDTLSHandshakeData { void nbd_tls_handshake(QIOTask *task, void *opaque); +const char *nbd_opt_lookup(uint32_t opt); +const char *nbd_rep_lookup(uint32_t rep); +const char *nbd_info_lookup(uint16_t info); +const char *nbd_cmd_lookup(uint16_t info); int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); diff --git a/nbd/server.c b/nbd/server.c index e15385b777..27a0aabe3f 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device Server Side @@ -139,7 +139,8 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type, { uint64_t magic; - trace_nbd_negotiate_send_rep_len(opt, type, len); + trace_nbd_negotiate_send_rep_len(opt, nbd_opt_lookup(opt), + type, nbd_rep_lookup(type), len); magic = cpu_to_be64(NBD_REP_MAGIC); if (nbd_write(ioc, &magic, sizeof(magic), errp) < 0) { @@ -441,7 +442,8 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) } length = be32_to_cpu(length); - trace_nbd_negotiate_options_check_option(option); + trace_nbd_negotiate_options_check_option(option, + nbd_opt_lookup(option)); if (client->tlscreds && client->ioc == (QIOChannel *)client->sioc) { QIOChannel *tioc; @@ -532,8 +534,8 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) NBD_REP_ERR_UNSUP, option, errp, "Unsupported option 0x%" - PRIx32, - option); + PRIx32 " (%s)", option, + nbd_opt_lookup(option)); if (ret < 0) { return ret; } @@ -549,7 +551,8 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) return nbd_negotiate_handle_export_name(client, length, errp); default: - error_setg(errp, "Unsupported option 0x%" PRIx32, option); + error_setg(errp, "Unsupported option 0x%" PRIx32 " (%s)", + option, nbd_opt_lookup(option)); return -EINVAL; } } @@ -1033,7 +1036,8 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return -EIO; } - trace_nbd_co_receive_request_decode_type(request->handle, request->type); + trace_nbd_co_receive_request_decode_type(request->handle, request->type, + nbd_cmd_lookup(request->type)); if (request->type != NBD_CMD_WRITE) { /* No payload, we are ready to read the next request. */ diff --git a/nbd/trace-events b/nbd/trace-events index c6f898e0d4..5e2f961257 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -1,8 +1,8 @@ # nbd/client.c nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL" -nbd_send_option_request(uint32_t opt, uint32_t len) "Sending option request %" PRIu32", len %" PRIu32 -nbd_receive_option_reply(uint32_t option, uint32_t type, uint32_t length) "Received option reply %" PRIx32", type %" PRIx32", len %" PRIu32 -nbd_reply_err_unsup(uint32_t option) "server doesn't understand request %" PRIx32 ", attempting fallback" +nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending option request %" PRIu32" (%s), len %" PRIu32 +nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIx32" (%s), type %" PRIx32" (%s), len %" PRIu32 +nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIx32 " (%s), attempting fallback" nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_starttls_request(void) "Requesting TLS from server" @@ -28,7 +28,7 @@ nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, u nbd_receive_reply(uint32_t magic, int32_t error, uint64_t handle) "Got reply: { magic = 0x%" PRIx32 ", .error = % " PRId32 ", handle = %" PRIu64" }" # nbd/server.c -nbd_negotiate_send_rep_len(uint32_t opt, uint32_t type, uint32_t len) "Reply opt=%" PRIx32 " type=%" PRIx32 " len=%" PRIu32 +nbd_negotiate_send_rep_len(uint32_t opt, const char *optname, uint32_t type, const char *typename, uint32_t len) "Reply opt=%" PRIx32 " (%s), type=%" PRIx32 " (%s), len=%" PRIu32 nbd_negotiate_send_rep_err(const char *msg) "sending error message \"%s\"" nbd_negotiate_send_rep_list(const char *name, const char *desc) "Advertising export name '%s' description '%s'" nbd_negotiate_handle_export_name(void) "Checking length" @@ -39,7 +39,7 @@ nbd_negotiate_options_flags(void) "Checking client flags" nbd_negotiate_options_newstyle(void) "Client supports fixed newstyle handshake" nbd_negotiate_options_no_zeroes(void) "Client supports no zeroes at handshake end" nbd_negotiate_options_check_magic(uint64_t magic) "Checking opts magic 0x%" PRIx64 -nbd_negotiate_options_check_option(uint32_t option) "Checking option 0x%" PRIx32 +nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking option 0x%" PRIx32 " (%s)" nbd_negotiate_begin(void) "Beginning negotiation" nbd_negotiate_old_style(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags %x" nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags %x" @@ -49,7 +49,7 @@ nbd_send_reply(int32_t error, uint64_t handle) "Sending response to client: { .e nbd_blk_aio_attached(const char *name, void *ctx) "Export %s: Attaching clients to AIO context %p\n" nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients from AIO context %p\n" nbd_co_send_reply(uint64_t handle, uint32_t error, int len) "Send reply: handle = %" PRIu64 ", error = %" PRIu32 ", len = %d" -nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 +nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 nbd_co_receive_request_cmd_write(uint32_t len) "Reading %" PRIu32 " byte(s)" nbd_trip(void) "Reading request" From 621c4f4eabff26493494909fc91aeac7e8205438 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:44 -0500 Subject: [PATCH 34/55] nbd: Simplify trace of client flags in negotiation Simplify the tracing of client flags in the server, and return -EINVAL instead of -EIO if we successfully read but don't like those flags. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-5-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/server.c | 6 ++---- nbd/trace-events | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 27a0aabe3f..8e363d3a83 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -396,21 +396,19 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) error_prepend(errp, "read failed: "); return -EIO; } - trace_nbd_negotiate_options_flags(); be32_to_cpus(&flags); + trace_nbd_negotiate_options_flags(flags); if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { - trace_nbd_negotiate_options_newstyle(); fixedNewstyle = true; flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; } if (flags & NBD_FLAG_C_NO_ZEROES) { - trace_nbd_negotiate_options_no_zeroes(); client->no_zeroes = true; flags &= ~NBD_FLAG_C_NO_ZEROES; } if (flags != 0) { error_setg(errp, "Unknown client flags 0x%" PRIx32 " received", flags); - return -EIO; + return -EINVAL; } while (1) { diff --git a/nbd/trace-events b/nbd/trace-events index 5e2f961257..8db05008a0 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -35,9 +35,7 @@ nbd_negotiate_handle_export_name(void) "Checking length" nbd_negotiate_handle_export_name_request(const char *name) "Client requested export '%s'" nbd_negotiate_handle_starttls(void) "Setting up TLS" nbd_negotiate_handle_starttls_handshake(void) "Starting TLS handshake" -nbd_negotiate_options_flags(void) "Checking client flags" -nbd_negotiate_options_newstyle(void) "Client supports fixed newstyle handshake" -nbd_negotiate_options_no_zeroes(void) "Client supports no zeroes at handshake end" +nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PRIx32 nbd_negotiate_options_check_magic(uint64_t magic) "Checking opts magic 0x%" PRIx64 nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking option 0x%" PRIx32 " (%s)" nbd_negotiate_begin(void) "Beginning negotiation" From 23e099c34c6c5d774af37be8f7b57a9e563c58f4 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:45 -0500 Subject: [PATCH 35/55] nbd: Refactor reply to NBD_OPT_EXPORT_NAME Reply directly in nbd_negotiate_handle_export_name(), rather than waiting until nbd_negotiate_options() completes. This will make it easier to implement NBD_OPT_GO. Pass additional parameters around, rather than stashing things inside NBDClient. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-6-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/server.c | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 8e363d3a83..841986d5f6 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -84,7 +84,6 @@ struct NBDClient { int refcount; void (*close_fn)(NBDClient *client, bool negotiated); - bool no_zeroes; NBDExport *exp; QCryptoTLSCreds *tlscreds; char *tlsaclname; @@ -277,9 +276,13 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length, } static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length, + uint16_t myflags, bool no_zeroes, Error **errp) { char name[NBD_MAX_NAME_SIZE + 1]; + char buf[8 + 4 + 124] = ""; + size_t len; + int ret; /* Client sends: [20 .. xx] export name (length bytes) @@ -303,6 +306,17 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length, return -EINVAL; } + trace_nbd_negotiate_new_style_size_flags(client->exp->size, + client->exp->nbdflags | myflags); + stq_be_p(buf, client->exp->size); + stw_be_p(buf + 8, client->exp->nbdflags | myflags); + len = no_zeroes ? 10 : sizeof(buf); + ret = nbd_write(client->ioc, buf, len, errp); + if (ret < 0) { + error_prepend(errp, "write failed: "); + return ret; + } + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); @@ -373,14 +387,17 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, * errp is not set */ -static int nbd_negotiate_options(NBDClient *client, Error **errp) +static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + Error **errp) { uint32_t flags; bool fixedNewstyle = false; + bool no_zeroes = false; /* Client sends: [ 0 .. 3] client flags + Then we loop until NBD_OPT_EXPORT_NAME: [ 0 .. 7] NBD_OPTS_MAGIC [ 8 .. 11] NBD option [12 .. 15] Data length @@ -403,7 +420,7 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; } if (flags & NBD_FLAG_C_NO_ZEROES) { - client->no_zeroes = true; + no_zeroes = true; flags &= ~NBD_FLAG_C_NO_ZEROES; } if (flags != 0) { @@ -503,7 +520,9 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) return 1; case NBD_OPT_EXPORT_NAME: - return nbd_negotiate_handle_export_name(client, length, errp); + return nbd_negotiate_handle_export_name(client, length, + myflags, no_zeroes, + errp); case NBD_OPT_STARTTLS: if (nbd_drop(client->ioc, length, errp) < 0) { @@ -546,7 +565,9 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) */ switch (option) { case NBD_OPT_EXPORT_NAME: - return nbd_negotiate_handle_export_name(client, length, errp); + return nbd_negotiate_handle_export_name(client, length, + myflags, no_zeroes, + errp); default: error_setg(errp, "Unsupported option 0x%" PRIx32 " (%s)", @@ -572,7 +593,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_WRITE_ZEROES); bool oldStyle; - size_t len; /* Old style negotiation header without options [ 0 .. 7] passwd ("NBDMAGIC") @@ -586,10 +606,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (NBD_OPTS_MAGIC) [16 .. 17] server flags (0) - ....options sent.... - [18 .. 25] size - [26 .. 27] export flags - [28 .. 151] reserved (0, omit if no_zeroes) + ....options sent, ending in NBD_OPT_EXPORT_NAME.... */ qio_channel_set_blocking(client->ioc, false, NULL); @@ -618,24 +635,13 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) error_prepend(errp, "write failed: "); return -EINVAL; } - ret = nbd_negotiate_options(client, errp); + ret = nbd_negotiate_options(client, myflags, errp); if (ret != 0) { if (ret < 0) { error_prepend(errp, "option negotiation failed: "); } return ret; } - - trace_nbd_negotiate_new_style_size_flags( - client->exp->size, client->exp->nbdflags | myflags); - stq_be_p(buf + 18, client->exp->size); - stw_be_p(buf + 26, client->exp->nbdflags | myflags); - len = client->no_zeroes ? 10 : sizeof(buf) - 18; - ret = nbd_write(client->ioc, buf + 18, len, errp); - if (ret < 0) { - error_prepend(errp, "write failed: "); - return ret; - } } trace_nbd_negotiate_success(); From f37708f6b8e0bef0dd85c6aad7fc2062071f8227 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:46 -0500 Subject: [PATCH 36/55] nbd: Implement NBD_OPT_GO on server NBD_OPT_EXPORT_NAME is lousy: per the NBD protocol, any failure requires us to close the connection rather than report an error. Therefore, upstream NBD recently added NBD_OPT_GO as the improved version of the option that does what we want [1], along with NBD_OPT_INFO that returns the same information but does not transition to transmission phase. [1] https://github.com/NetworkBlockDevice/nbd/blob/extension-info/doc/proto.md This is a first cut at the information types, and only passes the same information already available through NBD_OPT_LIST and NBD_OPT_EXPORT_NAME; items like NBD_INFO_BLOCK_SIZE (and thus any use of NBD_REP_ERR_BLOCK_SIZE_REQD) are intentionally left for later patches. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-7-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/server.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++- nbd/trace-events | 3 + 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 841986d5f6..e84d0125bb 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -141,6 +141,7 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type, trace_nbd_negotiate_send_rep_len(opt, nbd_opt_lookup(opt), type, nbd_rep_lookup(type), len); + assert(len < NBD_MAX_BUFFER_SIZE); magic = cpu_to_be64(NBD_REP_MAGIC); if (nbd_write(ioc, &magic, sizeof(magic), errp) < 0) { error_prepend(errp, "write failed (rep magic): "); @@ -275,6 +276,8 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length, return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST, errp); } +/* Send a reply to NBD_OPT_EXPORT_NAME. + * Return -errno on error, 0 on success. */ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length, uint16_t myflags, bool no_zeroes, Error **errp) @@ -323,6 +326,162 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length, return 0; } +/* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes. + * The buffer does NOT include the info type prefix. + * Return -errno on error, 0 if ready to send more. */ +static int nbd_negotiate_send_info(NBDClient *client, uint32_t opt, + uint16_t info, uint32_t length, void *buf, + Error **errp) +{ + int rc; + + trace_nbd_negotiate_send_info(info, nbd_info_lookup(info), length); + rc = nbd_negotiate_send_rep_len(client->ioc, NBD_REP_INFO, opt, + sizeof(info) + length, errp); + if (rc < 0) { + return rc; + } + cpu_to_be16s(&info); + if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) { + return -EIO; + } + if (nbd_write(client->ioc, buf, length, errp) < 0) { + return -EIO; + } + return 0; +} + +/* Handle NBD_OPT_INFO and NBD_OPT_GO. + * Return -errno on error, 0 if ready for next option, and 1 to move + * into transmission phase. */ +static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, + uint32_t opt, uint16_t myflags, + Error **errp) +{ + int rc; + char name[NBD_MAX_NAME_SIZE + 1]; + NBDExport *exp; + uint16_t requests; + uint16_t request; + uint32_t namelen; + bool sendname = false; + char buf[sizeof(uint64_t) + sizeof(uint16_t)]; + const char *msg; + + /* Client sends: + 4 bytes: L, name length (can be 0) + L bytes: export name + 2 bytes: N, number of requests (can be 0) + N * 2 bytes: N requests + */ + if (length < sizeof(namelen) + sizeof(requests)) { + msg = "overall request too short"; + goto invalid; + } + if (nbd_read(client->ioc, &namelen, sizeof(namelen), errp) < 0) { + return -EIO; + } + be32_to_cpus(&namelen); + length -= sizeof(namelen); + if (namelen > length - sizeof(requests) || (length - namelen) % 2) { + msg = "name length is incorrect"; + goto invalid; + } + if (nbd_read(client->ioc, name, namelen, errp) < 0) { + return -EIO; + } + name[namelen] = '\0'; + length -= namelen; + trace_nbd_negotiate_handle_export_name_request(name); + + if (nbd_read(client->ioc, &requests, sizeof(requests), errp) < 0) { + return -EIO; + } + be16_to_cpus(&requests); + length -= sizeof(requests); + trace_nbd_negotiate_handle_info_requests(requests); + if (requests != length / sizeof(request)) { + msg = "incorrect number of requests for overall length"; + goto invalid; + } + while (requests--) { + if (nbd_read(client->ioc, &request, sizeof(request), errp) < 0) { + return -EIO; + } + be16_to_cpus(&request); + length -= sizeof(request); + trace_nbd_negotiate_handle_info_request(request, + nbd_info_lookup(request)); + /* For now, we only care about NBD_INFO_NAME; everything else + * is either a request we don't know or something we send + * regardless of request. */ + if (request == NBD_INFO_NAME) { + sendname = true; + } + } + + exp = nbd_export_find(name); + if (!exp) { + return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_UNKNOWN, + opt, errp, "export '%s' not present", + name); + } + + /* Don't bother sending NBD_INFO_NAME unless client requested it */ + if (sendname) { + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_NAME, length, name, + errp); + if (rc < 0) { + return rc; + } + } + + /* Send NBD_INFO_DESCRIPTION only if available, regardless of + * client request */ + if (exp->description) { + size_t len = strlen(exp->description); + + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_DESCRIPTION, + len, exp->description, errp); + if (rc < 0) { + return rc; + } + } + + /* Send NBD_INFO_EXPORT always */ + trace_nbd_negotiate_new_style_size_flags(exp->size, + exp->nbdflags | myflags); + stq_be_p(buf, exp->size); + stw_be_p(buf + 8, exp->nbdflags | myflags); + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_EXPORT, + sizeof(buf), buf, errp); + if (rc < 0) { + return rc; + } + + /* Final reply */ + rc = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp); + if (rc < 0) { + return rc; + } + + if (opt == NBD_OPT_GO) { + client->exp = exp; + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); + rc = 1; + } + return rc; + + invalid: + if (nbd_drop(client->ioc, length, errp) < 0) { + return -EIO; + } + return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID, opt, + errp, "%s", msg); +} + + /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the * new channel for all further (now-encrypted) communication. */ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, @@ -380,7 +539,8 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, } /* nbd_negotiate_options - * Process all NBD_OPT_* client option commands. + * Process all NBD_OPT_* client option commands, during fixed newstyle + * negotiation. * Return: * -errno on error, errp is set * 0 on successful negotiation, errp is not set @@ -397,7 +557,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, /* Client sends: [ 0 .. 3] client flags - Then we loop until NBD_OPT_EXPORT_NAME: + Then we loop until NBD_OPT_EXPORT_NAME or NBD_OPT_GO: [ 0 .. 7] NBD_OPTS_MAGIC [ 8 .. 11] NBD option [12 .. 15] Data length @@ -524,6 +684,19 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, myflags, no_zeroes, errp); + case NBD_OPT_INFO: + case NBD_OPT_GO: + ret = nbd_negotiate_handle_info(client, length, option, + myflags, errp); + if (ret == 1) { + assert(option == NBD_OPT_GO); + return 0; + } + if (ret) { + return ret; + } + break; + case NBD_OPT_STARTTLS: if (nbd_drop(client->ioc, length, errp) < 0) { return -EIO; @@ -606,7 +779,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (NBD_OPTS_MAGIC) [16 .. 17] server flags (0) - ....options sent, ending in NBD_OPT_EXPORT_NAME.... + ....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO.... */ qio_channel_set_blocking(client->ioc, false, NULL); diff --git a/nbd/trace-events b/nbd/trace-events index 8db05008a0..5230c61ab1 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -33,6 +33,9 @@ nbd_negotiate_send_rep_err(const char *msg) "sending error message \"%s\"" nbd_negotiate_send_rep_list(const char *name, const char *desc) "Advertising export name '%s' description '%s'" nbd_negotiate_handle_export_name(void) "Checking length" nbd_negotiate_handle_export_name_request(const char *name) "Client requested export '%s'" +nbd_negotiate_send_info(int info, const char *name, uint32_t length) "Sending NBD_REP_INFO type %d (%s) with remaining length %" PRIu32 +nbd_negotiate_handle_info_requests(int requests) "Client requested %d items of info" +nbd_negotiate_handle_info_request(int request, const char *name) "Client requested info %d (%s)" nbd_negotiate_handle_starttls(void) "Setting up TLS" nbd_negotiate_handle_starttls_handshake(void) "Starting TLS handshake" nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PRIx32 From 8ecaeae82241b6e317d19e5558e52f9e2f979f74 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:47 -0500 Subject: [PATCH 37/55] nbd: Implement NBD_OPT_GO on client NBD_OPT_EXPORT_NAME is lousy: per the NBD protocol, any failure requires the server to close the connection rather than report an error to us. Therefore, upstream NBD recently added NBD_OPT_GO as the improved version of the option that does what we want [1]: it reports sane errors on failures, and on success provides at least as much info as NBD_OPT_EXPORT_NAME. [1] https://github.com/NetworkBlockDevice/nbd/blob/extension-info/doc/proto.md This is a first cut at use of the information types. Note that we do not need to use NBD_OPT_INFO, and that use of NBD_OPT_GO means we no longer have to use NBD_OPT_LIST to learn whether a server requires TLS (this requires servers that gracefully handle unknown NBD_OPT, many servers prior to qemu 2.5 were buggy, but I have patched qemu, upstream nbd, and nbdkit in the meantime, in part because of interoperability testing with this patch). We still fall back to NBD_OPT_LIST when NBD_OPT_GO is not supported on the server, as it is still one last chance for a nicer error message. Later patches will use further info, like NBD_INFO_BLOCK_SIZE. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-8-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/client.c | 126 ++++++++++++++++++++++++++++++++++++++++++++- nbd/nbd-internal.h | 3 ++ nbd/trace-events | 3 ++ 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/nbd/client.c b/nbd/client.c index a443e51aa0..1e98ca9613 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -350,6 +350,114 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, } +/* Returns -1 if NBD_OPT_GO proves the export @wantname cannot be + * used, 0 if NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and + * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to + * go (with @info populated). */ +static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + NBDExportInfo *info, Error **errp) +{ + nbd_opt_reply reply; + uint32_t len = strlen(wantname); + uint16_t type; + int error; + char *buf; + + /* The protocol requires that the server send NBD_INFO_EXPORT with + * a non-zero flags (at least NBD_FLAG_HAS_FLAGS must be set); so + * flags still 0 is a witness of a broken server. */ + info->flags = 0; + + trace_nbd_opt_go_start(wantname); + buf = g_malloc(4 + len + 2 + 1); + stl_be_p(buf, len); + memcpy(buf + 4, wantname, len); + /* No requests, live with whatever server sends */ + stw_be_p(buf + 4 + len, 0); + if (nbd_send_option_request(ioc, NBD_OPT_GO, len + 6, buf, errp) < 0) { + return -1; + } + + while (1) { + if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) { + return -1; + } + error = nbd_handle_reply_err(ioc, &reply, errp); + if (error <= 0) { + return error; + } + len = reply.length; + + if (reply.type == NBD_REP_ACK) { + /* Server is done sending info and moved into transmission + phase, but make sure it sent flags */ + if (len) { + error_setg(errp, "server sent invalid NBD_REP_ACK"); + nbd_send_opt_abort(ioc); + return -1; + } + if (!info->flags) { + error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); + nbd_send_opt_abort(ioc); + return -1; + } + trace_nbd_opt_go_success(); + return 1; + } + if (reply.type != NBD_REP_INFO) { + error_setg(errp, "unexpected reply type %" PRIx32 ", expected %x", + reply.type, NBD_REP_INFO); + nbd_send_opt_abort(ioc); + return -1; + } + if (len < sizeof(type)) { + error_setg(errp, "NBD_REP_INFO length %" PRIu32 " is too short", + len); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &type, sizeof(type), errp) < 0) { + error_prepend(errp, "failed to read info type"); + nbd_send_opt_abort(ioc); + return -1; + } + len -= sizeof(type); + be16_to_cpus(&type); + switch (type) { + case NBD_INFO_EXPORT: + if (len != sizeof(info->size) + sizeof(info->flags)) { + error_setg(errp, "remaining export info len %" PRIu32 + " is unexpected size", len); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { + error_prepend(errp, "failed to read info size"); + nbd_send_opt_abort(ioc); + return -1; + } + be64_to_cpus(&info->size); + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { + error_prepend(errp, "failed to read info flags"); + nbd_send_opt_abort(ioc); + return -1; + } + be16_to_cpus(&info->flags); + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); + break; + + default: + trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type)); + if (nbd_drop(ioc, len, errp) < 0) { + error_prepend(errp, "Failed to read info payload"); + nbd_send_opt_abort(ioc); + return -1; + } + break; + } + } +} + /* Return -1 on failure, 0 if wantname is an available export. */ static int nbd_receive_query_exports(QIOChannel *ioc, const char *wantname, @@ -531,11 +639,25 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, name = ""; } if (fixedNewStyle) { + int result; + + /* Try NBD_OPT_GO first - if it works, we are done (it + * also gives us a good message if the server requires + * TLS). If it is not available, fall back to + * NBD_OPT_LIST for nicer error messages about a missing + * export, then use NBD_OPT_EXPORT_NAME. */ + result = nbd_opt_go(ioc, name, info, errp); + if (result < 0) { + goto fail; + } + if (result > 0) { + return 0; + } /* Check our desired export is present in the * server export list. Since NBD_OPT_EXPORT_NAME * cannot return an error message, running this - * query gives us good error reporting if the - * server required TLS + * query gives us better error reporting if the + * export name is not available. */ if (nbd_receive_query_exports(ioc, name, errp) < 0) { goto fail; diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index bf95601ff5..4065bc68ac 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -37,8 +37,11 @@ * https://github.com/yoe/nbd/blob/master/doc/proto.md */ +/* Size of all NBD_OPT_*, without payload */ #define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4) +/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */ #define NBD_REPLY_SIZE (4 + 4 + 8) + #define NBD_REQUEST_MAGIC 0x25609513 #define NBD_REPLY_MAGIC 0x67446698 #define NBD_OPTS_MAGIC 0x49484156454F5054LL diff --git a/nbd/trace-events b/nbd/trace-events index 5230c61ab1..bd09fab375 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -3,6 +3,9 @@ nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL" nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending option request %" PRIu32" (%s), len %" PRIu32 nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIx32" (%s), type %" PRIx32" (%s), len %" PRIu32 nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIx32 " (%s), attempting fallback" +nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" +nbd_opt_go_success(void) "Export is good to go" +nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_starttls_request(void) "Requesting TLS from server" From 0c1d50bda7ae03146b4bf7bc3a12389897ea059c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:48 -0500 Subject: [PATCH 38/55] nbd: Implement NBD_INFO_BLOCK_SIZE on server The upstream NBD Protocol has defined a new extension to allow the server to advertise block sizes to the client, as well as a way for the client to inform the server that it intends to obey block sizes. Thanks to a recent fix (commit df7b97ff), our real minimum transfer size is always 1 (the block layer takes care of read-modify-write on our behalf), but we're still more efficient if we advertise 512 when the client supports it, as follows: - OPT_INFO, but no NBD_INFO_BLOCK_SIZE: advertise 512, then fail with NBD_REP_ERR_BLOCK_SIZE_REQD; client is free to try something else since we don't disconnect - OPT_INFO with NBD_INFO_BLOCK_SIZE: advertise 512 - OPT_GO, but no NBD_INFO_BLOCK_SIZE: advertise 1 - OPT_GO with NBD_INFO_BLOCK_SIZE: advertise 512 We can also advertise the optimum block size (presumably the cluster size, when exporting a qcow2 file), and our absolute maximum transfer size of 32M, to help newer clients avoid EINVAL failures or abrupt disconnects on oversize requests. We do not reject clients for using the older NBD_OPT_EXPORT_NAME; we are no worse off for those clients than we used to be. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-9-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- nbd/server.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- nbd/trace-events | 1 + 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index e84d0125bb..49ed57455c 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -365,6 +365,8 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, uint16_t request; uint32_t namelen; bool sendname = false; + bool blocksize = false; + uint32_t sizes[3]; char buf[sizeof(uint64_t) + sizeof(uint16_t)]; const char *msg; @@ -412,11 +414,16 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, length -= sizeof(request); trace_nbd_negotiate_handle_info_request(request, nbd_info_lookup(request)); - /* For now, we only care about NBD_INFO_NAME; everything else - * is either a request we don't know or something we send - * regardless of request. */ - if (request == NBD_INFO_NAME) { + /* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE; + * everything else is either a request we don't know or + * something we send regardless of request */ + switch (request) { + case NBD_INFO_NAME: sendname = true; + break; + case NBD_INFO_BLOCK_SIZE: + blocksize = true; + break; } } @@ -448,6 +455,27 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, } } + /* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size + * according to whether the client requested it, and according to + * whether this is OPT_INFO or OPT_GO. */ + /* minimum - 1 for back-compat, or 512 if client is new enough. + * TODO: consult blk_bs(blk)->bl.request_alignment? */ + sizes[0] = (opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1; + /* preferred - Hard-code to 4096 for now. + * TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */ + sizes[1] = 4096; + /* maximum - At most 32M, but smaller as appropriate. */ + sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); + trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); + cpu_to_be32s(&sizes[0]); + cpu_to_be32s(&sizes[1]); + cpu_to_be32s(&sizes[2]); + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_BLOCK_SIZE, + sizeof(sizes), sizes, errp); + if (rc < 0) { + return rc; + } + /* Send NBD_INFO_EXPORT always */ trace_nbd_negotiate_new_style_size_flags(exp->size, exp->nbdflags | myflags); @@ -459,6 +487,18 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, return rc; } + /* If the client is just asking for NBD_OPT_INFO, but forgot to + * request block sizes, return an error. + * TODO: consult blk_bs(blk)->request_align, and only error if it + * is not 1? */ + if (opt == NBD_OPT_INFO && !blocksize) { + return nbd_negotiate_send_rep_err(client->ioc, + NBD_REP_ERR_BLOCK_SIZE_REQD, opt, + errp, + "request NBD_INFO_BLOCK_SIZE to " + "use this export"); + } + /* Final reply */ rc = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp); if (rc < 0) { diff --git a/nbd/trace-events b/nbd/trace-events index bd09fab375..be3dce773e 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -39,6 +39,7 @@ nbd_negotiate_handle_export_name_request(const char *name) "Client requested exp nbd_negotiate_send_info(int info, const char *name, uint32_t length) "Sending NBD_REP_INFO type %d (%s) with remaining length %" PRIu32 nbd_negotiate_handle_info_requests(int requests) "Client requested %d items of info" nbd_negotiate_handle_info_request(int request, const char *name) "Client requested info %d (%s)" +nbd_negotiate_handle_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "advertising minimum 0x%" PRIx32 ", preferred 0x%" PRIx32 ", maximum 0x%" PRIx32 nbd_negotiate_handle_starttls(void) "Setting up TLS" nbd_negotiate_handle_starttls_handshake(void) "Starting TLS handshake" nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PRIx32 From 081dd1fe36f0ccc04130d1edd136c787c5f8cc50 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 7 Jul 2017 15:30:49 -0500 Subject: [PATCH 39/55] nbd: Implement NBD_INFO_BLOCK_SIZE on client The upstream NBD Protocol has defined a new extension to allow the server to advertise block sizes to the client, as well as a way for the client to inform the server whether it intends to obey block sizes. When using the block layer as the client, we will obey block sizes; but when used as 'qemu-nbd -c' to hand off to the kernel nbd module as the client, we are still waiting for the kernel to implement a way for us to learn if it will honor block sizes (perhaps by an addition to sysfs, rather than an ioctl), as well as any way to tell the kernel what additional block sizes to obey (NBD_SET_BLKSIZE appears to be accurate for the minimum size, but preferred and maximum sizes would probably be new ioctl()s), so until then, we need to make our request for block sizes conditional. When using ioctl(NBD_SET_BLKSIZE) to hand off to the kernel, use the minimum block size as the sector size if it is larger than 512, which also has the nice effect of cooperating with (non-qemu) servers that don't do read-modify-write when exposing a block device with 4k sectors; it might also allow us to visit a file larger than 2T on a 32-bit kernel. Signed-off-by: Eric Blake Message-Id: <20170707203049.534-10-eblake@redhat.com> Signed-off-by: Paolo Bonzini --- block/nbd-client.c | 4 +++ block/nbd.c | 14 ++++++-- include/block/nbd.h | 6 ++++ nbd/client.c | 81 ++++++++++++++++++++++++++++++++++++++------- nbd/trace-events | 1 + qemu-nbd.c | 2 +- 6 files changed, 92 insertions(+), 16 deletions(-) diff --git a/block/nbd-client.c b/block/nbd-client.c index aab1e320b2..25dd28406b 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -384,6 +384,7 @@ int nbd_client_init(BlockDriverState *bs, logout("session init %s\n", export); qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); + client->info.request_sizes = true; ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, tlscreds, hostname, &client->ioc, &client->info, errp); @@ -398,6 +399,9 @@ int nbd_client_init(BlockDriverState *bs, if (client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) { bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; } + if (client->info.min_block > bs->bl.request_alignment) { + bs->bl.request_alignment = client->info.min_block; + } qemu_co_mutex_init(&client->send_mutex); qemu_co_queue_init(&client->free_sema); diff --git a/block/nbd.c b/block/nbd.c index 4a9048c280..a50d24b50a 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -472,9 +472,17 @@ static int nbd_co_flush(BlockDriverState *bs) static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) { - bs->bl.max_pdiscard = NBD_MAX_BUFFER_SIZE; - bs->bl.max_pwrite_zeroes = NBD_MAX_BUFFER_SIZE; - bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE; + NBDClientSession *s = nbd_get_client_session(bs); + uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); + + bs->bl.max_pdiscard = max; + bs->bl.max_pwrite_zeroes = max; + bs->bl.max_transfer = max; + + if (s->info.opt_block && + s->info.opt_block > bs->bl.opt_transfer) { + bs->bl.opt_transfer = s->info.opt_block; + } } static void nbd_close(BlockDriverState *bs) diff --git a/include/block/nbd.h b/include/block/nbd.h index 4a22eca98b..9c3d0a5868 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -144,8 +144,14 @@ enum { /* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */ struct NBDExportInfo { + /* Set by client before nbd_receive_negotiate() */ + bool request_sizes; + /* Set by server results during nbd_receive_negotiate() */ uint64_t size; uint16_t flags; + uint32_t min_block; + uint32_t opt_block; + uint32_t max_block; }; typedef struct NBDExportInfo NBDExportInfo; diff --git a/nbd/client.c b/nbd/client.c index 1e98ca9613..c3ee9f36b1 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -369,12 +369,17 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, info->flags = 0; trace_nbd_opt_go_start(wantname); - buf = g_malloc(4 + len + 2 + 1); + buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); stl_be_p(buf, len); memcpy(buf + 4, wantname, len); - /* No requests, live with whatever server sends */ - stw_be_p(buf + 4 + len, 0); - if (nbd_send_option_request(ioc, NBD_OPT_GO, len + 6, buf, errp) < 0) { + /* At most one request, everything else up to server */ + stw_be_p(buf + 4 + len, info->request_sizes); + if (info->request_sizes) { + stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); + } + if (nbd_send_option_request(ioc, NBD_OPT_GO, + 4 + len + 2 + 2 * info->request_sizes, buf, + errp) < 0) { return -1; } @@ -405,8 +410,9 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, return 1; } if (reply.type != NBD_REP_INFO) { - error_setg(errp, "unexpected reply type %" PRIx32 ", expected %x", - reply.type, NBD_REP_INFO); + error_setg(errp, "unexpected reply type %" PRIx32 + " (%s), expected %x", + reply.type, nbd_rep_lookup(reply.type), NBD_REP_INFO); nbd_send_opt_abort(ioc); return -1; } @@ -446,6 +452,51 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, trace_nbd_receive_negotiate_size_flags(info->size, info->flags); break; + case NBD_INFO_BLOCK_SIZE: + if (len != sizeof(info->min_block) * 3) { + error_setg(errp, "remaining export info len %" PRIu32 + " is unexpected size", len); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->min_block, sizeof(info->min_block), + errp) < 0) { + error_prepend(errp, "failed to read info minimum block size"); + nbd_send_opt_abort(ioc); + return -1; + } + be32_to_cpus(&info->min_block); + if (!is_power_of_2(info->min_block)) { + error_setg(errp, "server minimum block size %" PRId32 + "is not a power of two", info->min_block); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->opt_block, sizeof(info->opt_block), + errp) < 0) { + error_prepend(errp, "failed to read info preferred block size"); + nbd_send_opt_abort(ioc); + return -1; + } + be32_to_cpus(&info->opt_block); + if (!is_power_of_2(info->opt_block) || + info->opt_block < info->min_block) { + error_setg(errp, "server preferred block size %" PRId32 + "is not valid", info->opt_block); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->max_block, sizeof(info->max_block), + errp) < 0) { + error_prepend(errp, "failed to read info maximum block size"); + nbd_send_opt_abort(ioc); + return -1; + } + be32_to_cpus(&info->max_block); + trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block, + info->max_block); + break; + default: trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type)); if (nbd_drop(ioc, len, errp) < 0) { @@ -729,8 +780,14 @@ fail: int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp) { - unsigned long sectors = info->size / BDRV_SECTOR_SIZE; - if (info->size / BDRV_SECTOR_SIZE != sectors) { + unsigned long sector_size = MAX(BDRV_SECTOR_SIZE, info->min_block); + unsigned long sectors = info->size / sector_size; + + /* FIXME: Once the kernel module is patched to honor block sizes, + * and to advertise that fact to user space, we should update the + * hand-off to the kernel to use any block sizes we learned. */ + assert(!info->request_sizes); + if (info->size / sector_size != sectors) { error_setg(errp, "Export size %" PRIu64 " too large for 32-bit kernel", info->size); return -E2BIG; @@ -744,17 +801,17 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, return -serrno; } - trace_nbd_init_set_block_size(BDRV_SECTOR_SIZE); + trace_nbd_init_set_block_size(sector_size); - if (ioctl(fd, NBD_SET_BLKSIZE, (unsigned long)BDRV_SECTOR_SIZE) < 0) { + if (ioctl(fd, NBD_SET_BLKSIZE, sector_size) < 0) { int serrno = errno; error_setg(errp, "Failed setting NBD block size"); return -serrno; } trace_nbd_init_set_size(sectors); - if (info->size % BDRV_SECTOR_SIZE) { - trace_nbd_init_trailing_bytes(info->size % BDRV_SECTOR_SIZE); + if (info->size % sector_size) { + trace_nbd_init_trailing_bytes(info->size % sector_size); } if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { diff --git a/nbd/trace-events b/nbd/trace-events index be3dce773e..f5024d85a1 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -6,6 +6,7 @@ nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understan nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" nbd_opt_go_success(void) "Export is good to go" nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" +nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_starttls_request(void) "Requesting TLS from server" diff --git a/qemu-nbd.c b/qemu-nbd.c index c8bd47fe77..78d05bea2d 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -255,7 +255,7 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { char *device = arg; - NBDExportInfo info; + NBDExportInfo info = { .request_sizes = false, }; QIOChannelSocket *sioc; int fd; int ret; From f940488feaca394ccc68e0595b4c3a9017f16e45 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sun, 9 Jul 2017 03:49:53 -0400 Subject: [PATCH 40/55] vl: fix breakage of -tb-size Commit e7b161d573 ("vl: add tcg_enabled() for tcg related code") adds a check to exit the program when !tcg_enabled() while parsing the -tb-size flag. It turns out that when the -tb-size flag is evaluated, tcg_enabled() can only return 0, since it is set (or not) much later by configure_accelerator(). Fix it by unconditionally exiting if the flag is passed to a QEMU binary built with !CONFIG_TCG. Signed-off-by: Emilio G. Cota Signed-off-by: Paolo Bonzini --- vl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vl.c b/vl.c index f7560de622..85cdbbff15 100644 --- a/vl.c +++ b/vl.c @@ -3933,10 +3933,10 @@ int main(int argc, char **argv, char **envp) configure_rtc(opts); break; case QEMU_OPTION_tb_size: - if (!tcg_enabled()) { - error_report("TCG is disabled"); - exit(1); - } +#ifndef CONFIG_TCG + error_report("TCG is disabled"); + exit(1); +#endif if (qemu_strtoul(optarg, NULL, 0, &tcg_tb_size) < 0) { error_report("Invalid argument to -tb-size"); exit(1); From d40d3da00c10f0169a26985ecb65033bff536f2c Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Sun, 9 Jul 2017 03:49:54 -0400 Subject: [PATCH 41/55] translate-all: remove redundant !tcg_enabled check in dump_exec_info This check is redundant because it is already performed by the only caller of dump_exec_info -- the caller was updated by b7da97eef ("monitor: Check whether TCG is enabled before running the "info jit" code"). Checking twice wouldn't necessarily be too bad, but here the check also returns with tb_lock held. So we can either do the check before tb_lock is acquired, or just get rid of it. Given that it is redundant, I am going for the latter option. Signed-off-by: Emilio G. Cota Signed-off-by: Paolo Bonzini --- accel/tcg/translate-all.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 0caf80db75..4e1831cbb9 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1851,11 +1851,6 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) tb_lock(); - if (!tcg_enabled()) { - cpu_fprintf(f, "TCG not enabled\n"); - return; - } - target_code_size = 0; max_target_code_size = 0; cross_page = 0; From 8f5d58ef2c92d7b82d9a6eeefd7c8854a183ba4a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 14 Jul 2017 10:14:50 +0800 Subject: [PATCH 42/55] qom: enforce readonly nature of link's check callback link's check callback is supposed to verify/permit setting it, however currently nothing restricts it from misusing it and modifying target object from within. Make sure that readonly semantics are checked by compiler to prevent callback's misuse. Signed-off-by: Igor Mammedov Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-2-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 3 ++- hw/display/xlnx_dp.c | 2 +- hw/ipmi/ipmi.c | 2 +- hw/mem/pc-dimm.c | 2 +- hw/misc/ivshmem.c | 2 +- hw/virtio/virtio-crypto.c | 2 +- include/hw/qdev-properties.h | 3 ++- include/qom/object.h | 6 +++--- qom/object.c | 8 ++++---- 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index f11d57831b..113ce7df46 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -25,7 +25,8 @@ void qdev_prop_set_after_realize(DeviceState *dev, const char *name, } } -void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name, +void qdev_prop_allow_set_link_before_realize(const Object *obj, + const char *name, Object *val, Error **errp) { DeviceState *dev = DEVICE(obj); diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index a77d7db22f..561f828e7a 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -515,7 +515,7 @@ static void xlnx_dp_aux_set_command(XlnxDPState *s, uint32_t value) s->core_registers[DP_INTERRUPT_SIGNAL_STATE] |= 0x04; } -static void xlnx_dp_set_dpdma(Object *obj, const char *name, Object *val, +static void xlnx_dp_set_dpdma(const Object *obj, const char *name, Object *val, Error **errp) { XlnxDPState *s = XLNX_DP(obj); diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index afafe1400f..b27babd504 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -90,7 +90,7 @@ static TypeInfo ipmi_interface_type_info = { .class_init = ipmi_interface_class_init, }; -static void isa_ipmi_bmc_check(Object *obj, const char *name, +static void isa_ipmi_bmc_check(const Object *obj, const char *name, Object *val, Error **errp) { IPMIBmc *bmc = IPMI_BMC(val); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index b72258e28f..5e23495473 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -367,7 +367,7 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &value, errp); } -static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, +static void pc_dimm_check_memdev_is_busy(const Object *obj, const char *name, Object *val, Error **errp) { Error *local_err = NULL; diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 7f678d0197..731f3c1d4f 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1009,7 +1009,7 @@ static const TypeInfo ivshmem_common_info = { .class_init = ivshmem_common_class_init, }; -static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, +static void ivshmem_check_memdev_is_busy(const Object *obj, const char *name, Object *val, Error **errp) { if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 0353eb6d5d..2590893016 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -889,7 +889,7 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) } static void -virtio_crypto_check_cryptodev_is_used(Object *obj, const char *name, +virtio_crypto_check_cryptodev_is_used(const Object *obj, const char *name, Object *val, Error **errp) { if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(val))) { diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 0604c337e0..9edded29d2 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -272,7 +272,8 @@ void qdev_prop_set_after_realize(DeviceState *dev, const char *name, * This function should be used as the check() argument to * object_property_add_link(). */ -void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name, +void qdev_prop_allow_set_link_before_realize(const Object *obj, + const char *name, Object *val, Error **errp); #endif diff --git a/include/qom/object.h b/include/qom/object.h index abaeb8cf4e..1b828994fa 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -764,7 +764,7 @@ ObjectClass *object_get_class(Object *obj); * * Returns: The QOM typename of @obj. */ -const char *object_get_typename(Object *obj); +const char *object_get_typename(const Object *obj); /** * type_register_static: @@ -1319,7 +1319,7 @@ typedef enum { * callback function. It allows the link property to be set and never returns * an error. */ -void object_property_allow_set_link(Object *, const char *, +void object_property_allow_set_link(const Object *, const char *, Object *, Error **); /** @@ -1352,7 +1352,7 @@ void object_property_allow_set_link(Object *, const char *, */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, - void (*check)(Object *obj, const char *name, + void (*check)(const Object *obj, const char *name, Object *val, Error **errp), ObjectPropertyLinkFlags flags, Error **errp); diff --git a/qom/object.c b/qom/object.c index 5f6fdfa6e6..dfdbd50f04 100644 --- a/qom/object.c +++ b/qom/object.c @@ -740,7 +740,7 @@ out: return ret; } -const char *object_get_typename(Object *obj) +const char *object_get_typename(const Object *obj) { return obj->class->type->name; } @@ -1428,7 +1428,7 @@ out: g_free(type); } -void object_property_allow_set_link(Object *obj, const char *name, +void object_property_allow_set_link(const Object *obj, const char *name, Object *val, Error **errp) { /* Allow the link to be set, always */ @@ -1436,7 +1436,7 @@ void object_property_allow_set_link(Object *obj, const char *name, typedef struct { Object **child; - void (*check)(Object *, const char *, Object *, Error **); + void (*check)(const Object *, const char *, Object *, Error **); ObjectPropertyLinkFlags flags; } LinkProperty; @@ -1552,7 +1552,7 @@ static void object_release_link_property(Object *obj, const char *name, void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, - void (*check)(Object *, const char *, + void (*check)(const Object *, const char *, Object *, Error **), ObjectPropertyLinkFlags flags, Error **errp) From faabdbb792889bf011a593578d1de51e14616bb7 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:51 +0800 Subject: [PATCH 43/55] qdev: Introduce PropertyInfo.create This allows property implementation to provide a specialized property creation method. Update conditions guarding property types accordingly. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-3-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/qdev.c | 31 +++++++++++++++++++------------ include/hw/qdev-core.h | 1 + qmp.c | 2 +- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 849952a8d4..ec63fe0354 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -744,6 +744,10 @@ static void qdev_property_add_legacy(DeviceState *dev, Property *prop, return; } + if (prop->info->create) { + return; + } + name = g_strdup_printf("legacy-%s", prop->name); object_property_add(OBJECT(dev), name, "str", prop->info->print ? qdev_get_legacy_property : prop->info->get, @@ -770,20 +774,23 @@ void qdev_property_add_static(DeviceState *dev, Property *prop, Error *local_err = NULL; Object *obj = OBJECT(dev); - /* - * TODO qdev_prop_ptr does not have getters or setters. It must - * go now that it can be replaced with links. The test should be - * removed along with it: all static properties are read/write. - */ - if (!prop->info->get && !prop->info->set) { - return; + if (prop->info->create) { + prop->info->create(obj, prop, &local_err); + } else { + /* + * TODO qdev_prop_ptr does not have getters or setters. It must + * go now that it can be replaced with links. The test should be + * removed along with it: all static properties are read/write. + */ + if (!prop->info->get && !prop->info->set) { + return; + } + object_property_add(obj, prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, + prop, &local_err); } - object_property_add(obj, prop->name, prop->info->name, - prop->info->get, prop->info->set, - prop->info->release, - prop, &local_err); - if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 9d7c1c0e9b..33518eec12 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -241,6 +241,7 @@ struct PropertyInfo { const char * const *enum_table; int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len); void (*set_default_value)(Object *obj, const Property *prop); + void (*create)(Object *obj, Property *prop, Error **errp); ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; ObjectPropertyRelease *release; diff --git a/qmp.c b/qmp.c index 84a4f29563..e6b68fe798 100644 --- a/qmp.c +++ b/qmp.c @@ -480,7 +480,7 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass, * for removal. This conditional should be removed along with * it. */ - if (!prop->info->set) { + if (!prop->info->set && !prop->info->create) { return NULL; /* no way to set it, don't show */ } From 5b4ff3c661374b2df247e12b58667e4643b30af1 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:52 +0800 Subject: [PATCH 44/55] qdev: Introduce DEFINE_PROP_LINK This property can be used to replace the object_property_add_link in device code, to add a link to other objects, which is a common pattern. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-4-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties.c | 18 ++++++++++++++++++ include/hw/qdev-core.h | 1 + include/hw/qdev-properties.h | 9 +++++++++ 3 files changed, 28 insertions(+) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 113ce7df46..5429c63e23 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1214,3 +1214,21 @@ PropertyInfo qdev_prop_size = { .set = set_size, .set_default_value = set_default_value_uint, }; + +/* --- object link property --- */ + +static void create_link_property(Object *obj, Property *prop, Error **errp) +{ + Object **child = qdev_get_prop_ptr(DEVICE(obj), prop); + + object_property_add_link(obj, prop->name, prop->link_type, + child, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + errp); +} + +PropertyInfo qdev_prop_link = { + .name = "link", + .create = create_link_property, +}; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 33518eec12..08d1d2c6f9 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -233,6 +233,7 @@ struct Property { int arrayoffset; PropertyInfo *arrayinfo; int arrayfieldsize; + const char *link_type; }; struct PropertyInfo { diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 9edded29d2..876ba2e283 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -30,6 +30,7 @@ extern PropertyInfo qdev_prop_pci_devfn; extern PropertyInfo qdev_prop_blocksize; extern PropertyInfo qdev_prop_pci_host_devaddr; extern PropertyInfo qdev_prop_arraylen; +extern PropertyInfo qdev_prop_link; #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ .name = (_name), \ @@ -117,6 +118,14 @@ extern PropertyInfo qdev_prop_arraylen; .arrayoffset = offsetof(_state, _arrayfield), \ } +#define DEFINE_PROP_LINK(_name, _state, _field, _type, _ptr_type) { \ + .name = (_name), \ + .info = &(qdev_prop_link), \ + .offset = offsetof(_state, _field) \ + + type_check(_ptr_type, typeof_field(_state, _field)), \ + .link_type = _type, \ + } + #define DEFINE_PROP_UINT8(_n, _s, _f, _d) \ DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_uint8, uint8_t) #define DEFINE_PROP_UINT16(_n, _s, _f, _d) \ From 75ab905383ea92d1c44cdd07e0f9fd509d51c9cc Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:53 +0800 Subject: [PATCH 45/55] qmp: Use ObjectProperty.type if present The dynamic value is more informative in the case of link property, otherwise it is the same. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-5-famz@redhat.com> Signed-off-by: Paolo Bonzini --- qmp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qmp.c b/qmp.c index e6b68fe798..2cd40c3080 100644 --- a/qmp.c +++ b/qmp.c @@ -486,7 +486,8 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass, info = g_malloc0(sizeof(*info)); info->name = g_strdup(prop->name); - info->type = g_strdup(prop->info->name); + info->type = default_type ? g_strdup(default_type) + : g_strdup(prop->info->name); info->has_description = !!prop->info->description; info->description = g_strdup(prop->info->description); return info; From 1b6b7d109ea53157525ad787802e45773fb7becb Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:54 +0800 Subject: [PATCH 46/55] qdev: Add const qualifier to PropertyInfo definitions The remaining non-const ones are in e1000e which modifies description at runtime. They can be addressed separatedly. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-6-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/qdev-properties-system.c | 8 ++--- hw/core/qdev-properties.c | 44 +++++++++++++-------------- hw/s390x/css.c | 4 +-- hw/s390x/s390-pci-bus.c | 2 +- include/hw/qdev-core.h | 4 +-- include/hw/qdev-properties.h | 52 ++++++++++++++++---------------- include/hw/s390x/css.h | 4 +-- target/i386/cpu.c | 2 +- target/ppc/translate_init.c | 2 +- 9 files changed, 61 insertions(+), 61 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 3bef41914d..ec10da7424 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -159,7 +159,7 @@ static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, set_pointer(obj, v, opaque, parse_drive, name, errp); } -PropertyInfo qdev_prop_drive = { +const PropertyInfo qdev_prop_drive = { .name = "str", .description = "Node name or ID of a block device to use as a backend", .get = get_drive, @@ -228,7 +228,7 @@ static void release_chr(Object *obj, const char *name, void *opaque) qemu_chr_fe_deinit(be, false); } -PropertyInfo qdev_prop_chr = { +const PropertyInfo qdev_prop_chr = { .name = "str", .description = "ID of a chardev to use as a backend", .get = get_chr, @@ -313,7 +313,7 @@ out: g_free(str); } -PropertyInfo qdev_prop_netdev = { +const PropertyInfo qdev_prop_netdev = { .name = "str", .description = "ID of a netdev to use as a backend", .get = get_netdev, @@ -393,7 +393,7 @@ static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, *ptr = hubport; } -PropertyInfo qdev_prop_vlan = { +const PropertyInfo qdev_prop_vlan = { .name = "int32", .description = "Integer VLAN id to connect to", .print = print_vlan, diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 5429c63e23..f22bd71ef4 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -132,7 +132,7 @@ static void set_default_value_bool(Object *obj, const Property *prop) object_property_set_bool(obj, prop->defval.u, prop->name, &error_abort); } -PropertyInfo qdev_prop_bit = { +const PropertyInfo qdev_prop_bit = { .name = "bool", .description = "on/off", .get = prop_get_bit, @@ -191,7 +191,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, const char *name, bit64_prop_set(dev, prop, value); } -PropertyInfo qdev_prop_bit64 = { +const PropertyInfo qdev_prop_bit64 = { .name = "bool", .description = "on/off", .get = prop_get_bit64, @@ -226,7 +226,7 @@ static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_bool(v, name, ptr, errp); } -PropertyInfo qdev_prop_bool = { +const PropertyInfo qdev_prop_bool = { .name = "bool", .get = get_bool, .set = set_bool, @@ -270,7 +270,7 @@ static void set_default_value_uint(Object *obj, const Property *prop) object_property_set_uint(obj, prop->defval.u, prop->name, &error_abort); } -PropertyInfo qdev_prop_uint8 = { +const PropertyInfo qdev_prop_uint8 = { .name = "uint8", .get = get_uint8, .set = set_uint8, @@ -304,7 +304,7 @@ static void set_uint16(Object *obj, Visitor *v, const char *name, visit_type_uint16(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint16 = { +const PropertyInfo qdev_prop_uint16 = { .name = "uint16", .get = get_uint16, .set = set_uint16, @@ -363,14 +363,14 @@ static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_int32(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint32 = { +const PropertyInfo qdev_prop_uint32 = { .name = "uint32", .get = get_uint32, .set = set_uint32, .set_default_value = set_default_value_uint, }; -PropertyInfo qdev_prop_int32 = { +const PropertyInfo qdev_prop_int32 = { .name = "int32", .get = get_int32, .set = set_int32, @@ -404,7 +404,7 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint64 = { +const PropertyInfo qdev_prop_uint64 = { .name = "uint64", .get = get_uint64, .set = set_uint64, @@ -457,7 +457,7 @@ static void set_string(Object *obj, Visitor *v, const char *name, *ptr = str; } -PropertyInfo qdev_prop_string = { +const PropertyInfo qdev_prop_string = { .name = "str", .release = release_string, .get = get_string, @@ -467,7 +467,7 @@ PropertyInfo qdev_prop_string = { /* --- pointer --- */ /* Not a proper property, just for dirty hacks. TODO Remove it! */ -PropertyInfo qdev_prop_ptr = { +const PropertyInfo qdev_prop_ptr = { .name = "ptr", }; @@ -541,7 +541,7 @@ inval: g_free(str); } -PropertyInfo qdev_prop_macaddr = { +const PropertyInfo qdev_prop_macaddr = { .name = "str", .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, @@ -550,7 +550,7 @@ PropertyInfo qdev_prop_macaddr = { /* --- on/off/auto --- */ -PropertyInfo qdev_prop_on_off_auto = { +const PropertyInfo qdev_prop_on_off_auto = { .name = "OnOffAuto", .description = "on/off/auto", .enum_table = OnOffAuto_lookup, @@ -563,7 +563,7 @@ PropertyInfo qdev_prop_on_off_auto = { QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); -PropertyInfo qdev_prop_losttickpolicy = { +const PropertyInfo qdev_prop_losttickpolicy = { .name = "LostTickPolicy", .enum_table = LostTickPolicy_lookup, .get = get_enum, @@ -575,7 +575,7 @@ PropertyInfo qdev_prop_losttickpolicy = { QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); -PropertyInfo qdev_prop_blockdev_on_error = { +const PropertyInfo qdev_prop_blockdev_on_error = { .name = "BlockdevOnError", .description = "Error handling policy, " "report/ignore/enospc/stop/auto", @@ -589,7 +589,7 @@ PropertyInfo qdev_prop_blockdev_on_error = { QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); -PropertyInfo qdev_prop_bios_chs_trans = { +const PropertyInfo qdev_prop_bios_chs_trans = { .name = "BiosAtaTranslation", .description = "Logical CHS translation algorithm, " "auto/none/lba/large/rechs", @@ -601,7 +601,7 @@ PropertyInfo qdev_prop_bios_chs_trans = { /* --- FDC default drive types */ -PropertyInfo qdev_prop_fdc_drive_type = { +const PropertyInfo qdev_prop_fdc_drive_type = { .name = "FdcDriveType", .description = "FDC drive type, " "144/288/120/none/auto", @@ -677,7 +677,7 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, } } -PropertyInfo qdev_prop_pci_devfn = { +const PropertyInfo qdev_prop_pci_devfn = { .name = "int32", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, @@ -726,7 +726,7 @@ static void set_blocksize(Object *obj, Visitor *v, const char *name, *ptr = value; } -PropertyInfo qdev_prop_blocksize = { +const PropertyInfo qdev_prop_blocksize = { .name = "uint16", .description = "A power of two between 512 and 32768", .get = get_uint16, @@ -841,7 +841,7 @@ inval: g_free(str); } -PropertyInfo qdev_prop_pci_host_devaddr = { +const PropertyInfo qdev_prop_pci_host_devaddr = { .name = "str", .description = "Address (bus/device/function) of " "the host device, example: 04:10.0", @@ -950,7 +950,7 @@ static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, } } -PropertyInfo qdev_prop_arraylen = { +const PropertyInfo qdev_prop_arraylen = { .name = "uint32", .get = get_uint32, .set = set_prop_arraylen, @@ -1208,7 +1208,7 @@ static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_size(v, name, ptr, errp); } -PropertyInfo qdev_prop_size = { +const PropertyInfo qdev_prop_size = { .name = "size", .get = get_size, .set = set_size, @@ -1228,7 +1228,7 @@ static void create_link_property(Object *obj, Property *prop, Error **errp) errp); } -PropertyInfo qdev_prop_link = { +const PropertyInfo qdev_prop_link = { .name = "link", .create = create_link_property, }; diff --git a/hw/s390x/css.c b/hw/s390x/css.c index d67fffae30..cd0b776861 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -2095,7 +2095,7 @@ out: g_free(str); } -PropertyInfo css_devid_propinfo = { +const PropertyInfo css_devid_propinfo = { .name = "str", .description = "Identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", @@ -2103,7 +2103,7 @@ PropertyInfo css_devid_propinfo = { .set = set_css_devid, }; -PropertyInfo css_devid_ro_propinfo = { +const PropertyInfo css_devid_ro_propinfo = { .name = "str", .description = "Read-only identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 903b3cadc5..af702f8840 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1015,7 +1015,7 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, zpci->fid_defined = true; } -static PropertyInfo s390_pci_fid_propinfo = { +static const PropertyInfo s390_pci_fid_propinfo = { .name = "zpci_fid", .get = s390_pci_get_fid, .set = s390_pci_set_fid, diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 08d1d2c6f9..53488153fd 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -223,7 +223,7 @@ struct BusState { struct Property { const char *name; - PropertyInfo *info; + const PropertyInfo *info; ptrdiff_t offset; uint8_t bitnr; union { @@ -231,7 +231,7 @@ struct Property { uint64_t u; } defval; int arrayoffset; - PropertyInfo *arrayinfo; + const PropertyInfo *arrayinfo; int arrayfieldsize; const char *link_type; }; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 876ba2e283..f6692d5dc3 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -5,32 +5,32 @@ /*** qdev-properties.c ***/ -extern PropertyInfo qdev_prop_bit; -extern PropertyInfo qdev_prop_bit64; -extern PropertyInfo qdev_prop_bool; -extern PropertyInfo qdev_prop_uint8; -extern PropertyInfo qdev_prop_uint16; -extern PropertyInfo qdev_prop_uint32; -extern PropertyInfo qdev_prop_int32; -extern PropertyInfo qdev_prop_uint64; -extern PropertyInfo qdev_prop_size; -extern PropertyInfo qdev_prop_string; -extern PropertyInfo qdev_prop_chr; -extern PropertyInfo qdev_prop_ptr; -extern PropertyInfo qdev_prop_macaddr; -extern PropertyInfo qdev_prop_on_off_auto; -extern PropertyInfo qdev_prop_losttickpolicy; -extern PropertyInfo qdev_prop_blockdev_on_error; -extern PropertyInfo qdev_prop_bios_chs_trans; -extern PropertyInfo qdev_prop_fdc_drive_type; -extern PropertyInfo qdev_prop_drive; -extern PropertyInfo qdev_prop_netdev; -extern PropertyInfo qdev_prop_vlan; -extern PropertyInfo qdev_prop_pci_devfn; -extern PropertyInfo qdev_prop_blocksize; -extern PropertyInfo qdev_prop_pci_host_devaddr; -extern PropertyInfo qdev_prop_arraylen; -extern PropertyInfo qdev_prop_link; +extern const PropertyInfo qdev_prop_bit; +extern const PropertyInfo qdev_prop_bit64; +extern const PropertyInfo qdev_prop_bool; +extern const PropertyInfo qdev_prop_uint8; +extern const PropertyInfo qdev_prop_uint16; +extern const PropertyInfo qdev_prop_uint32; +extern const PropertyInfo qdev_prop_int32; +extern const PropertyInfo qdev_prop_uint64; +extern const PropertyInfo qdev_prop_size; +extern const PropertyInfo qdev_prop_string; +extern const PropertyInfo qdev_prop_chr; +extern const PropertyInfo qdev_prop_ptr; +extern const PropertyInfo qdev_prop_macaddr; +extern const PropertyInfo qdev_prop_on_off_auto; +extern const PropertyInfo qdev_prop_losttickpolicy; +extern const PropertyInfo qdev_prop_blockdev_on_error; +extern const PropertyInfo qdev_prop_bios_chs_trans; +extern const PropertyInfo qdev_prop_fdc_drive_type; +extern const PropertyInfo qdev_prop_drive; +extern const PropertyInfo qdev_prop_netdev; +extern const PropertyInfo qdev_prop_vlan; +extern const PropertyInfo qdev_prop_pci_devfn; +extern const PropertyInfo qdev_prop_blocksize; +extern const PropertyInfo qdev_prop_pci_host_devaddr; +extern const PropertyInfo qdev_prop_arraylen; +extern const PropertyInfo qdev_prop_link; #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ .name = (_name), \ diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index eb0e26f258..dc1001bee1 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -112,7 +112,7 @@ typedef struct CssDevId { bool valid; } CssDevId; -extern PropertyInfo css_devid_propinfo; +extern const PropertyInfo css_devid_propinfo; #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId) @@ -196,7 +196,7 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid); bool css_present(uint8_t cssid); #endif -extern PropertyInfo css_devid_ro_propinfo; +extern const PropertyInfo css_devid_ro_propinfo; #define DEFINE_PROP_CSS_DEV_ID_RO(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_ro_propinfo, CssDevId) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c57177278b..1b089c5cfd 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1961,7 +1961,7 @@ static void x86_set_hv_spinlocks(Object *obj, Visitor *v, const char *name, cpu->hyperv_spinlock_attempts = value; } -static PropertyInfo qdev_prop_spinlocks = { +static const PropertyInfo qdev_prop_spinlocks = { .name = "int", .get = x86_get_hv_spinlocks, .set = x86_set_hv_spinlocks, diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 783bf98217..ae25fafab9 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8435,7 +8435,7 @@ static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, visit_type_null(v, name, NULL); } -static PropertyInfo ppc_compat_deprecated_propinfo = { +static const PropertyInfo ppc_compat_deprecated_propinfo = { .name = "str", .description = "compatibility mode (deprecated)", .get = getset_compat_deprecated, From d679ac09f06c53c7cc4ac43bedb068db943272bc Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:55 +0800 Subject: [PATCH 47/55] virtio-blk: Convert to DEFINE_PROP_LINK Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-7-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/block/virtio-blk.c | 6 ++---- hw/s390x/virtio-ccw.c | 2 -- hw/virtio/virtio-pci.c | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index c0bd247b37..b750bd8b53 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -983,10 +983,6 @@ static void virtio_blk_instance_init(Object *obj) { VirtIOBlock *s = VIRTIO_BLK(obj); - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&s->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); device_add_bootindex_property(obj, &s->conf.conf.bootindex, "bootindex", "/disk@0,0", DEVICE(obj), NULL); @@ -1014,6 +1010,8 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), + DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, + IOThread *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e18fd2600d..a0437d1517 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -867,8 +867,6 @@ static void virtio_ccw_blk_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), "bootindex", &error_abort); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 93480a7af1..94f72f3e1a 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2000,8 +2000,6 @@ static void virtio_blk_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), "bootindex", &error_abort); } From 08f1ecd873e98d2e04ee884c8e268a96b5fb4160 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:56 +0800 Subject: [PATCH 48/55] virtio-scsi: Convert to DEFINE_PROP_LINK Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-8-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/virtio-ccw.c | 2 -- hw/scsi/virtio-scsi.c | 13 ++----------- hw/virtio/virtio-pci.c | 2 -- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index a0437d1517..c5e0606f54 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -950,8 +950,6 @@ static void virtio_ccw_scsi_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); } #ifdef CONFIG_VHOST_SCSI diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index d076fe778b..eb639442d1 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -898,16 +898,6 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) virtio_scsi_dataplane_setup(s, errp); } -static void virtio_scsi_instance_init(Object *obj) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&vs->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); -} - void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -935,6 +925,8 @@ static Property virtio_scsi_properties[] = { VIRTIO_SCSI_F_HOTPLUG, true), DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features, VIRTIO_SCSI_F_CHANGE, true), + DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread, + TYPE_IOTHREAD, IOThread *), DEFINE_PROP_END_OF_LIST(), }; @@ -989,7 +981,6 @@ static const TypeInfo virtio_scsi_info = { .name = TYPE_VIRTIO_SCSI, .parent = TYPE_VIRTIO_SCSI_COMMON, .instance_size = sizeof(VirtIOSCSI), - .instance_init = virtio_scsi_instance_init, .class_init = virtio_scsi_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 94f72f3e1a..cc69fe3214 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2069,8 +2069,6 @@ static void virtio_scsi_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); } static const TypeInfo virtio_scsi_pci_info = { From d1fd7f775ea975f7c710c380a1967e1576dec48f Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:57 +0800 Subject: [PATCH 49/55] virtio-rng: Convert to DEFINE_PROP_LINK Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-9-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/virtio-ccw.c | 2 -- hw/virtio/virtio-pci.c | 2 -- hw/virtio/virtio-rng.c | 12 +----------- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index c5e0606f54..a492d0ffa7 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1548,8 +1548,6 @@ static void virtio_ccw_rng_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), - "rng", &error_abort); } static Property virtio_ccw_rng_properties[] = { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index cc69fe3214..5d14bd66dc 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2463,8 +2463,6 @@ static void virtio_rng_initfn(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), "rng", - &error_abort); } static const TypeInfo virtio_rng_pci_info = { diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index a6ee501051..289bbcac03 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -246,6 +246,7 @@ static Property virtio_rng_properties[] = { */ DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), + DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -262,21 +263,10 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) vdc->get_features = get_features; } -static void virtio_rng_initfn(Object *obj) -{ - VirtIORNG *vrng = VIRTIO_RNG(obj); - - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&vrng->conf.rng, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); -} - static const TypeInfo virtio_rng_info = { .name = TYPE_VIRTIO_RNG, .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIORNG), - .instance_init = virtio_rng_initfn, .class_init = virtio_rng_class_init, }; From aa8f057e74ae08014736a690ff41f76c756f75f1 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:58 +0800 Subject: [PATCH 50/55] virtio-crypto: Convert to DEFINE_PROP_LINK Unlike other object_property_add_link() occurrences in virtio devices, virtio-crypto checks the "in use" state of the linked backend object in addition to qdev_prop_allow_set_link_before_realize. To convert it without needing to specialize DEFINE_PROP_LINK which always uses the qdev callback, move the "in use" check to device realize time. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-10-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/virtio-ccw.c | 3 --- hw/virtio/virtio-crypto-pci.c | 2 -- hw/virtio/virtio-crypto.c | 27 +++++++-------------------- 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index a492d0ffa7..c07ddb1c94 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1594,9 +1594,6 @@ static void virtio_ccw_crypto_instance_init(Object *obj) ccw_dev->force_revision_1 = true; virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_CRYPTO); - - object_property_add_alias(obj, "cryptodev", OBJECT(&dev->vdev), - "cryptodev", &error_abort); } static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 422aca3a98..bf64996e48 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -62,8 +62,6 @@ static void virtio_crypto_initfn(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_CRYPTO); - object_property_add_alias(obj, "cryptodev", OBJECT(&dev->vdev), - "cryptodev", &error_abort); } static const TypeInfo virtio_crypto_pci_info = { diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 2590893016..19c82e0432 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -781,6 +781,11 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) if (vcrypto->cryptodev == NULL) { error_setg(errp, "'cryptodev' parameter expects a valid object"); return; + } else if (cryptodev_backend_is_used(vcrypto->cryptodev)) { + char *path = object_get_canonical_path_component(OBJECT(vcrypto->conf.cryptodev)); + error_setg(errp, "can't use already used cryptodev backend: %s", path); + g_free(path); + return; } vcrypto->max_queues = MAX(vcrypto->cryptodev->conf.peers.queues, 1); @@ -845,6 +850,8 @@ static const VMStateDescription vmstate_virtio_crypto = { }; static Property virtio_crypto_properties[] = { + DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, + TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -888,20 +895,6 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_crypto_reset; } -static void -virtio_crypto_check_cryptodev_is_used(const Object *obj, const char *name, - Object *val, Error **errp) -{ - if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(errp, - "can't use already used cryptodev backend: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); - } -} - static void virtio_crypto_instance_init(Object *obj) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(obj); @@ -911,12 +904,6 @@ static void virtio_crypto_instance_init(Object *obj) * Can be overriden with virtio_crypto_set_config_size. */ vcrypto->config_size = sizeof(struct virtio_crypto_config); - - object_property_add_link(obj, "cryptodev", - TYPE_CRYPTODEV_BACKEND, - (Object **)&vcrypto->conf.cryptodev, - virtio_crypto_check_cryptodev_is_used, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); } static const TypeInfo virtio_crypto_info = { From 2de7e26891db3d5b7f214fa485a5e946b17a57b9 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:14:59 +0800 Subject: [PATCH 51/55] dimm: Convert to DEFINE_PROP_LINK Unlike the usual object_property_add_link() invocations in other devices, dimm checks the "is mapped" state of the backend in addition to qdev_prop_allow_set_link_before_realize. To convert it without specializing DEFINE_PROP_LINK which always uses the qdev general check callback, move the extra check to device realize time. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-11-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/mem/pc-dimm.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 5e23495473..ea67b461c2 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -350,6 +350,8 @@ static Property pc_dimm_properties[] = { DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, PC_DIMM_UNASSIGNED_SLOT), + DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -367,33 +369,10 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &value, errp); } -static void pc_dimm_check_memdev_is_busy(const Object *obj, const char *name, - Object *val, Error **errp) -{ - Error *local_err = NULL; - - if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(&local_err, "can't use already busy memdev: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err); - } - - error_propagate(errp, local_err); -} - static void pc_dimm_init(Object *obj) { - PCDIMMDevice *dimm = PC_DIMM(obj); - object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size, NULL, NULL, NULL, &error_abort); - object_property_add_link(obj, PC_DIMM_MEMDEV_PROP, TYPE_MEMORY_BACKEND, - (Object **)&dimm->hostmem, - pc_dimm_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); } static void pc_dimm_realize(DeviceState *dev, Error **errp) @@ -404,6 +383,11 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp) if (!dimm->hostmem) { error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); return; + } else if (host_memory_backend_is_mapped(dimm->hostmem)) { + char *path = object_get_canonical_path_component(OBJECT(dimm->hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + return; } if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) || (!nb_numa_nodes && dimm->node)) { From e9cb190ad4cea8e6fd24afb973c5007b9a439bc9 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:15:00 +0800 Subject: [PATCH 52/55] ivshmem: Convert to DEFINE_PROP_LINK Unlike the usual object_property_add_link() invocations in other devices, ivshmem checks the "is mapped" state of the backend in addition to qdev_prop_allow_set_link_before_realize. To convert it without specializing DEFINE_PROP_LINK which always uses the qdev callback, move the extra check to device realize time. Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-12-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/misc/ivshmem.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 731f3c1d4f..a58f9ee579 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1009,18 +1009,6 @@ static const TypeInfo ivshmem_common_info = { .class_init = ivshmem_common_class_init, }; -static void ivshmem_check_memdev_is_busy(const Object *obj, const char *name, - Object *val, Error **errp) -{ - if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(errp, "can't use already busy memdev: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); - } -} - static const VMStateDescription ivshmem_plain_vmsd = { .name = TYPE_IVSHMEM_PLAIN, .version_id = 0, @@ -1037,6 +1025,8 @@ static const VMStateDescription ivshmem_plain_vmsd = { static Property ivshmem_plain_properties[] = { DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, + HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -1044,11 +1034,6 @@ static void ivshmem_plain_init(Object *obj) { IVShmemState *s = IVSHMEM_PLAIN(obj); - object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND, - (Object **)&s->hostmem, - ivshmem_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); s->not_legacy_32bit = 1; } @@ -1059,6 +1044,11 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) if (!s->hostmem) { error_setg(errp, "You must specify a 'memdev'"); return; + } else if (host_memory_backend_is_mapped(s->hostmem)) { + char *path = object_get_canonical_path_component(OBJECT(s->hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + return; } ivshmem_common_realize(dev, errp); From e4934bb39c76bd86cccae384f2b5ef414d6f2dcb Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:15:07 +0800 Subject: [PATCH 53/55] mips_cmgcr: Convert to DEFINE_PROP_LINK Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-19-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/misc/mips_cmgcr.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index a1edb53f95..211f6097fd 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -181,18 +181,6 @@ static void mips_gcr_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); MIPSGCRState *s = MIPS_GCR(obj); - object_property_add_link(obj, "gic", TYPE_MEMORY_REGION, - (Object **)&s->gic_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - - object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION, - (Object **)&s->cpc_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s, "mips-gcr", GCR_ADDRSPACE_SZ); sysbus_init_mmio(sbd, &s->iomem); @@ -227,6 +215,10 @@ static Property mips_gcr_properties[] = { DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1), DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), + DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION, + MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; From c7e002c55acd82c8b7f512d52fe3fb6f94c1939e Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:15:08 +0800 Subject: [PATCH 54/55] cpu: Convert to DEFINE_PROP_LINK Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-20-famz@redhat.com> Signed-off-by: Paolo Bonzini --- exec.c | 27 +++++++++++++++------------ include/qom/cpu.h | 1 + qom/cpu.c | 1 + 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/exec.c b/exec.c index b3c080f731..01ac21e3cd 100644 --- a/exec.c +++ b/exec.c @@ -27,6 +27,7 @@ #include "exec/target_page.h" #include "tcg.h" #include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) #include "hw/boards.h" #include "hw/xen/xen.h" @@ -737,6 +738,20 @@ void cpu_exec_unrealizefn(CPUState *cpu) } } +Property cpu_common_props[] = { +#ifndef CONFIG_USER_ONLY + /* Create a memory property for softmmu CPU object, + * so users can wire up its memory. (This can't go in qom/cpu.c + * because that file is compiled only once for both user-mode + * and system builds.) The default if no link is set up is to use + * the system address space. + */ + DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), +#endif + DEFINE_PROP_END_OF_LIST(), +}; + void cpu_exec_initfn(CPUState *cpu) { cpu->as = NULL; @@ -744,18 +759,6 @@ void cpu_exec_initfn(CPUState *cpu) #ifndef CONFIG_USER_ONLY cpu->thread_id = qemu_get_thread_id(); - - /* This is a softmmu CPU object, so create a property for it - * so users can wire up its memory. (This can't go in qom/cpu.c - * because that file is compiled only once for both user-mode - * and system builds.) The default if no link is set up is to use - * the system address space. - */ - object_property_add_link(OBJECT(cpu), "memory", TYPE_MEMORY_REGION, - (Object **)&cpu->memory, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); cpu->memory = system_memory; object_ref(OBJECT(cpu->memory)); #endif diff --git a/include/qom/cpu.h b/include/qom/cpu.h index d3c783b4b5..04c31e63eb 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -1013,6 +1013,7 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); void QEMU_NORETURN cpu_abort(CPUState *cpu, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +extern Property cpu_common_props[]; void cpu_exec_initfn(CPUState *cpu); void cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); diff --git a/qom/cpu.c b/qom/cpu.c index 8757f033a0..a39ff6c19c 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -458,6 +458,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; + dc->props = cpu_common_props; /* * Reason: CPUs still need special care by board code: wiring up * IRQs, adding reset handlers, halting non-first CPUs, ... From 68c761e19c2ea453f880dbbd04e867d34d1468b8 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 14 Jul 2017 10:15:09 +0800 Subject: [PATCH 55/55] spapr_rng: Convert to DEFINE_PROP_LINK Signed-off-by: Fam Zheng Message-Id: <20170714021509.23681-21-famz@redhat.com> Signed-off-by: Paolo Bonzini --- hw/ppc/spapr_rng.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 80515eb54d..d2acd61a15 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -96,17 +96,11 @@ static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void spapr_rng_instance_init(Object *obj) { - sPAPRRngState *rngstate = SPAPR_RNG(obj); - if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) { error_report("spapr-rng can not be instantiated twice!"); return; } - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&rngstate->backend, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); object_property_set_description(obj, "rng", "ID of the random number generator backend", NULL); @@ -163,6 +157,8 @@ int spapr_rng_populate_dt(void *fdt) static Property spapr_rng_properties[] = { DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false), + DEFINE_PROP_LINK("rng", sPAPRRngState, backend, TYPE_RNG_BACKEND, + RngBackend *), DEFINE_PROP_END_OF_LIST(), };