| 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/core/boards.h" |
| 24 | #include "hw/core/qdev-properties.h" |
| 25 | #include "hw/riscv/numa.h" |
| 26 | #include "system/device_tree.h" |
| 27 | |
| 28 | bool riscv_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 (riscv_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 (!riscv_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 (!riscv_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 (!riscv_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 (!riscv_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 (!riscv_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 (!riscv_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 (riscv_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 (riscv_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_socket_id = true; |
| 243 | ms->possible_cpus->cpus[n].props.socket_id = |
| 244 | n / (ms->smp.clusters * ms->smp.cores * ms->smp.threads); |
| 245 | ms->possible_cpus->cpus[n].props.has_cluster_id = true; |
| 246 | ms->possible_cpus->cpus[n].props.cluster_id = |
| 247 | (n / (ms->smp.cores * ms->smp.threads)) % ms->smp.clusters; |
| 248 | ms->possible_cpus->cpus[n].props.has_core_id = true; |
| 249 | ms->possible_cpus->cpus[n].props.core_id = |
| 250 | (n / ms->smp.threads) % ms->smp.cores; |
| 251 | ms->possible_cpus->cpus[n].props.has_thread_id = true; |
| 252 | ms->possible_cpus->cpus[n].props.thread_id = |
| 253 | n % ms->smp.threads; |
| 254 | } |
| 255 | |
| 256 | return ms->possible_cpus; |
| 257 | } |