Wanlong Gao | 96d0e26 | 2014-05-14 17:43:05 +0800 | [diff] [blame] | 1 | /* |
| 2 | * NUMA parameter parsing routines |
| 3 | * |
| 4 | * Copyright (c) 2014 Fujitsu Ltd. |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
| 25 | #include "sysemu/sysemu.h" |
| 26 | #include "exec/cpu-common.h" |
| 27 | #include "qemu/bitmap.h" |
| 28 | #include "qom/cpu.h" |
Wanlong Gao | 2b631ec | 2014-05-14 17:43:06 +0800 | [diff] [blame^] | 29 | #include "qemu/error-report.h" |
| 30 | #include "include/exec/cpu-common.h" /* for RAM_ADDR_FMT */ |
Wanlong Gao | 96d0e26 | 2014-05-14 17:43:05 +0800 | [diff] [blame] | 31 | |
| 32 | static void numa_node_parse_cpus(int nodenr, const char *cpus) |
| 33 | { |
| 34 | char *endptr; |
| 35 | unsigned long long value, endvalue; |
| 36 | |
| 37 | /* Empty CPU range strings will be considered valid, they will simply |
| 38 | * not set any bit in the CPU bitmap. |
| 39 | */ |
| 40 | if (!*cpus) { |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | if (parse_uint(cpus, &value, &endptr, 10) < 0) { |
| 45 | goto error; |
| 46 | } |
| 47 | if (*endptr == '-') { |
| 48 | if (parse_uint_full(endptr + 1, &endvalue, 10) < 0) { |
| 49 | goto error; |
| 50 | } |
| 51 | } else if (*endptr == '\0') { |
| 52 | endvalue = value; |
| 53 | } else { |
| 54 | goto error; |
| 55 | } |
| 56 | |
| 57 | if (endvalue >= MAX_CPUMASK_BITS) { |
| 58 | endvalue = MAX_CPUMASK_BITS - 1; |
| 59 | fprintf(stderr, |
| 60 | "qemu: NUMA: A max of %d VCPUs are supported\n", |
| 61 | MAX_CPUMASK_BITS); |
| 62 | } |
| 63 | |
| 64 | if (endvalue < value) { |
| 65 | goto error; |
| 66 | } |
| 67 | |
| 68 | bitmap_set(node_cpumask[nodenr], value, endvalue-value+1); |
| 69 | return; |
| 70 | |
| 71 | error: |
| 72 | fprintf(stderr, "qemu: Invalid NUMA CPU range: %s\n", cpus); |
| 73 | exit(1); |
| 74 | } |
| 75 | |
| 76 | void numa_add(const char *optarg) |
| 77 | { |
| 78 | char option[128]; |
| 79 | char *endptr; |
| 80 | unsigned long long nodenr; |
| 81 | |
| 82 | optarg = get_opt_name(option, 128, optarg, ','); |
| 83 | if (*optarg == ',') { |
| 84 | optarg++; |
| 85 | } |
| 86 | if (!strcmp(option, "node")) { |
| 87 | |
| 88 | if (nb_numa_nodes >= MAX_NODES) { |
| 89 | fprintf(stderr, "qemu: too many NUMA nodes\n"); |
| 90 | exit(1); |
| 91 | } |
| 92 | |
| 93 | if (get_param_value(option, 128, "nodeid", optarg) == 0) { |
| 94 | nodenr = nb_numa_nodes; |
| 95 | } else { |
| 96 | if (parse_uint_full(option, &nodenr, 10) < 0) { |
| 97 | fprintf(stderr, "qemu: Invalid NUMA nodeid: %s\n", option); |
| 98 | exit(1); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | if (nodenr >= MAX_NODES) { |
| 103 | fprintf(stderr, "qemu: invalid NUMA nodeid: %llu\n", nodenr); |
| 104 | exit(1); |
| 105 | } |
| 106 | |
| 107 | if (get_param_value(option, 128, "mem", optarg) == 0) { |
| 108 | node_mem[nodenr] = 0; |
| 109 | } else { |
| 110 | int64_t sval; |
| 111 | sval = strtosz(option, &endptr); |
| 112 | if (sval < 0 || *endptr) { |
| 113 | fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg); |
| 114 | exit(1); |
| 115 | } |
| 116 | node_mem[nodenr] = sval; |
| 117 | } |
| 118 | if (get_param_value(option, 128, "cpus", optarg) != 0) { |
| 119 | numa_node_parse_cpus(nodenr, option); |
| 120 | } |
| 121 | nb_numa_nodes++; |
| 122 | } else { |
| 123 | fprintf(stderr, "Invalid -numa option: %s\n", option); |
| 124 | exit(1); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | void set_numa_nodes(void) |
| 129 | { |
| 130 | if (nb_numa_nodes > 0) { |
Wanlong Gao | 2b631ec | 2014-05-14 17:43:06 +0800 | [diff] [blame^] | 131 | uint64_t numa_total; |
Wanlong Gao | 96d0e26 | 2014-05-14 17:43:05 +0800 | [diff] [blame] | 132 | int i; |
| 133 | |
| 134 | if (nb_numa_nodes > MAX_NODES) { |
| 135 | nb_numa_nodes = MAX_NODES; |
| 136 | } |
| 137 | |
| 138 | /* If no memory size if given for any node, assume the default case |
| 139 | * and distribute the available memory equally across all nodes |
| 140 | */ |
| 141 | for (i = 0; i < nb_numa_nodes; i++) { |
| 142 | if (node_mem[i] != 0) { |
| 143 | break; |
| 144 | } |
| 145 | } |
| 146 | if (i == nb_numa_nodes) { |
| 147 | uint64_t usedmem = 0; |
| 148 | |
| 149 | /* On Linux, the each node's border has to be 8MB aligned, |
| 150 | * the final node gets the rest. |
| 151 | */ |
| 152 | for (i = 0; i < nb_numa_nodes - 1; i++) { |
| 153 | node_mem[i] = (ram_size / nb_numa_nodes) & ~((1 << 23UL) - 1); |
| 154 | usedmem += node_mem[i]; |
| 155 | } |
| 156 | node_mem[i] = ram_size - usedmem; |
| 157 | } |
| 158 | |
Wanlong Gao | 2b631ec | 2014-05-14 17:43:06 +0800 | [diff] [blame^] | 159 | numa_total = 0; |
| 160 | for (i = 0; i < nb_numa_nodes; i++) { |
| 161 | numa_total += node_mem[i]; |
| 162 | } |
| 163 | if (numa_total != ram_size) { |
| 164 | error_report("total memory for NUMA nodes (%" PRIu64 ")" |
| 165 | " should equal RAM size (" RAM_ADDR_FMT ")", |
| 166 | numa_total, ram_size); |
| 167 | exit(1); |
| 168 | } |
| 169 | |
Wanlong Gao | 96d0e26 | 2014-05-14 17:43:05 +0800 | [diff] [blame] | 170 | for (i = 0; i < nb_numa_nodes; i++) { |
| 171 | if (!bitmap_empty(node_cpumask[i], MAX_CPUMASK_BITS)) { |
| 172 | break; |
| 173 | } |
| 174 | } |
| 175 | /* assigning the VCPUs round-robin is easier to implement, guest OSes |
| 176 | * must cope with this anyway, because there are BIOSes out there in |
| 177 | * real machines which also use this scheme. |
| 178 | */ |
| 179 | if (i == nb_numa_nodes) { |
| 180 | for (i = 0; i < max_cpus; i++) { |
| 181 | set_bit(i, node_cpumask[i % nb_numa_nodes]); |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | void set_numa_modes(void) |
| 188 | { |
| 189 | CPUState *cpu; |
| 190 | int i; |
| 191 | |
| 192 | CPU_FOREACH(cpu) { |
| 193 | for (i = 0; i < nb_numa_nodes; i++) { |
| 194 | if (test_bit(cpu->cpu_index, node_cpumask[i])) { |
| 195 | cpu->numa_node = i; |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | } |