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/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 159 void riscv_socket_fdt_write_id(const MachineState *ms, const char *node_name, 160 int socket_id) 161 { 162 if (numa_enabled(ms)) { 163 qemu_fdt_setprop_cell(ms->fdt, node_name, "numa-node-id", socket_id); 164 } 165 } 166 167 void riscv_socket_fdt_write_distance_matrix(const MachineState *ms) 168 { 169 int i, j, idx; 170 g_autofree uint32_t *dist_matrix = NULL; 171 uint32_t 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(ms->fdt, "/distance-map"); 189 qemu_fdt_setprop_string(ms->fdt, "/distance-map", "compatible", 190 "numa-distance-map-v1"); 191 qemu_fdt_setprop(ms->fdt, "/distance-map", "distance-matrix", 192 dist_matrix, dist_matrix_size); 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 210 if (ms->numa_state->num_nodes > ms->smp.cpus) { 211 error_report("Number of NUMA nodes (%d)" 212 " cannot exceed the number of available CPUs (%u).", 213 ms->numa_state->num_nodes, ms->smp.cpus); 214 exit(EXIT_FAILURE); 215 } 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 } 248