[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2 4/6] xen/cpupool: Create different cpupools at boot time



On 10.03.22 18:10, Luca Fancellu wrote:
Introduce a way to create different cpupools at boot time, this is
particularly useful on ARM big.LITTLE system where there might be the
need to have different cpupools for each type of core, but also
systems using NUMA can have different cpu pools for each node.

The feature on arm relies on a specification of the cpupools from the
device tree to build pools and assign cpus to them.

Documentation is created to explain the feature.

Signed-off-by: Luca Fancellu <luca.fancellu@xxxxxxx>
---
Changes in v2:
- Move feature to common code (Juergen)
- Try to decouple dtb parse and cpupool creation to allow
   more way to specify cpupools (for example command line)
- Created standalone dt node for the scheduler so it can
   be used in future work to set scheduler specific
   parameters
- Use only auto generated ids for cpupools
---
  docs/misc/arm/device-tree/cpupools.txt | 156 ++++++++++++++++++
  xen/common/Kconfig                     |   8 +
  xen/common/Makefile                    |   1 +
  xen/common/boot_cpupools.c             | 212 +++++++++++++++++++++++++
  xen/common/sched/cpupool.c             |   6 +-
  xen/include/xen/sched.h                |  19 +++
  6 files changed, 401 insertions(+), 1 deletion(-)
  create mode 100644 docs/misc/arm/device-tree/cpupools.txt
  create mode 100644 xen/common/boot_cpupools.c

diff --git a/docs/misc/arm/device-tree/cpupools.txt 
b/docs/misc/arm/device-tree/cpupools.txt
new file mode 100644
index 000000000000..d5a82ed0d45a
--- /dev/null
+++ b/docs/misc/arm/device-tree/cpupools.txt
@@ -0,0 +1,156 @@
+Boot time cpupools
+==================
+
+When BOOT_TIME_CPUPOOLS is enabled in the Xen configuration, it is possible to
+create cpupools during boot phase by specifying them in the device tree.
+
+Cpupools specification nodes shall be direct childs of /chosen node.
+Each cpupool node contains the following properties:
+
+- compatible (mandatory)
+
+    Must always include the compatiblity string: "xen,cpupool".
+
+- cpupool-cpus (mandatory)
+
+    Must be a list of device tree phandle to nodes describing cpus (e.g. having
+    device_type = "cpu"), it can't be empty.
+
+- cpupool-sched (optional)
+
+    Must be a device tree phandle to a node having "xen,scheduler" compatible
+    (description below), it has no effect when the cpupool refers to the 
cpupool
+    number zero, in that case the default Xen scheduler is selected 
(sched=<...>
+    boot argument).
+
+
+A scheduler specification node is a device tree node that contains the 
following
+properties:
+
+- compatible (mandatory)
+
+    Must always include the compatiblity string: "xen,scheduler".
+
+- sched-name (mandatory)
+
+    Must be a string having the name of a Xen scheduler, check the sched=<...>
+    boot argument for allowed values.
+
+
+Constraints
+===========
+
+If no cpupools are specified, all cpus will be assigned to one cpupool
+implicitly created (Pool-0).
+
+If cpupools node are specified, but not every cpu brought up by Xen is 
assigned,
+all the not assigned cpu will be assigned to an additional cpupool.
+
+If a cpu is assigned to a cpupool, but it's not brought up correctly, Xen will
+stop.
+
+
+Examples
+========
+
+A system having two types of core, the following device tree specification will
+instruct Xen to have two cpupools:
+
+- The cpupool with id 0 will have 4 cpus assigned.
+- The cpupool with id 1 will have 2 cpus assigned.
+
+The following example can work only if hmp-unsafe=1 is passed to Xen boot
+arguments, otherwise not all cores will be brought up by Xen and the cpupool
+creation process will stop Xen.
+
+
+a72_1: cpu@0 {
+        compatible = "arm,cortex-a72";
+        reg = <0x0 0x0>;
+        device_type = "cpu";
+        [...]
+};
+
+a72_2: cpu@1 {
+        compatible = "arm,cortex-a72";
+        reg = <0x0 0x1>;
+        device_type = "cpu";
+        [...]
+};
+
+a53_1: cpu@100 {
+        compatible = "arm,cortex-a53";
+        reg = <0x0 0x100>;
+        device_type = "cpu";
+        [...]
+};
+
+a53_2: cpu@101 {
+        compatible = "arm,cortex-a53";
+        reg = <0x0 0x101>;
+        device_type = "cpu";
+        [...]
+};
+
+a53_3: cpu@102 {
+        compatible = "arm,cortex-a53";
+        reg = <0x0 0x102>;
+        device_type = "cpu";
+        [...]
+};
+
+a53_4: cpu@103 {
+        compatible = "arm,cortex-a53";
+        reg = <0x0 0x103>;
+        device_type = "cpu";
+        [...]
+};
+
+chosen {
+
+    sched: sched_a {
+        compatible = "xen,scheduler";
+        sched-name = "credit2";
+    };
+    cpupool_a {
+        compatible = "xen,cpupool";
+        cpupool-cpus = <&a53_1 &a53_2 &a53_3 &a53_4>;
+    };
+    cpupool_b {
+        compatible = "xen,cpupool";
+        cpupool-cpus = <&a72_1 &a72_2>;
+        cpupool-sched = <&sched>;
+    };
+
+    [...]
+
+};
+
+
+A system having the cpupools specification below will instruct Xen to have 
three
+cpupools:
+
+- The cpupool Pool-0 will have 2 cpus assigned.
+- The cpupool Pool-1 will have 2 cpus assigned.
+- The cpupool Pool-2 will have 2 cpus assigned (created by Xen with all the not
+  assigned cpus a53_3 and a53_4).
+
+chosen {
+
+    sched: sched_a {
+        compatible = "xen,scheduler";
+        sched-name = "null";
+    };
+    cpupool_a {
+        compatible = "xen,cpupool";
+        cpupool-cpus = <&a53_1 &a53_2>;
+    };
+    cpupool_b {
+        compatible = "xen,cpupool";
+        cpupool-cpus = <&a72_1 &a72_2>;
+        cpupool-sched = <&sched>;
+    };
+
+    [...]
+
+};
\ No newline at end of file
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 64439438891c..dc9eed31682f 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -22,6 +22,14 @@ config GRANT_TABLE
If unsure, say Y. +config BOOT_TIME_CPUPOOLS
+       bool "Create cpupools at boot time"
+       depends on HAS_DEVICE_TREE
+       default n
+       help
+         Creates cpupools during boot time and assigns cpus to them. Cpupools
+         options can be specified in the device tree.
+
  config ALTERNATIVE_CALL
        bool
