qemu/hw/riscv/numa.c
<<
>>
Prefs
   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
  28static bool numa_enabled(const MachineState *ms)
  29{
  30    return (ms->numa_state && ms->numa_state->num_nodes) ? true : false;
  31}
  32
  33int riscv_socket_count(const MachineState *ms)
  34{
  35    return (numa_enabled(ms)) ? ms->numa_state->num_nodes : 1;
  36}
  37
  38int 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
  58int 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
  78int 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
 103bool 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
 130uint64_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
 149uint64_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
 159void 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
 167void riscv_socket_fdt_write_distance_matrix(const MachineState *ms)
 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
 187        qemu_fdt_add_subnode(ms->fdt, "/distance-map");
 188        qemu_fdt_setprop_string(ms->fdt, "/distance-map", "compatible",
 189                                "numa-distance-map-v1");
 190        qemu_fdt_setprop(ms->fdt, "/distance-map", "distance-matrix",
 191                         dist_matrix, dist_matrix_size);
 192        g_free(dist_matrix);
 193    }
 194}
 195
 196CpuInstanceProperties
 197riscv_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
 206int64_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 (%d).",
 213                     ms->numa_state->num_nodes, ms->smp.max_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
 226const 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