]>
Commit | Line | Data |
---|---|---|
96d0e26c WG |
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" | |
2b631ec2 WG |
29 | #include "qemu/error-report.h" |
30 | #include "include/exec/cpu-common.h" /* for RAM_ADDR_FMT */ | |
96d0e26c WG |
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 | ||
8c85901e | 68 | bitmap_set(numa_info[nodenr].node_cpu, value, endvalue-value+1); |
96d0e26c WG |
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) { | |
8c85901e | 108 | numa_info[nodenr].node_mem = 0; |
96d0e26c WG |
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 | } | |
8c85901e | 116 | numa_info[nodenr].node_mem = sval; |
96d0e26c WG |
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) { | |
2b631ec2 | 131 | uint64_t numa_total; |
96d0e26c WG |
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++) { | |
8c85901e | 142 | if (numa_info[i].node_mem != 0) { |
96d0e26c WG |
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++) { | |
8c85901e WG |
153 | numa_info[i].node_mem = (ram_size / nb_numa_nodes) & |
154 | ~((1 << 23UL) - 1); | |
155 | usedmem += numa_info[i].node_mem; | |
96d0e26c | 156 | } |
8c85901e | 157 | numa_info[i].node_mem = ram_size - usedmem; |
96d0e26c WG |
158 | } |
159 | ||
2b631ec2 WG |
160 | numa_total = 0; |
161 | for (i = 0; i < nb_numa_nodes; i++) { | |
8c85901e | 162 | numa_total += numa_info[i].node_mem; |
2b631ec2 WG |
163 | } |
164 | if (numa_total != ram_size) { | |
165 | error_report("total memory for NUMA nodes (%" PRIu64 ")" | |
166 | " should equal RAM size (" RAM_ADDR_FMT ")", | |
167 | numa_total, ram_size); | |
168 | exit(1); | |
169 | } | |
170 | ||
96d0e26c | 171 | for (i = 0; i < nb_numa_nodes; i++) { |
8c85901e | 172 | if (!bitmap_empty(numa_info[i].node_cpu, MAX_CPUMASK_BITS)) { |
96d0e26c WG |
173 | break; |
174 | } | |
175 | } | |
176 | /* assigning the VCPUs round-robin is easier to implement, guest OSes | |
177 | * must cope with this anyway, because there are BIOSes out there in | |
178 | * real machines which also use this scheme. | |
179 | */ | |
180 | if (i == nb_numa_nodes) { | |
181 | for (i = 0; i < max_cpus; i++) { | |
8c85901e | 182 | set_bit(i, numa_info[i % nb_numa_nodes].node_cpu); |
96d0e26c WG |
183 | } |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | void set_numa_modes(void) | |
189 | { | |
190 | CPUState *cpu; | |
191 | int i; | |
192 | ||
193 | CPU_FOREACH(cpu) { | |
194 | for (i = 0; i < nb_numa_nodes; i++) { | |
8c85901e | 195 | if (test_bit(cpu->cpu_index, numa_info[i].node_cpu)) { |
96d0e26c WG |
196 | cpu->numa_node = i; |
197 | } | |
198 | } | |
199 | } | |
200 | } |