]>
Commit | Line | Data |
---|---|---|
83fcaefd AP |
1 | /* |
2 | * QEMU RISC-V NUMA Helper | |
3 | * | |
4 | * Copyright (c) 2020 Western Digital Corporation or its affiliates. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2 or later, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "qemu/osdep.h" | |
20 | #include "qemu/units.h" | |
83fcaefd AP |
21 | #include "qemu/error-report.h" |
22 | #include "qapi/error.h" | |
23 | #include "hw/boards.h" | |
24 | #include "hw/qdev-properties.h" | |
25 | #include "hw/riscv/numa.h" | |
26 | #include "sysemu/device_tree.h" | |
27 | ||
28 | static bool numa_enabled(const MachineState *ms) | |
29 | { | |
30 | return (ms->numa_state && ms->numa_state->num_nodes) ? true : false; | |
31 | } | |
32 | ||
33 | int riscv_socket_count(const MachineState *ms) | |
34 | { | |
35 | return (numa_enabled(ms)) ? ms->numa_state->num_nodes : 1; | |
36 | } | |
37 | ||
38 | int riscv_socket_first_hartid(const MachineState *ms, int socket_id) | |
39 | { | |
40 | int i, first_hartid = ms->smp.cpus; | |
41 | ||
42 | if (!numa_enabled(ms)) { | |
43 | return (!socket_id) ? 0 : -1; | |
44 | } | |
45 | ||
46 | for (i = 0; i < ms->smp.cpus; i++) { | |
47 | if (ms->possible_cpus->cpus[i].props.node_id != socket_id) { | |
48 | continue; | |
49 | } | |
50 | if (i < first_hartid) { | |
51 | first_hartid = i; | |
52 | } | |
53 | } | |
54 | ||
55 | return (first_hartid < ms->smp.cpus) ? first_hartid : -1; | |
56 | } | |
57 | ||
58 | int riscv_socket_last_hartid(const MachineState *ms, int socket_id) | |
59 | { | |
60 | int i, last_hartid = -1; | |
61 | ||
62 | if (!numa_enabled(ms)) { | |
63 | return (!socket_id) ? ms->smp.cpus - 1 : -1; | |
64 | } | |
65 | ||
66 | for (i = 0; i < ms->smp.cpus; i++) { | |
67 | if (ms->possible_cpus->cpus[i].props.node_id != socket_id) { | |
68 | continue; | |
69 | } | |
70 | if (i > last_hartid) { | |
71 | last_hartid = i; | |
72 | } | |
73 | } | |
74 | ||
75 | return (last_hartid < ms->smp.cpus) ? last_hartid : -1; | |
76 | } | |
77 | ||
78 | int riscv_socket_hart_count(const MachineState *ms, int socket_id) | |
79 | { | |
80 | int first_hartid, last_hartid; | |
81 | ||
82 | if (!numa_enabled(ms)) { | |
83 | return (!socket_id) ? ms->smp.cpus : -1; | |
84 | } | |
85 | ||
86 | first_hartid = riscv_socket_first_hartid(ms, socket_id); | |
87 | if (first_hartid < 0) { | |
88 | return -1; | |
89 | } | |
90 | ||
91 | last_hartid = riscv_socket_last_hartid(ms, socket_id); | |
92 | if (last_hartid < 0) { | |
93 | return -1; | |
94 | } | |
95 | ||
96 | if (first_hartid > last_hartid) { | |
97 | return -1; | |
98 | } | |
99 | ||
100 | return last_hartid - first_hartid + 1; | |
101 | } | |
102 | ||
103 | bool riscv_socket_check_hartids(const MachineState *ms, int socket_id) | |
104 | { | |
105 | int i, first_hartid, last_hartid; | |
106 | ||
107 | if (!numa_enabled(ms)) { | |
108 | return (!socket_id) ? true : false; | |
109 | } | |
110 | ||
111 | first_hartid = riscv_socket_first_hartid(ms, socket_id); | |
112 | if (first_hartid < 0) { | |
113 | return false; | |
114 | } | |
115 | ||
116 | last_hartid = riscv_socket_last_hartid(ms, socket_id); | |
117 | if (last_hartid < 0) { | |
118 | return false; | |
119 | } | |
120 | ||
121 | for (i = first_hartid; i <= last_hartid; i++) { | |
122 | if (ms->possible_cpus->cpus[i].props.node_id != socket_id) { | |
123 | return false; | |
124 | } | |
125 | } | |
126 | ||
127 | return true; | |
128 | } | |
129 | ||
130 | uint64_t riscv_socket_mem_offset(const MachineState *ms, int socket_id) | |
131 | { | |
132 | int i; | |
133 | uint64_t mem_offset = 0; | |
134 | ||
135 | if (!numa_enabled(ms)) { | |
136 | return 0; | |
137 | } | |
138 | ||
139 | for (i = 0; i < ms->numa_state->num_nodes; i++) { | |
140 | if (i == socket_id) { | |
141 | break; | |
142 | } | |
143 | mem_offset += ms->numa_state->nodes[i].node_mem; | |
144 | } | |
145 | ||
146 | return (i == socket_id) ? mem_offset : 0; | |
147 | } | |
148 | ||
149 | uint64_t riscv_socket_mem_size(const MachineState *ms, int socket_id) | |
150 | { | |
151 | if (!numa_enabled(ms)) { | |
152 | return (!socket_id) ? ms->ram_size : 0; | |
153 | } | |
154 | ||
155 | return (socket_id < ms->numa_state->num_nodes) ? | |
156 | ms->numa_state->nodes[socket_id].node_mem : 0; | |
157 | } | |
158 | ||
fb60b488 DHB |
159 | void riscv_socket_fdt_write_id(const MachineState *ms, const char *node_name, |
160 | int socket_id) | |
83fcaefd AP |
161 | { |
162 | if (numa_enabled(ms)) { | |
fb60b488 | 163 | qemu_fdt_setprop_cell(ms->fdt, node_name, "numa-node-id", socket_id); |
83fcaefd AP |
164 | } |
165 | } | |
166 | ||
9c3ee7e8 | 167 | void riscv_socket_fdt_write_distance_matrix(const MachineState *ms) |
83fcaefd AP |
168 | { |
169 | int i, j, idx; | |
170 | uint32_t *dist_matrix, dist_matrix_size; | |
171 | ||
172 | if (numa_enabled(ms) && ms->numa_state->have_numa_distance) { | |
173 | dist_matrix_size = riscv_socket_count(ms) * riscv_socket_count(ms); | |
174 | dist_matrix_size *= (3 * sizeof(uint32_t)); | |
175 | dist_matrix = g_malloc0(dist_matrix_size); | |
176 | ||
177 | for (i = 0; i < riscv_socket_count(ms); i++) { | |
178 | for (j = 0; j < riscv_socket_count(ms); j++) { | |
179 | idx = (i * riscv_socket_count(ms) + j) * 3; | |
180 | dist_matrix[idx + 0] = cpu_to_be32(i); | |
181 | dist_matrix[idx + 1] = cpu_to_be32(j); | |
182 | dist_matrix[idx + 2] = | |
183 | cpu_to_be32(ms->numa_state->nodes[i].distance[j]); | |
184 | } | |
185 | } | |
186 | ||
9c3ee7e8 DHB |
187 | qemu_fdt_add_subnode(ms->fdt, "/distance-map"); |
188 | qemu_fdt_setprop_string(ms->fdt, "/distance-map", "compatible", | |
83fcaefd | 189 | "numa-distance-map-v1"); |
9c3ee7e8 | 190 | qemu_fdt_setprop(ms->fdt, "/distance-map", "distance-matrix", |
83fcaefd AP |
191 | dist_matrix, dist_matrix_size); |
192 | g_free(dist_matrix); | |
193 | } | |
194 | } | |
195 | ||
196 | CpuInstanceProperties | |
197 | riscv_numa_cpu_index_to_props(MachineState *ms, unsigned cpu_index) | |
198 | { | |
199 | MachineClass *mc = MACHINE_GET_CLASS(ms); | |
200 | const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); | |
201 | ||
202 | assert(cpu_index < possible_cpus->len); | |
203 | return possible_cpus->cpus[cpu_index].props; | |
204 | } | |
205 | ||
206 | int64_t riscv_numa_get_default_cpu_node_id(const MachineState *ms, int idx) | |
207 | { | |
208 | int64_t nidx = 0; | |
209 | ||
b9cedbf1 YW |
210 | if (ms->numa_state->num_nodes > ms->smp.cpus) { |
211 | error_report("Number of NUMA nodes (%d)" | |
a916dc95 ZL |
212 | " cannot exceed the number of available CPUs (%u).", |
213 | ms->numa_state->num_nodes, ms->smp.cpus); | |
b9cedbf1 YW |
214 | exit(EXIT_FAILURE); |
215 | } | |
83fcaefd AP |
216 | if (ms->numa_state->num_nodes) { |
217 | nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); | |
218 | if (ms->numa_state->num_nodes <= nidx) { | |
219 | nidx = ms->numa_state->num_nodes - 1; | |
220 | } | |
221 | } | |
222 | ||
223 | return nidx; | |
224 | } | |
225 | ||
226 | const CPUArchIdList *riscv_numa_possible_cpu_arch_ids(MachineState *ms) | |
227 | { | |
228 | int n; | |
229 | unsigned int max_cpus = ms->smp.max_cpus; | |
230 | ||
231 | if (ms->possible_cpus) { | |
232 | assert(ms->possible_cpus->len == max_cpus); | |
233 | return ms->possible_cpus; | |
234 | } | |
235 | ||
236 | ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + | |
237 | sizeof(CPUArchId) * max_cpus); | |
238 | ms->possible_cpus->len = max_cpus; | |
239 | for (n = 0; n < ms->possible_cpus->len; n++) { | |
240 | ms->possible_cpus->cpus[n].type = ms->cpu_type; | |
241 | ms->possible_cpus->cpus[n].arch_id = n; | |
242 | ms->possible_cpus->cpus[n].props.has_core_id = true; | |
243 | ms->possible_cpus->cpus[n].props.core_id = n; | |
244 | } | |
245 | ||
246 | return ms->possible_cpus; | |
247 | } |