diff --git a/xen/common/Makefile b/xen/common/Makefile
index dc8d3a13f5b8..c5949785ab28 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -1,5 +1,6 @@
  obj-$(CONFIG_ARGO) += argo.o
  obj-y += bitmap.o
+obj-$(CONFIG_BOOT_TIME_CPUPOOLS) += boot_cpupools.o
  obj-$(CONFIG_HYPFS_CONFIG) += config_data.o
  obj-$(CONFIG_CORE_PARKING) += core_parking.o
  obj-y += cpu.o
diff --git a/xen/common/boot_cpupools.c b/xen/common/boot_cpupools.c
new file mode 100644
index 000000000000..e8529a902d21
--- /dev/null
+++ b/xen/common/boot_cpupools.c
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * xen/common/boot_cpupools.c
+ *
+ * Code to create cpupools at boot time for arm architecture.

Please drop the arm reference here.

+ *
+ * Copyright (C) 2022 Arm Ltd.
+ */
+
+#include <xen/sched.h>
+
+#define BTCPUPOOLS_DT_NODE_NO_REG     (-1)
+#define BTCPUPOOLS_DT_NODE_NO_LOG_CPU (-2)

Move those inside the #ifdef below, please

+
+struct pool_map {
+    int pool_id;
+    int sched_id;
+    struct cpupool *pool;
+};
+
+static struct pool_map __initdata pool_cpu_map[NR_CPUS] =
+    { [0 ... NR_CPUS-1] = {.pool_id = -1, .sched_id = -1, .pool = NULL} };
+static unsigned int __initdata next_pool_id;
+
+#ifdef CONFIG_ARM

Shouldn't this be CONFIG_HAS_DEVICE_TREE?

+static int __init get_logical_cpu_from_hw_id(unsigned int hwid)
+{
+    unsigned int i;
+
+    for ( i = 0; i < nr_cpu_ids; i++ )
+        if ( cpu_logical_map(i) == hwid )
+            return i;
+
+    return -1;
+}
+
+static int __init
+get_logical_cpu_from_cpu_node(const struct dt_device_node *cpu_node)
+{
+    unsigned int cpu_reg, cpu_num;
+    const __be32 *prop;
+
+    prop = dt_get_property(cpu_node, "reg", NULL);
+    if ( !prop )
+        return BTCPUPOOLS_DT_NODE_NO_REG;
+
+    cpu_reg = dt_read_number(prop, dt_n_addr_cells(cpu_node));
+
+    cpu_num = get_logical_cpu_from_hw_id(cpu_reg);
+    if ( cpu_num < 0 )
+        return BTCPUPOOLS_DT_NODE_NO_LOG_CPU;
+
+    return cpu_num;
+}
+
+static int __init check_and_get_sched_id(const char* scheduler_name)
+{
+    int sched_id = sched_get_id_by_name(scheduler_name);
+
+    if ( sched_id < 0 )
+        panic("Scheduler %s does not exists!\n", scheduler_name);
+
+    return sched_id;
+}
+
+void __init btcpupools_dtb_parse(void)
+{
+    const struct dt_device_node *chosen, *node;
+
+    chosen = dt_find_node_by_path("/chosen");
+    if ( !chosen )
+        return;
+
+    dt_for_each_child_node(chosen, node)
+    {
+        const struct dt_device_node *phandle_node;
+        int sched_id = -1;
+        const char* scheduler_name;
+        unsigned int i = 0;
+
+        if ( !dt_device_is_compatible(node, "xen,cpupool") )
+            continue;
+
+        phandle_node = dt_parse_phandle(node, "cpupool-sched", 0);
+        if ( phandle_node )
+        {
+            if ( !dt_device_is_compatible(phandle_node, "xen,scheduler") )
+                panic("cpupool-sched must be a xen,scheduler compatible"
+                      "node!\n");
+            if ( !dt_property_read_string(phandle_node, "sched-name",
+                                          &scheduler_name) )
+                sched_id = check_and_get_sched_id(scheduler_name);
+            else
+                panic("Error trying to read sched-name in %s!\n",
+                      dt_node_name(phandle_node));
+        }
+
+        phandle_node = dt_parse_phandle(node, "cpupool-cpus", i++);
+        if ( !phandle_node )
+            panic("Missing or empty cpupool-cpus property!\n");
+
+        while ( phandle_node )
+        {
+            int cpu_num;
+
+            cpu_num = get_logical_cpu_from_cpu_node(phandle_node);
+
+            if ( cpu_num < 0 )
+                panic("Error retrieving logical cpu from node %s (%d)\n",
+                      dt_node_name(node), cpu_num);
+
+            if ( pool_cpu_map[cpu_num].pool_id != -1 )
+                panic("Logical cpu %d already added to a cpupool!\n", cpu_num);
+
+            pool_cpu_map[cpu_num].pool_id = next_pool_id;
+            pool_cpu_map[cpu_num].sched_id = sched_id;
+
+            phandle_node = dt_parse_phandle(node, "cpupool-cpus", i++);
+        }
+
+        /* Let Xen generate pool ids */
+        next_pool_id++;
+    }
+}
+#endif
+
+void __init btcpupools_allocate_pools(const cpumask_t *cpu_online_map)

