Skip to contentSkip to conversation

Xen regression fixes and cleanups

GIT PULL Applied David Woodhouse 4 participants 13 messages Jan 16, 2025 – Jan 17, 2025

lore
Share & tools
lore
#0 lore raw

From: David Woodhouse <[email protected]>

The following changes since commit 7433709a147706ad7d1956b15669279933d0f82b:

Merge tag 'hw-misc-20250113' of https://github.com/philmd/qemu into staging (2025-01-14 12:46:56 -0500)

are available in the Git repository at:

git://git.infradead.org/users/dwmw2/qemu.git tags/pull-xenfv-20250116

for you to fetch changes up to e7bc0204e57836b3df611b73d2decc56ed698c4a:

system/runstate: Fix regression, clarify BQL status of exit notifiers (2025-01-15 18:05:19 +0000)

----------------------------------------------------------------
Xen regression fixes and cleanups

----------------------------------------------------------------
David Woodhouse (6):
hw/xen: Add xs_node_read() helper function
hw/xen: Use xs_node_read() from xs_node_vscanf()
hw/xen: Use xs_node_read() from xen_console_get_name()
hw/xen: Use xs_node_read() from xen_netdev_get_name()
hw/xen: Use xs_node_read() from xenstore_read_str() instead of open-coding it hw/xen: Fix errp handling in xen_console

Phil Dennis-Jordan (1):
system/runstate: Fix regression, clarify BQL status of exit notifiers

Roger Pau Monné (1):
xen: do not use '%ms' scanf specifier

 hw/block/xen-block.c            |  3 ++-
 hw/char/xen_console.c           | 56 ++++++++++++++++++++++++-----------------
 hw/net/xen_nic.c                | 13 +++++-----
 hw/xen/trace-events             |  2 +-
 hw/xen/xen-bus-helper.c         | 37 ++++++++++++++++++++-------
 hw/xen/xen-bus.c                | 14 +++++++++--
 hw/xen/xen_pvdev.c              |  6 ++---
 include/hw/xen/xen-bus-helper.h |  9 +++++++
 include/hw/xen/xen-bus.h        |  1 +
 include/system/system.h         |  1 +
 system/runstate.c               |  1 +
 11 files changed, 96 insertions(+), 47 deletions(-)
#1 lore raw

[PULL 8/8] system/runstate: Fix regression, clarify BQL status of exit notifiers

From: Phil Dennis-Jordan <[email protected]>

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

---
 include/system/system.h | 1 +
 system/runstate.c       | 1 +
 2 files changed, 2 insertions(+)
include/system/system.h +1 -0
index 5364ad4f27..0cbb43ec30 100644
@@ -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);
 
system/runstate.c +1 -0
index 3a8fe866bc..272801d307 100644
@@ -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);
 }
 
Signature · 1 line
2.47.0
#2 lore raw

[PULL 6/8] hw/xen: Use xs_node_read() from xenstore_read_str() instead of open-coding it

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

---
 hw/xen/xen_pvdev.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
hw/xen/xen_pvdev.c +2 -4
index c5ad71e8dc..c9143ba259 100644
@@ -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. */
Signature · 1 line
2.47.0
#3 lore raw

[PULL 7/8] hw/xen: Fix errp handling in xen_console

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

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).

---
 hw/char/xen_console.c | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)
hw/char/xen_console.c +21 -13
index e61902461b..d03c188d1d 100644
@@ -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;
+            }
         }
     }
 
Signature · 1 line
2.47.0
#4 lore raw

[PULL 1/8] hw/xen: Add xs_node_read() helper function

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

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.

[remove double newline and constify trace parameters]
---
 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(+)
hw/xen/trace-events +1 -0
index a07fe41c6d..461dee7b23 100644
@@ -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"
 
hw/xen/xen-bus-helper.c +22 -0
index b2b2cc9c5d..22fd2f6c1a 100644
@@ -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)
include/hw/xen/xen-bus-helper.h +9 -0
index d8dcc2f010..e9911115b3 100644
@@ -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,
Signature · 1 line
2.47.0
#5 lore raw

[PULL 2/8] xen: do not use '%ms' scanf specifier

In reply to David Woodhouse

From: Roger Pau Monne <[email protected]>

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')
---
 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(-)
hw/block/xen-block.c +2 -1
index 306d38927c..034a18b70e 100644
@@ -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;
hw/char/xen_console.c +4 -2
index ef0c2912ef..cb39b21504 100644
@@ -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?
hw/xen/xen-bus.c +12 -2
index adfc4efad0..feeb612681 100644
@@ -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)
include/hw/xen/xen-bus.h +1 -0
index 38d40afa37..2adb2af839 100644
@@ -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);
Signature · 1 line
2.47.0
#6 lore raw

[PULL 3/8] hw/xen: Use xs_node_read() from xs_node_vscanf()

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Reduce some duplication.

---
 hw/xen/trace-events     |  1 -
 hw/xen/xen-bus-helper.c | 15 ++++++---------
 2 files changed, 6 insertions(+), 10 deletions(-)
hw/xen/trace-events +0 -1
index 461dee7b23..b67942d07b 100644
@@ -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"
hw/xen/xen-bus-helper.c +6 -9
index 22fd2f6c1a..288fad422b 100644
@@ -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;
 }
Signature · 1 line
2.47.0
#7 lore raw

[PULL 5/8] hw/xen: Use xs_node_read() from xen_netdev_get_name()

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Now that xs_node_read() can construct a node path, no need to open-code it.

---
 hw/net/xen_nic.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
hw/net/xen_nic.c +6 -7
index 97ebd9fa30..5410039490 100644
@@ -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);
Signature · 1 line
2.47.0
#8 lore raw

[PULL 4/8] hw/xen: Use xs_node_read() from xen_console_get_name()

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Now that xs_node_read() can construct a node path, no need to open-code it.

---
 hw/char/xen_console.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
hw/char/xen_console.c +9 -9
index cb39b21504..e61902461b 100644
@@ -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);
Signature · 1 line
2.47.0
#10 lore raw
Quoted 5 lines · Stefan Hajnoczi

Applied, thanks.

Please update the changelog at https://wiki.qemu.org/ChangeLog/10.0 for any user-visible changes.

This note made me realise that my feature addition (macOS PV graphics device) that caused the regression requiring my patch in this series hasn't made it into the changelog. I'd be happy to add it myself, but I don't have a wiki account. How do I get one?

Thanks,
Phil

#11 lore raw
Quoted 5 lines · Phil Dennis-Jordan

This note made me realise that my feature addition (macOS PV graphics device) that caused the regression requiring my patch in this series hasn't made it into the changelog. I'd be happy to add it myself, but I don't have a wiki account. How do I get one?

I think the automatic account creation and you have to mail someone to get access, but I can't remember whom.

If you want to give me something I can cut/paste, I can add it for you.

#12 lore raw
Quoted 8 lines · David Woodhouse
Quoted 5 lines · Phil Dennis-Jordan

This note made me realise that my feature addition (macOS PV graphics device) that caused the regression requiring my patch in this series hasn't made it into the changelog. I'd be happy to add it myself, but I don't have a wiki account. How do I get one?

Quoted 4 lines

I think the automatic account creation and you have to mail someone to get access, but I can't remember whom.

If you want to give me something I can cut/paste, I can add it for you.

Any existing wiki user can create a new account for someone:
https://wiki.qemu.org/Special:CreateAccount

I have created an account for Phil and will send him the login details in a private email.

Stefan