From a5ec494e274ddcad6d487e3872e16964ef57e0de Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 11 Mar 2015 17:26:31 +0100 Subject: qdev-monitor: Stop error avalanche in qbus_find_recursive() Reproducer: $ qemu-system-x86_64 -nodefaults -device virtio-rng-pci -device virtio-rng-pci -device virtio-rng-device,bus=virtio-bus qemu-system-x86_64: -device virtio-rng-device,bus=virtio-bus: Bus 'virtio-bus' is full qemu-system-x86_64: -device virtio-rng-device,bus=virtio-bus: Bus 'virtio-bus' is full qemu-system-x86_64: -device virtio-rng-device,bus=virtio-bus: Bus 'virtio-bus' not found qbus_find_recursive() reports the "is full" error itself, and leaves reporting "not found" to its caller. The result is confusion. Write it a function contract that permits leaving all error reporting to the caller, and implement it. Update callers to detect and report "is full". Screwed up when commit 1395af6 added the max_dev limit and the "is full" error condition to enforce it. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake --- qdev-monitor.c | 64 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 24 deletions(-) (limited to 'qdev-monitor.c') diff --git a/qdev-monitor.c b/qdev-monitor.c index d71d1ee520..2c4d4c89bd 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -364,43 +364,55 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem) return NULL; } +static inline bool qbus_is_full(BusState *bus) +{ + BusClass *bus_class = BUS_GET_CLASS(bus); + return bus_class->max_dev && bus->max_index >= bus_class->max_dev; +} + +/* + * Search the tree rooted at @bus for a bus. + * If @name, search for a bus with that name. Note that bus names + * need not be unique. Yes, that's screwed up. + * Else search for a bus that is a subtype of @bus_typename. + * If more than one exists, prefer one that can take another device. + * Return the bus if found, else %NULL. + */ static BusState *qbus_find_recursive(BusState *bus, const char *name, const char *bus_typename) { - BusClass *bus_class = BUS_GET_CLASS(bus); BusChild *kid; - BusState *child, *ret; - int match = 1; - - if (name && (strcmp(bus->name, name) != 0)) { - match = 0; - } else if (bus_typename && !object_dynamic_cast(OBJECT(bus), bus_typename)) { - match = 0; - } else if ((bus_class->max_dev != 0) && (bus_class->max_dev <= bus->max_index)) { - if (name != NULL) { - /* bus was explicitly specified: return an error. */ - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Bus '%s' is full", - bus->name); - return NULL; - } else { - /* bus was not specified: try to find another one. */ - match = 0; - } + BusState *pick, *child, *ret; + bool match; + + assert(name || bus_typename); + if (name) { + match = !strcmp(bus->name, name); + } else { + match = !!object_dynamic_cast(OBJECT(bus), bus_typename); } - if (match) { - return bus; + + if (match && !qbus_is_full(bus)) { + return bus; /* root matches and isn't full */ } + pick = match ? bus : NULL; + QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; QLIST_FOREACH(child, &dev->child_bus, sibling) { ret = qbus_find_recursive(child, name, bus_typename); - if (ret) { - return ret; + if (ret && !qbus_is_full(ret)) { + return ret; /* a descendant matches and isn't full */ + } + if (ret && !pick) { + pick = ret; } } } - return NULL; + + /* root or a descendant matches, but is full */ + return pick; } static BusState *qbus_find(const char *path) @@ -423,6 +435,10 @@ static BusState *qbus_find(const char *path) if (!bus) { qerror_report(QERR_BUS_NOT_FOUND, elem); return NULL; + } else if (qbus_is_full(bus)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "Bus '%s' is full", + elem); + return NULL; } pos = len; } @@ -529,7 +545,7 @@ DeviceState *qdev_device_add(QemuOpts *opts) } } else if (dc->bus_type != NULL) { bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); - if (!bus) { + if (!bus || qbus_is_full(bus)) { qerror_report(ERROR_CLASS_GENERIC_ERROR, "No '%s' bus found for device '%s'", dc->bus_type, driver); -- cgit v1.2.3