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