diff mbox series

[RFC,v2,10/15] hw/machine: Build smp topology tree from -smp

Message ID 20240919015533.766754-11-zhao1.liu@intel.com (mailing list archive)
State New
Headers show
Series qom-topo: Abstract CPU Topology Level to Topology Device | expand

Commit Message

Zhao Liu Sept. 19, 2024, 1:55 a.m. UTC
For architectures supports QOM topology (indicated by the MachineClass.
topo_tree_supported field), implement smp QOM topology tree from
MachineState.smp.

The topology tree is created before MachineClass.init(), where arch
will initialize CPUs or cores, corresponding to the
MachineState.possible_cpus[].

To avoid conflicts with CPU/core generation in the arch machine,
create_smp_topo_children() will only create topology levels which
are higher than the granularity of possible_cpus[]. The remaining
topology parts will be completed by the arch machine during machine
init().

There's a new field, arch_id_topo_level, to indicate the granularity of
possible_cpus[]. While this field is set, CPU slot can create the
topology tree level by level. Without this field, any topology device
will be collect at the CPU bus of the CPU slot and will not be organized
into a tree structure.

Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
---
 hw/core/machine.c         |   5 ++
 hw/cpu/cpu-slot.c         | 153 ++++++++++++++++++++++++++++++++++++++
 include/hw/boards.h       |   2 +
 include/hw/cpu/cpu-slot.h |   5 ++
 include/qemu/bitops.h     |   5 ++
 5 files changed, 170 insertions(+)
diff mbox series

Patch

diff --git a/hw/core/machine.c b/hw/core/machine.c
index b6258d95b1e8..076bd365197b 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -1638,6 +1638,11 @@  void machine_run_board_init(MachineState *machine, const char *mem_path, Error *
                                    "on", false);
     }
 
+    if (machine_class->smp_props.topo_tree_supported &&
+        !machine_create_topo_tree(machine, errp)) {
+        return;
+    }
+
     accel_init_interfaces(ACCEL_GET_CLASS(machine->accelerator));
     machine_class->init(machine);
     phase_advance(PHASE_MACHINE_INITIALIZED);
diff --git a/hw/cpu/cpu-slot.c b/hw/cpu/cpu-slot.c
index 4dbd5b7b7e00..1cc3b32ed675 100644
--- a/hw/cpu/cpu-slot.c
+++ b/hw/cpu/cpu-slot.c
@@ -12,8 +12,12 @@ 
 #include "qemu/osdep.h"
 
 #include "hw/boards.h"
+#include "hw/cpu/core.h"
 #include "hw/cpu/cpu-slot.h"
 #include "hw/cpu/cpu-topology.h"
+#include "hw/cpu/die.h"
+#include "hw/cpu/module.h"
+#include "hw/cpu/socket.h"
 #include "hw/qdev-core.h"
 #include "hw/qdev-properties.h"
 #include "hw/sysbus.h"
@@ -172,3 +176,152 @@  void machine_plug_cpu_slot(MachineState *ms)
         qbus_set_hotplug_handler(BUS(&slot->bus), OBJECT(ms));
     }
 }
