1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * xen/common/boot_cpupools.c
4  *
5  * Code to create cpupools at boot time.
6  *
7  * Copyright (C) 2022 Arm Ltd.
8  */
9 
10 #include <xen/bootfdt.h>
11 #include <xen/acpi.h>
12 #include <xen/sched.h>
13 
14 /*
15  * pool_cpu_map:   Index is logical cpu number, content is cpupool id, (-1) for
16  *                 unassigned.
17  * pool_sched_map: Index is cpupool id, content is scheduler id, (-1) for
18  *                 unassigned.
19  */
20 static int __initdata pool_cpu_map[NR_CPUS]   = { [0 ... NR_CPUS-1] = -1 };
21 static int __initdata pool_sched_map[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 };
22 static unsigned int __initdata next_pool_id;
23 
24 #define BTCPUPOOLS_DT_NODE_NO_REG     (-1)
25 #define BTCPUPOOLS_DT_NODE_NO_LOG_CPU (-2)
26 #define BTCPUPOOLS_DT_WRONG_NODE      (-3)
27 #define BTCPUPOOLS_DT_CORRUPTED_NODE  (-4)
28 
get_logical_cpu_from_hw_id(unsigned int hwid)29 static int __init get_logical_cpu_from_hw_id(unsigned int hwid)
30 {
31     unsigned int i;
32 
33     for ( i = 0; i < nr_cpu_ids; i++ )
34     {
35         if ( cpu_physical_id(i) == hwid )
36             return i;
37     }
38 
39     return -1;
40 }
41 
42 static int __init
get_logical_cpu_from_cpu_node(const struct dt_device_node * cpu_node)43 get_logical_cpu_from_cpu_node(const struct dt_device_node *cpu_node)
44 {
45     int cpu_num;
46     const __be32 *prop;
47     unsigned int cpu_reg;
48 
49     prop = dt_get_property(cpu_node, "reg", NULL);
50     if ( !prop )
51         return BTCPUPOOLS_DT_NODE_NO_REG;
52 
53     cpu_reg = dt_read_number(prop, dt_n_addr_cells(cpu_node));
54 
55     cpu_num = get_logical_cpu_from_hw_id(cpu_reg);
56     if ( cpu_num < 0 )
57         return BTCPUPOOLS_DT_NODE_NO_LOG_CPU;
58 
59     return cpu_num;
60 }
61 
btcpupools_get_domain_pool_id(const struct dt_device_node * node)62 int __init btcpupools_get_domain_pool_id(const struct dt_device_node *node)
63 {
64     const struct dt_device_node *phandle_node;
65     int cpu_num;
66 
67     if ( !dt_device_is_compatible(node, "xen,cpupool") )
68         return BTCPUPOOLS_DT_WRONG_NODE;
69     /*
70      * Get first cpu listed in the cpupool, from its reg it's possible to
71      * retrieve the cpupool id.
72      */
73     phandle_node = dt_parse_phandle(node, "cpupool-cpus", 0);
74     if ( !phandle_node )
75         return BTCPUPOOLS_DT_CORRUPTED_NODE;
76 
77     cpu_num = get_logical_cpu_from_cpu_node(phandle_node);
78     if ( cpu_num < 0 )
79         return cpu_num;
80 
81     return pool_cpu_map[cpu_num];
82 }
83 
check_and_get_sched_id(const char * scheduler_name)84 static int __init check_and_get_sched_id(const char* scheduler_name)
85 {
86     int sched_id = sched_get_id_by_name(scheduler_name);
87 
88     if ( sched_id < 0 )
89         panic("Scheduler %s does not exists!\n", scheduler_name);
90 
91     return sched_id;
92 }
93 
btcpupools_dtb_parse(void)94 void __init btcpupools_dtb_parse(void)
95 {
96     const struct dt_device_node *chosen, *node;
97 
98     if ( !acpi_disabled )
99         return;
100 
101     chosen = dt_find_node_by_path("/chosen");
102     if ( !chosen )
103         panic("/chosen missing. Boot time cpupools can't be parsed from DT.\n");
104 
105     dt_for_each_child_node(chosen, node)
106     {
107         const struct dt_device_node *phandle_node;
108         int sched_id = -1;
109         const char* scheduler_name;
110         unsigned int i = 0;
111 
112         if ( !dt_device_is_compatible(node, "xen,cpupool") )
113             continue;
114 
115         if ( !dt_property_read_string(node, "cpupool-sched", &scheduler_name) )
116             sched_id = check_and_get_sched_id(scheduler_name);
117 
118         phandle_node = dt_parse_phandle(node, "cpupool-cpus", i++);
119         if ( !phandle_node )
120             panic("Missing or empty cpupool-cpus property!\n");
121 
122         while ( phandle_node )
123         {
124             int cpu_num;
125 
126             cpu_num = get_logical_cpu_from_cpu_node(phandle_node);
127 
128             if ( cpu_num < 0 )
129                 panic("Error retrieving logical cpu from node %s (%d)\n",
130                       dt_node_name(node), cpu_num);
131 
132             if ( pool_cpu_map[cpu_num] != -1 )
133                 panic("Logical cpu %d already added to a cpupool!\n", cpu_num);
134 
135             pool_cpu_map[cpu_num] = next_pool_id;
136 
137             phandle_node = dt_parse_phandle(node, "cpupool-cpus", i++);
138         }
139 
140         /* Save scheduler choice for this cpupool id */
141         pool_sched_map[next_pool_id] = sched_id;
142 
143         /* Let Xen generate pool ids */
144         next_pool_id++;
145     }
146 }
147 
btcpupools_allocate_pools(void)148 void __init btcpupools_allocate_pools(void)
149 {
150     unsigned int i;
151     bool add_extra_cpupool = false;
152     int swap_id = -1;
153 
154     /*
155      * If there are no cpupools, the value of next_pool_id is zero, so the code
156      * below will assign every cpu to cpupool0 as the default behavior.
157      * When there are cpupools, the code below is assigning all the not
158      * assigned cpu to a new pool (next_pool_id value is the last id + 1).
159      * In the same loop we check if there is any assigned cpu that is not
160      * online.
161      */
162     for ( i = 0; i < nr_cpu_ids; i++ )
163     {
164         if ( cpumask_test_cpu(i, &cpu_online_map) )
165         {
166             /* Unassigned cpu gets next_pool_id pool id value */
167             if ( pool_cpu_map[i] < 0 )
168             {
169                 pool_cpu_map[i] = next_pool_id;
170                 add_extra_cpupool = true;
171             }
172 
173             /*
174              * Cpu0 must be in cpupool0, otherwise some operations like moving
175              * cpus between cpupools, cpu hotplug, destroying cpupools, shutdown
176              * of the host, might not work in a sane way.
177              */
178             if ( !i && (pool_cpu_map[0] != 0) )
179                 swap_id = pool_cpu_map[0];
180 
181             if ( swap_id != -1 )
182             {
183                 if ( pool_cpu_map[i] == swap_id )
184                     pool_cpu_map[i] = 0;
185                 else if ( pool_cpu_map[i] == 0 )
186                     pool_cpu_map[i] = swap_id;
187             }
188         }
189         else
190         {
191             if ( pool_cpu_map[i] >= 0 )
192                 panic("Pool-%d contains cpu%u that is not online!\n",
193                       pool_cpu_map[i], i);
194         }
195     }
196 
197     /* A swap happened, swap schedulers between cpupool id 0 and the other */
198     if ( swap_id != -1 )
199     {
200         int swap_sched = pool_sched_map[swap_id];
201 
202         pool_sched_map[swap_id] = pool_sched_map[0];
203         pool_sched_map[0] = swap_sched;
204     }
205 
206     if ( add_extra_cpupool )
207         next_pool_id++;
208 
209     /* Keep track of cpupool id 0 with the global cpupool0 */
210     cpupool0 = cpupool_create_pool(0, pool_sched_map[0]);
211 
212     /* Create cpupools with selected schedulers */
213     for ( i = 1; i < next_pool_id; i++ )
214         cpupool_create_pool(i, pool_sched_map[i]);
215 }
216 
btcpupools_get_cpupool_id(unsigned int cpu)217 unsigned int __init btcpupools_get_cpupool_id(unsigned int cpu)
218 {
219     ASSERT((cpu < NR_CPUS) && (pool_cpu_map[cpu] >= 0));
220 
221     printk(XENLOG_INFO "Logical CPU %u in Pool-%d (Scheduler id: %d).\n",
222            cpu, pool_cpu_map[cpu], pool_sched_map[pool_cpu_map[cpu]]);
223 
224     return pool_cpu_map[cpu];
225 }
226 
227 /*
228  * Local variables:
229  * mode: C
230  * c-file-style: "BSD"
231  * c-basic-offset: 4
232  * tab-width: 4
233  * indent-tabs-mode: nil
234  * End:
235  */
236