Either rename the parameter or drop it completely.

Right now shadowing cpu_online_map is no real problem, because the only
caller is passing the global cpu_online_map, but in case another caller
with different needs would come up, this would be rather confusing.

With the x86 specific loop in this function I don't see how a different
map than the global cpu_online_map could work, so I think dropping the
parameter is the best move.

+{
+    unsigned int cpu_num;
+
+    /*
+     * If there are no cpupools, the value of next_pool_id is zero, so the code
+     * below will assign every cpu to cpupool0 as the default behavior.
+     * When there are cpupools, the code below is assigning all the not
+     * assigned cpu to a new pool (next_pool_id value is the last id + 1).
+     * In the same loop we check if there is any assigned cpu that is not
+     * online.
+     */
+    for ( cpu_num = 0; cpu_num < nr_cpu_ids; cpu_num++ )
+        if ( cpumask_test_cpu(cpu_num, cpu_online_map) )
+        {
+            if ( pool_cpu_map[cpu_num].pool_id < 0 )
+                pool_cpu_map[cpu_num].pool_id = next_pool_id;
+        }
+        else
+            if ( pool_cpu_map[cpu_num].pool_id >= 0 )
+                panic("Pool-%d contains cpu%u that is not online!\n",
+                      pool_cpu_map[cpu_num].pool_id, cpu_num);
+
+#ifdef CONFIG_X86
+    /* Cpu0 must be in cpupool0 for x86 */
+    if ( pool_cpu_map[0].pool_id != 0 )
+    {
+        /* The cpupool containing cpu0 will become cpupool0 */
+        unsigned int swap_id = pool_cpu_map[0].pool_id;
+        for_each_cpu ( cpu_num, cpu_online_map )
+            if ( pool_cpu_map[cpu_num].pool_id == swap_id )
+                pool_cpu_map[cpu_num].pool_id = 0;
+            else if ( pool_cpu_map[cpu_num].pool_id == 0 )
+                pool_cpu_map[cpu_num].pool_id = swap_id;
+    }
+#endif
+
+    for_each_cpu ( cpu_num, cpu_online_map )
+    {
+        struct cpupool *pool = NULL;
+        int pool_id, sched_id;
+
+        pool_id = pool_cpu_map[cpu_num].pool_id;
+        sched_id = pool_cpu_map[cpu_num].sched_id;
+
+        if ( pool_id )
+        {
+            unsigned int i;
+
+            /* Look for previously created pool with id pool_id */
+            for ( i = 0; i < cpu_num; i++ )
+                if ( (pool_cpu_map[i].pool_id == pool_id) &&
+                     pool_cpu_map[i].pool )
+                {
+                    pool = pool_cpu_map[i].pool;
+                    break;
+                }
+
+            /* If no pool was created before, create it */
+            if ( !pool )
+                pool = cpupool_create_pool(pool_id, sched_id);
+            if ( !pool )
+                panic("Error creating pool id %u!\n", pool_id);
+        }
+        else
+            pool = cpupool0;
+
+        pool_cpu_map[cpu_num].pool = pool;
+        printk(XENLOG_INFO "Logical CPU %u in Pool-%u.\n", cpu_num, pool_id);
+    }
+}


Juergen

Attachment: OpenPGP_0xB0DE9DD628BF132F.asc
Description: OpenPGP public key

Attachment: OpenPGP_signature
Description: OpenPGP digital signature


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.