xref: /qemu/hw/riscv/numa.c (revision c418f935)
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"
21 #include "qemu/log.h"
22 #include "qemu/error-report.h"
23 #include "qapi/error.h"
24 #include "hw/boards.h"
25 #include "hw/qdev-properties.h"
26 #include "hw/riscv/numa.h"
27 #include "sysemu/device_tree.h"
28 
29 static bool numa_enabled(const MachineState *ms)
30 {
31     return (ms->numa_state && ms->numa_state->num_nodes) ? true : false;
32 }
33 
34 int riscv_socket_count(const MachineState *ms)
35 {
36     return (numa_enabled(ms)) ? ms->numa_state->num_nodes : 1;
37 }
38 
39 int riscv_socket_first_hartid(const MachineState *ms, int socket_id)
40 {
41     int i, first_hartid = ms->smp.cpus;
42 
43     if (!numa_enabled(ms)) {
44         return (!socket_id) ? 0 : -1;
45     }
46 
47     for (i = 0; i < ms->smp.cpus; i++) {
48         if (ms->possible_cpus->cpus[i].props.node_id != socket_id) {
49             continue;
50         }
51         if (i < first_hartid) {
52             first_hartid = i;
53         }
54     }
55 
56     return (first_hartid < ms->smp.cpus) ? first_hartid : -1;
57 }
58 
59 int riscv_socket_last_hartid(const MachineState *ms, int socket_id)
60 {
61     int i, last_hartid = -1;
62 
63     if (!numa_enabled(ms)) {
64         return (!socket_id) ? ms->smp.cpus - 1 : -1;
65     }
66 
67     for (i = 0; i < ms->smp.cpus; i++) {
68         if (ms->possible_cpus->cpus[i].props.node_id != socket_id) {
69             continue;
70         }
71         if (i > last_hartid) {
72             last_hartid = i;
73         }
74     }
75 
76     return (last_hartid < ms->smp.cpus) ? last_hartid : -1;
77 }
78 
79 int riscv_socket_hart_count(const MachineState *ms, int socket_id)
80 {
81     int first_hartid, last_hartid;
82 
83     if (!numa_enabled(ms)) {
84         return (!socket_id) ? ms->smp.cpus : -1;
85     }
86 
87     first_hartid = riscv_socket_first_hartid(ms, socket_id);
88     if (first_hartid < 0) {
89         return -1;
90     }
91 
92     last_hartid = riscv_socket_last_hartid(ms, socket_id);
93     if (last_hartid < 0) {
94         return -1;
95     }
96 
97     if (first_hartid > last_hartid) {
98         return -1;
99     }
100 
101     return last_hartid - first_hartid + 1;
102 }
103 
104 bool riscv_socket_check_hartids(const MachineState *ms, int socket_id)
105 {
106     int i, first_hartid, last_hartid;
107 
108     if (!numa_enabled(ms)) {
109         return (!socket_id) ? true : false;
110     }
111 
112     first_hartid = riscv_socket_first_hartid(ms, socket_id);
113     if (first_hartid < 0) {
114         return false;
115     }
116 
117     last_hartid = riscv_socket_last_hartid(ms, socket_id);
118     if (last_hartid < 0) {
119         return false;
120     }
121 
122     for (i = first_hartid; i <= last_hartid; i++) {
123         if (ms->possible_cpus->cpus[i].props.node_id != socket_id) {
124             return false;
125         }
126     }
127 
128     return true;
129 }
130 
131 uint64_t riscv_socket_mem_offset(const MachineState *ms, int socket_id)
132 {
133     int i;
134     uint64_t mem_offset = 0;
135 
136     if (!numa_enabled(ms)) {
137         return 0;
138     }
139 
140     for (i = 0; i < ms->numa_state->num_nodes; i++) {
141         if (i == socket_id) {
142             break;
143         }
144         mem_offset += ms->numa_state->nodes[i].node_mem;
145     }
146 
147     return (i == socket_id) ? mem_offset : 0;
148 }
149 
150 uint64_t riscv_socket_mem_size(const MachineState *ms, int socket_id)
151 {
152     if (!numa_enabled(ms)) {
153         return (!socket_id) ? ms->ram_size : 0;
154     }
155 
156     return (socket_id < ms->numa_state->num_nodes) ?
157             ms->numa_state->nodes[socket_id].node_mem : 0;
158 }
159 
160 void riscv_socket_fdt_write_id(const MachineState *ms, void *fdt,
161                                const char *node_name, int socket_id)
162 {
163     if (numa_enabled(ms)) {
164         qemu_fdt_setprop_cell(fdt, node_name, "numa-node-id", socket_id);
165     }
166 }
167 
168 void riscv_socket_fdt_write_distance_matrix(const MachineState *ms, void *fdt)
169 {
170     int i, j, idx;
171     uint32_t *dist_matrix, dist_matrix_size;
172 
173     if (numa_enabled(ms) && ms->numa_state->have_numa_distance) {
174         dist_matrix_size = riscv_socket_count(ms) * riscv_socket_count(ms);
175         dist_matrix_size *= (3 * sizeof(uint32_t));
176         dist_matrix = g_malloc0(dist_matrix_size);
177 
178         for (i = 0; i < riscv_socket_count(ms); i++) {
179             for (j = 0; j < riscv_socket_count(ms); j++) {
180                 idx = (i * riscv_socket_count(ms) + j) * 3;
181                 dist_matrix[idx + 0] = cpu_to_be32(i);
182                 dist_matrix[idx + 1] = cpu_to_be32(j);
183                 dist_matrix[idx + 2] =
184                     cpu_to_be32(ms->numa_state->nodes[i].distance[j]);
185             }
186         }
187 
188         qemu_fdt_add_subnode(fdt, "/distance-map");
189         qemu_fdt_setprop_string(fdt, "/distance-map", "compatible",
190                                 "numa-distance-map-v1");
191         qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix",
192                          dist_matrix, dist_matrix_size);
193         g_free(dist_matrix);
194     }
195 }
196 
197 CpuInstanceProperties
198 riscv_numa_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
199 {
200     MachineClass *mc = MACHINE_GET_CLASS(ms);
201     const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
202 
203     assert(cpu_index < possible_cpus->len);
204     return possible_cpus->cpus[cpu_index].props;
205 }
206 
207 int64_t riscv_numa_get_default_cpu_node_id(const MachineState *ms, int idx)
208 {
209     int64_t nidx = 0;
210 
211     if (ms->numa_state->num_nodes) {
212         nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes);
213         if (ms->numa_state->num_nodes <= nidx) {
214             nidx = ms->numa_state->num_nodes - 1;
215         }
216     }
217 
218     return nidx;
219 }
220 
221 const CPUArchIdList *riscv_numa_possible_cpu_arch_ids(MachineState *ms)
222 {
223     int n;
224     unsigned int max_cpus = ms->smp.max_cpus;
225 
226     if (ms->possible_cpus) {
227         assert(ms->possible_cpus->len == max_cpus);
228         return ms->possible_cpus;
229     }
230 
231     ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
232                                   sizeof(CPUArchId) * max_cpus);
233     ms->possible_cpus->len = max_cpus;
234     for (n = 0; n < ms->possible_cpus->len; n++) {
235         ms->possible_cpus->cpus[n].type = ms->cpu_type;
236         ms->possible_cpus->cpus[n].arch_id = n;
237         ms->possible_cpus->cpus[n].props.has_core_id = true;
238         ms->possible_cpus->cpus[n].props.core_id = n;
239     }
240 
241     return ms->possible_cpus;
242 }
243