From e6cdeee95990a2c6f5d6873d3afb3c90518aed5c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 10:35:30 +0100 Subject: [PATCH 1/8] hw/xen: Add xs_node_read() helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This returns the full contents of the node, having created the node path from the printf-style format string provided in its arguments. This will save various callers from having to do so for themselves (and from using xs_node_scanf() with the non-portable %ms format string. Signed-off-by: David Woodhouse [remove double newline and constify trace parameters] Signed-off-by: Roger Pau Monné Reviewed-by: Anthony PERARD --- hw/xen/trace-events | 1 + hw/xen/xen-bus-helper.c | 22 ++++++++++++++++++++++ include/hw/xen/xen-bus-helper.h | 9 +++++++++ 3 files changed, 32 insertions(+) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index a07fe41c6d..461dee7b23 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -39,6 +39,7 @@ xs_node_create(const char *node) "%s" xs_node_destroy(const char *node) "%s" xs_node_vprintf(char *path, char *value) "%s %s" xs_node_vscanf(char *path, char *value) "%s %s" +xs_node_read(const char *path, const char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c index b2b2cc9c5d..22fd2f6c1a 100644 --- a/hw/xen/xen-bus-helper.c +++ b/hw/xen/xen-bus-helper.c @@ -142,6 +142,28 @@ int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, return rc; } +char *xs_node_read(struct qemu_xs_handle *h, xs_transaction_t tid, + unsigned int *len, Error **errp, + const char *path_fmt, ...) +{ + char *path, *value; + va_list ap; + + va_start(ap, path_fmt); + path = g_strdup_vprintf(path_fmt, ap); + va_end(ap); + + value = qemu_xen_xs_read(h, tid, path, len); + trace_xs_node_read(path, value); + if (!value) { + error_setg_errno(errp, errno, "failed to read from '%s'", path); + } + + g_free(path); + + return value; +} + struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, const char *key, xs_watch_fn fn, void *opaque, Error **errp) diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h index d8dcc2f010..e9911115b3 100644 --- a/include/hw/xen/xen-bus-helper.h +++ b/include/hw/xen/xen-bus-helper.h @@ -38,6 +38,15 @@ int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *fmt, ...) G_GNUC_SCANF(6, 7); +/* + * Unlike other functions here, the printf-formatted path_fmt is for + * the XenStore path, not the contents of the node. + */ +char *xs_node_read(struct qemu_xs_handle *h, xs_transaction_t tid, + unsigned int *len, Error **errp, + const char *path_fmt, ...) + G_GNUC_PRINTF(5, 6); + /* Watch node/key unless node is empty, in which case watch key */ struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, const char *key, xs_watch_fn fn, From 7a0b74d8716836f1206c5dfd778984c5d6eee46b Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Fri, 10 Jan 2025 10:35:31 +0100 Subject: [PATCH 2/8] xen: do not use '%ms' scanf specifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'm' parameter used to request auto-allocation of the destination variable is not supported on FreeBSD, and as such leads to failures to parse. What's more, the current usage of '%ms' with xs_node_scanf() is pointless, as it just leads to a double allocation of the same string. Instead use xs_node_read() to read the whole xenstore node. Fixes: a783f8ad4ec9 ('xen: add a mechanism to automatically create XenDevice-s...') Fixes: 9b7737469080 ('hw/xen: update Xen console to XenDevice model') Signed-off-by: Roger Pau Monné Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD --- hw/block/xen-block.c | 3 ++- hw/char/xen_console.c | 6 ++++-- hw/xen/xen-bus.c | 14 ++++++++++++-- include/hw/xen/xen-bus.h | 1 + 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 306d38927c..034a18b70e 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -239,7 +239,8 @@ static void xen_block_connect(XenDevice *xendev, Error **errp) return; } - if (xen_device_frontend_scanf(xendev, "protocol", "%ms", &str) != 1) { + str = xen_device_frontend_read(xendev, "protocol"); + if (!str) { /* x86 defaults to the 32-bit protocol even for 64-bit guests. */ if (object_dynamic_cast(OBJECT(qdev_get_machine()), "x86-machine")) { protocol = BLKIF_PROTOCOL_X86_32; diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index ef0c2912ef..cb39b21504 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -550,7 +550,8 @@ static void xen_console_device_create(XenBackendInstance *backend, goto fail; } - if (xs_node_scanf(xsh, XBT_NULL, fe, "type", errp, "%ms", &type) != 1) { + type = xs_node_read(xsh, XBT_NULL, NULL, errp, "%s/%s", fe, "type"); + if (!type) { error_prepend(errp, "failed to read console device type: "); goto fail; } @@ -568,7 +569,8 @@ static void xen_console_device_create(XenBackendInstance *backend, snprintf(label, sizeof(label), "xencons%ld", number); - if (xs_node_scanf(xsh, XBT_NULL, fe, "output", NULL, "%ms", &output) == 1) { + output = xs_node_read(xsh, XBT_NULL, NULL, NULL, "%s/%s", fe, "output"); + if (output) { /* * FIXME: sure we want to support implicit * muxed monitors here? diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index adfc4efad0..feeb612681 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -156,8 +156,8 @@ again: !strcmp(key[i], "hotplug-status")) continue; - if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", - &val) == 1) { + val = xs_node_read(xenbus->xsh, tid, NULL, NULL, "%s/%s", path, key[i]); + if (val) { qdict_put_str(opts, key[i], val); free(val); } @@ -650,6 +650,16 @@ int xen_device_frontend_scanf(XenDevice *xendev, const char *key, return rc; } +char *xen_device_frontend_read(XenDevice *xendev, const char *key) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + + g_assert(xenbus->xsh); + + return xs_node_read(xenbus->xsh, XBT_NULL, NULL, NULL, "%s/%s", + xendev->frontend_path, key); +} + static void xen_device_frontend_set_state(XenDevice *xendev, enum xenbus_state state, bool publish) diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 38d40afa37..2adb2af839 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -91,6 +91,7 @@ void xen_device_frontend_printf(XenDevice *xendev, const char *key, int xen_device_frontend_scanf(XenDevice *xendev, const char *key, const char *fmt, ...) G_GNUC_SCANF(3, 4); +char *xen_device_frontend_read(XenDevice *xendev, const char *key); void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, Error **errp); From 76f26e46ac577421e148adcedabfa12d4200ff3e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:37:48 +0000 Subject: [PATCH 3/8] hw/xen: Use xs_node_read() from xs_node_vscanf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce some duplication. Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/xen/trace-events | 1 - hw/xen/xen-bus-helper.c | 15 ++++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 461dee7b23..b67942d07b 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -38,7 +38,6 @@ xen_device_remove_watch(const char *type, char *name, const char *node, const ch xs_node_create(const char *node) "%s" xs_node_destroy(const char *node) "%s" xs_node_vprintf(char *path, char *value) "%s %s" -xs_node_vscanf(char *path, char *value) "%s %s" xs_node_read(const char *path, const char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c index 22fd2f6c1a..288fad422b 100644 --- a/hw/xen/xen-bus-helper.c +++ b/hw/xen/xen-bus-helper.c @@ -105,25 +105,22 @@ int xs_node_vscanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, va_list ap) { - char *path, *value; + char *value; int rc; - path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : - g_strdup(key); - value = qemu_xen_xs_read(h, tid, path, NULL); - - trace_xs_node_vscanf(path, value); + if (node && strlen(node) != 0) { + value = xs_node_read(h, tid, NULL, errp, "%s/%s", node, key); + } else { + value = xs_node_read(h, tid, NULL, errp, "%s", key); + } if (value) { rc = vsscanf(value, fmt, ap); } else { - error_setg_errno(errp, errno, "failed to read from '%s'", - path); rc = EOF; } free(value); - g_free(path); return rc; } From b34729aca2f18a7c277bd2903b932add21cf7796 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:07:16 +0000 Subject: [PATCH 4/8] hw/xen: Use xs_node_read() from xen_console_get_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that xs_node_read() can construct a node path, no need to open-code it. Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/char/xen_console.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index cb39b21504..e61902461b 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -367,28 +367,28 @@ static char *xen_console_get_name(XenDevice *xendev, Error **errp) if (con->dev == -1) { XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - char fe_path[XENSTORE_ABS_PATH_MAX + 1]; int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + Error *local_err = NULL; char *value; /* Theoretically we could go up to INT_MAX here but that's overkill */ while (idx < 100) { if (!idx) { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/console", xendev->frontend_id); + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/console", + xendev->frontend_id); } else { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/device/console/%u", - xendev->frontend_id, idx); + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/device/console/%u", + xendev->frontend_id, idx); } - value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); if (!value) { if (errno == ENOENT) { con->dev = idx; + error_free(local_err); goto found; } - error_setg(errp, "cannot read %s: %s", fe_path, - strerror(errno)); + error_propagate(errp, local_err); return NULL; } free(value); From e4e113ecc2d2e4d1bc9d3a70bf01bba4b86f845c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:12:36 +0000 Subject: [PATCH 5/8] hw/xen: Use xs_node_read() from xen_netdev_get_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that xs_node_read() can construct a node path, no need to open-code it. Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/net/xen_nic.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 97ebd9fa30..5410039490 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -510,23 +510,22 @@ static char *xen_netdev_get_name(XenDevice *xendev, Error **errp) if (netdev->dev == -1) { XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - char fe_path[XENSTORE_ABS_PATH_MAX + 1]; int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + Error *local_err = NULL; char *value; /* Theoretically we could go up to INT_MAX here but that's overkill */ while (idx < 100) { - snprintf(fe_path, sizeof(fe_path), - "/local/domain/%u/device/vif/%u", - xendev->frontend_id, idx); - value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/device/vif/%u", + xendev->frontend_id, idx); if (!value) { if (errno == ENOENT) { netdev->dev = idx; + error_free(local_err); goto found; } - error_setg(errp, "cannot read %s: %s", fe_path, - strerror(errno)); + error_propagate(errp, local_err); return NULL; } free(value); From cd414c3f566fdbd98778c1c9a497428a80cd7fdd Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 10 Jan 2025 09:18:13 +0000 Subject: [PATCH 6/8] hw/xen: Use xs_node_read() from xenstore_read_str() instead of open-coding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD Acked-by: Roger Pau Monné --- hw/xen/xen_pvdev.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index c5ad71e8dc..c9143ba259 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -22,6 +22,7 @@ #include "qemu/main-loop.h" #include "hw/qdev-core.h" #include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus-helper.h" #include "hw/xen/xen_pvdev.h" /* private */ @@ -81,12 +82,9 @@ int xenstore_write_str(const char *base, const char *node, const char *val) char *xenstore_read_str(const char *base, const char *node) { - char abspath[XEN_BUFSIZE]; - unsigned int len; char *str, *ret = NULL; - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - str = qemu_xen_xs_read(xenstore, 0, abspath, &len); + str = xs_node_read(xenstore, 0, NULL, NULL, "%s/%s", base, node); if (str != NULL) { /* move to qemu-allocated memory to make sure * callers can safely g_free() stuff. */ From 8b44a3e39f36540818d99ef8cf79e64bba1ed9c3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 15 Jan 2025 15:46:06 +0000 Subject: [PATCH 7/8] hw/xen: Fix errp handling in xen_console When attempting to read the 'output' node, interpret any error *other* than ENOENT as a fatal error. For ENOENT, fall back to serial_hd() to find a character device, or create a null device. Do not attempt to prepend to errp when serial_hd() fails; the error isn't relevant (and prior to this change, wasn't set anyway). Signed-off-by: David Woodhouse Reviewed-by: Anthony PERARD --- hw/char/xen_console.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index e61902461b..d03c188d1d 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -569,7 +569,7 @@ static void xen_console_device_create(XenBackendInstance *backend, snprintf(label, sizeof(label), "xencons%ld", number); - output = xs_node_read(xsh, XBT_NULL, NULL, NULL, "%s/%s", fe, "output"); + output = xs_node_read(xsh, XBT_NULL, NULL, errp, "%s/%s", fe, "output"); if (output) { /* * FIXME: sure we want to support implicit @@ -581,19 +581,27 @@ static void xen_console_device_create(XenBackendInstance *backend, output); goto fail; } - } else if (number) { - cd = serial_hd(number); - if (!cd) { - error_prepend(errp, "console: No serial device #%ld found: ", - number); - goto fail; - } + } else if (errno != ENOENT) { + error_prepend(errp, "console: No valid chardev found: "); + goto fail; } else { - /* No 'output' node on primary console: use null. */ - cd = qemu_chr_new(label, "null", NULL); - if (!cd) { - error_setg(errp, "console: failed to create null device"); - goto fail; + error_free(*errp); + *errp = NULL; + + if (number) { + cd = serial_hd(number); + if (!cd) { + error_setg(errp, "console: No serial device #%ld found", + number); + goto fail; + } + } else { + /* No 'output' node on primary console: use null. */ + cd = qemu_chr_new(label, "null", NULL); + if (!cd) { + error_setg(errp, "console: failed to create null device"); + goto fail; + } } } From e7bc0204e57836b3df611b73d2decc56ed698c4a Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Sun, 12 Jan 2025 22:26:09 +0100 Subject: [PATCH 8/8] system/runstate: Fix regression, clarify BQL status of exit notifiers By changing the way the main QEMU event loop is invoked, I inadvertently changed the BQL status of exit notifiers: some of them implicitly assumed they would be called with the BQL held; the BQL is however not held during the exit(status) call in qemu_default_main(). Instead of attempting to ensuring we always call exit() from the BQL - including any transitive calls - this change adds a BQL lock guard to qemu_run_exit_notifiers, ensuring the BQL will always be held in the exit notifiers. Additionally, the BQL promise is now documented at the qemu_{add,remove}_exit_notifier() declarations. Fixes: f5ab12caba4f ("ui & main loop: Redesign of system-specific main thread event handling") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2771 Reported-by: David Woodhouse Signed-off-by: Phil Dennis-Jordan Tested-by: David Woodhouse Signed-off-by: David Woodhouse --- include/system/system.h | 1 + system/runstate.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/system/system.h b/include/system/system.h index 5364ad4f27..0cbb43ec30 100644 --- a/include/system/system.h +++ b/include/system/system.h @@ -15,6 +15,7 @@ extern bool qemu_uuid_set; const char *qemu_get_vm_name(void); +/* Exit notifiers will run with BQL held. */ void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); diff --git a/system/runstate.c b/system/runstate.c index 3a8fe866bc..272801d307 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -850,6 +850,7 @@ void qemu_remove_exit_notifier(Notifier *notify) static void qemu_run_exit_notifiers(void) { + BQL_LOCK_GUARD(); notifier_list_notify(&exit_notifiers, NULL); }