+
+static int get_smp_info_by_level(const CpuTopology *smp_info,
+                                 CpuTopologyLevel child_level)
+{
+    switch (child_level) {
+    case CPU_TOPOLOGY_LEVEL_THREAD:
+        return smp_info->threads;
+    case CPU_TOPOLOGY_LEVEL_CORE:
+        return smp_info->cores;
+    case CPU_TOPOLOGY_LEVEL_MODULE:
+        return smp_info->modules;
+    case CPU_TOPOLOGY_LEVEL_DIE:
+        return smp_info->dies;
+    case CPU_TOPOLOGY_LEVEL_SOCKET:
+        return smp_info->sockets;
+    default:
+        /* TODO: Add support for other levels. */
+        g_assert_not_reached();
+    }
+
+    return 0;
+}
+
+static const char *get_topo_typename_by_level(CpuTopologyLevel level)
+{
+    switch (level) {
+    case CPU_TOPOLOGY_LEVEL_CORE:
+        return TYPE_CPU_CORE;
+    case CPU_TOPOLOGY_LEVEL_MODULE:
+        return TYPE_CPU_MODULE;
+    case CPU_TOPOLOGY_LEVEL_DIE:
+        return TYPE_CPU_DIE;
+    case CPU_TOPOLOGY_LEVEL_SOCKET:
+        return TYPE_CPU_SOCKET;
+    default:
+        /* TODO: Add support for other levels. */
+        g_assert_not_reached();
+    }
+
+    return NULL;
+}
+
+typedef struct SMPBuildCbData {
+    DECLARE_BITMAP(create_levels, CPU_TOPOLOGY_LEVEL__MAX);
+    const CpuTopology *smp_info;
+    CPUTopoStat *stat;
+    Error **errp;
+} SMPBuildCbData;
+
+static int create_smp_topo_children(DeviceState *dev, void *opaque)
+{
+    Object *parent = OBJECT(dev);
+    CpuTopologyLevel child_level;
+    SMPBuildCbData *cb = opaque;
+    CPUTopoState *topo = NULL;
+    BusState *qbus;
+    CPUBusState *cbus;
+    Error **errp = cb->errp;
+    int max_children;
+
+    if (object_dynamic_cast(parent, TYPE_CPU_TOPO)) {
+        topo = CPU_TOPO(parent);
+        CpuTopologyLevel parent_level;
+
+        parent_level = GET_CPU_TOPO_LEVEL(topo);
+        child_level = find_last_bit(cb->create_levels, parent_level);
+
+        if (child_level == parent_level) {
+            return TOPO_FOREACH_CONTINUE;
+        }
+
+        cbus = topo->bus;
+    } else if (object_dynamic_cast(parent, TYPE_CPU_SLOT)) {
+        child_level = find_last_bit(cb->create_levels, CPU_TOPOLOGY_LEVEL__MAX);
+        cbus = &CPU_SLOT(parent)->bus;
+    } else {
+        return TOPO_FOREACH_ERR;
+    }
+
+    qbus = BUS(cbus);
+    max_children = get_smp_info_by_level(cb->smp_info, child_level);
+    for (int i = 0; i < max_children; i++) {
+        DeviceState *child;
+
+        child = qdev_new(get_topo_typename_by_level(child_level));
+
+        /*
+         * Bus inserts child device at head (QTAILQ_INSERT_HEAD_RCU), This
+         * could result in the device IDs in the created topology having a
+         * zig-zag arrangement.
+         *
+         * TODO: Remove obstacles preventing the use of QTAILQ_INSERT_HEAD_RCU
+         * for bus to insert kid device.
+         */
+        child->id = g_strdup_printf("%s[%d]",
+            CpuTopologyLevel_str(child_level),
+            cb->stat->entries[child_level].total_instances);
+
+        if (!qdev_realize_and_unref(child, qbus, errp)) {
+            return TOPO_FOREACH_ERR;
+        }
+    }
+
+    return TOPO_FOREACH_CONTINUE;
+}
+
+bool machine_create_topo_tree(MachineState *ms, Error **errp)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    CPUSlot *slot = ms->topo;
+    CpuTopologyLevel level;
+    SMPBuildCbData cb;
+
+    if (!slot) {
+        error_setg(errp, "Invalid machine: "
+                   "the cpu-slot of machine is not initialized.");
+        return false;
+    }
+
+    /*
+     * Don't support full topology tree.
+     * Just use slot to collect topology device.
+     */
+    if (!mc->smp_props.arch_id_topo_level) {
+        return true;
+    }
+
+    bitmap_copy(cb.create_levels, slot->supported_levels,
+                CPU_TOPOLOGY_LEVEL__MAX);
+    cb.smp_info = &ms->smp;
+    cb.stat = &slot->stat;
+    cb.errp = errp;
+
+    /*
+     * Topology objects at arch_id_topo_level and lower levels will be
+     * created by MachineClass.possible_cpu_arch_ids().
+     */
+    FOR_EACH_SET_BIT(level, slot->supported_levels,
+                     mc->smp_props.arch_id_topo_level + 1) {
+        clear_bit(level, cb.create_levels);
+    }
+
+    if (qdev_walk_children(DEVICE(slot), create_smp_topo_children,
+                           NULL, NULL, NULL, &cb) < 0) {
+        return false;
+    }
+
+    return true;
+}
diff --git a/include/hw/boards.h b/include/hw/boards.h
index eeb4e7e2ce9f..a49677466ef6 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -155,6 +155,7 @@  typedef struct {
  *                    supported by the machine
  * @topo_tree_supported - whether QOM topology tree is supported by the
  *                        machine
+ * @arch_id_topo_level - topology granularity for possible_cpus[]
  */
 typedef struct {
     bool prefer_sockets;
@@ -166,6 +167,7 @@  typedef struct {
     bool modules_supported;
     bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX];
     bool topo_tree_supported;
+    CpuTopologyLevel arch_id_topo_level;
 } SMPCompatProps;
 
 /**
diff --git a/include/hw/cpu/cpu-slot.h b/include/hw/cpu/cpu-slot.h
index 24e122013bf7..1838e8c0c3f9 100644
--- a/include/hw/cpu/cpu-slot.h
+++ b/include/hw/cpu/cpu-slot.h
@@ -69,6 +69,11 @@  struct CPUSlot {
     DeviceListener listener;
 };
 
+#define TOPO_FOREACH_END             1
+#define TOPO_FOREACH_CONTINUE        0
+#define TOPO_FOREACH_ERR             -1
+
 void machine_plug_cpu_slot(MachineState *ms);
+bool machine_create_topo_tree(MachineState *ms, Error **errp);
 
 #endif /* CPU_SLOT_H */
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 2c0a2fe7512d..d1c0e52219de 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -631,4 +631,9 @@  static inline uint64_t half_unshuffle64(uint64_t x)
     return x;
 }
 
+#define FOR_EACH_SET_BIT(bit, addr, size)               \
+    for ((bit) = find_first_bit((addr), (size));        \
+         (bit) < (size);                                \
+         (bit) = find_next_bit((addr), (size), (bit) + 1))
+
 #endif