@@ -3281,12 +3281,14 @@ F: hw/core/qdev*
F: hw/core/bus.c
F: hw/core/sysbus.c
F: include/hw/qdev*
+F: include/monitor/bus-finder.h
F: include/monitor/qdev.h
F: include/qom/
F: qapi/qom.json
F: qapi/qdev.json
F: scripts/coccinelle/qom-parent-type.cocci
F: scripts/qom-cast-macro-clean-cocci-gen.py
+F: system/bus-finder.c
F: system/qdev-monitor.c
F: stubs/qdev.c
F: qom/
new file mode 100644
@@ -0,0 +1,41 @@
+/*
+ * Bus finder interface header
+ *
+ * Copyright (C) 2024 Intel Corporation.
+ *
+ * Author: Zhao Liu <zhao1.liu@intel.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+
+#ifndef BUS_FINDER_H
+#define BUS_FINDER_H
+
+#include "hw/qdev-core.h"
+#include "qom/object.h"
+
+#define TYPE_BUS_FINDER "bus-finder"
+
+typedef struct BusFinderClass BusFinderClass;
+DECLARE_CLASS_CHECKERS(BusFinderClass, BUS_FINDER, TYPE_BUS_FINDER)
+#define BUS_FINDER(obj) INTERFACE_CHECK(BusFinder, (obj), TYPE_BUS_FINDER)
+
+typedef struct BusFinder BusFinder;
+
+/**
+ * BusFinderClass:
+ * @find_bus: Method to find bus.
+ */
+struct BusFinderClass {
+ /* <private> */
+ InterfaceClass parent_class;
+
+ /* <public> */
+ BusState *(*find_bus)(DeviceState *dev);
+};
+
+bool is_bus_finder_type(DeviceClass *dc);
+BusState *bus_finder_select_bus(DeviceState *dev);
+
+#endif /* BUS_FINDER_H */
new file mode 100644
@@ -0,0 +1,46 @@
+/*
+ * Bus finder interface
+ *
+ * Copyright (C) 2024 Intel Corporation.
+ *
+ * Author: Zhao Liu <zhao1.liu@intel.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/qdev-core.h"
+#include "monitor/bus-finder.h"
+#include "qom/object.h"
+
+bool is_bus_finder_type(DeviceClass *dc)
+{
+ return !!object_class_dynamic_cast(OBJECT_CLASS(dc), TYPE_BUS_FINDER);
+}
+
+BusState *bus_finder_select_bus(DeviceState *dev)
+{
+ BusFinder *bf = BUS_FINDER(dev);
+ BusFinderClass *bfc = BUS_FINDER_GET_CLASS(bf);
+
+ if (bfc->find_bus) {
+ return bfc->find_bus(dev);
+ }
+
+ return NULL;
+}
+
+static const TypeInfo bus_finder_interface_info = {
+ .name = TYPE_BUS_FINDER,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(BusFinderClass),
+};
+
+static void bus_finder_register_types(void)
+{
+ type_register_static(&bus_finder_interface_info);
+}
+
+type_init(bus_finder_register_types)
@@ -9,6 +9,7 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files(
system_ss.add(files(
'balloon.c',
'bootdevice.c',
+ 'bus-finder.c',
'cpus.c',
'cpu-throttle.c',
'cpu-timers.c',
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "monitor/bus-finder.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
#include "monitor/qdev.h"
@@ -589,6 +590,16 @@ static BusState *qbus_find(const char *path, Error **errp)
return bus;
}
+static inline bool qdev_post_find_bus(DeviceClass *dc)
+{
+ return is_bus_finder_type(dc);
+}
+
+static inline BusState *qdev_find_bus_post_device(DeviceState *dev)
+{
+ return bus_finder_select_bus(dev);
+}
+
/* Takes ownership of @id, will be freed when deleting the device */
const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
{
@@ -630,6 +641,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
char *id;
DeviceState *dev = NULL;
BusState *bus = NULL;
+ bool post_bus = false;
driver = qdict_get_try_str(opts, "driver");
if (!driver) {
@@ -656,11 +668,15 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
return NULL;
}
} else if (dc->bus_type != NULL) {
- bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
- if (!bus || qbus_is_full(bus)) {
- error_setg(errp, "No '%s' bus found for device '%s'",
- dc->bus_type, driver);
- return NULL;
+ if (qdev_post_find_bus(dc)) {
+ post_bus = true; /* Wait for bus-finder to arbitrate. */
+ } else {
+ bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
+ if (!bus || qbus_is_full(bus)) {
+ error_setg(errp, "No '%s' bus found for device '%s'",
+ dc->bus_type, driver);
+ return NULL;
+ }
}
}
@@ -722,6 +738,21 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
goto err_del_dev;
}
+ if (post_bus) {
+ bus = qdev_find_bus_post_device(dev);
+ if (!bus) {
+ error_setg(errp, "No proper '%s' bus found for device '%s'",
+ dc->bus_type, driver);
+ goto err_del_dev;
+ }
+
+ if (phase_check(PHASE_MACHINE_READY) && !qbus_is_hotpluggable(bus)) {
+ error_setg(errp, "Bus '%s' does not support hotplugging",
+ bus->name);
+ goto err_del_dev;
+ }
+ }
+
if (!qdev_realize(dev, bus, errp)) {
goto err_del_dev;
}
Currently, cpu and core is located by topology IDs when plugging. On a topology tree, each topology device will has a CPU bus. Once cpu and core specify the bus_type, it's necessary to find accurate buses for them based on topology IDs (if bus=* is not set in -device). Therefore, we need a way to use traditional topology IDs for locating specific bus in the topology tree. This is the bus-finder interface. With bus-finder, qdev-monitor can locate the bus based on device properties when "bus=*" is not specified. Signed-off-by: Zhao Liu <zhao1.liu@intel.com> --- MAINTAINERS | 2 ++ include/monitor/bus-finder.h | 41 ++++++++++++++++++++++++++++++++ system/bus-finder.c | 46 ++++++++++++++++++++++++++++++++++++ system/meson.build | 1 + system/qdev-monitor.c | 41 ++++++++++++++++++++++++++++---- 5 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 include/monitor/bus-finder.h create mode 100644 system/bus-finder.c