diff options
Diffstat (limited to 'arch/s390/kernel/topology.c')
-rw-r--r-- | arch/s390/kernel/topology.c | 172 |
1 files changed, 117 insertions, 55 deletions
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index fdb5b8cb260f..621f89e36c8a 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c | |||
@@ -6,17 +6,17 @@ | |||
6 | #define KMSG_COMPONENT "cpu" | 6 | #define KMSG_COMPONENT "cpu" |
7 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | 7 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
8 | 8 | ||
9 | #include <linux/kernel.h> | 9 | #include <linux/workqueue.h> |
10 | #include <linux/mm.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/bootmem.h> | 10 | #include <linux/bootmem.h> |
11 | #include <linux/cpuset.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/workqueue.h> | 15 | #include <linux/init.h> |
16 | #include <linux/delay.h> | ||
16 | #include <linux/cpu.h> | 17 | #include <linux/cpu.h> |
17 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
18 | #include <linux/cpuset.h> | 19 | #include <linux/mm.h> |
19 | #include <asm/delay.h> | ||
20 | 20 | ||
21 | #define PTF_HORIZONTAL (0UL) | 21 | #define PTF_HORIZONTAL (0UL) |
22 | #define PTF_VERTICAL (1UL) | 22 | #define PTF_VERTICAL (1UL) |
@@ -41,11 +41,12 @@ static struct mask_info core_info; | |||
41 | cpumask_t cpu_core_map[NR_CPUS]; | 41 | cpumask_t cpu_core_map[NR_CPUS]; |
42 | unsigned char cpu_core_id[NR_CPUS]; | 42 | unsigned char cpu_core_id[NR_CPUS]; |
43 | 43 | ||
44 | #ifdef CONFIG_SCHED_BOOK | ||
45 | static struct mask_info book_info; | 44 | static struct mask_info book_info; |
46 | cpumask_t cpu_book_map[NR_CPUS]; | 45 | cpumask_t cpu_book_map[NR_CPUS]; |
47 | unsigned char cpu_book_id[NR_CPUS]; | 46 | unsigned char cpu_book_id[NR_CPUS]; |
48 | #endif | 47 | |
48 | /* smp_cpu_state_mutex must be held when accessing this array */ | ||
49 | int cpu_polarization[NR_CPUS]; | ||
49 | 50 | ||
50 | static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu) | 51 | static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu) |
51 | { | 52 | { |
@@ -85,10 +86,8 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu, | |||
85 | for_each_present_cpu(lcpu) { | 86 | for_each_present_cpu(lcpu) { |
86 | if (cpu_logical_map(lcpu) != rcpu) | 87 | if (cpu_logical_map(lcpu) != rcpu) |
87 | continue; | 88 | continue; |
88 | #ifdef CONFIG_SCHED_BOOK | ||
89 | cpumask_set_cpu(lcpu, &book->mask); | 89 | cpumask_set_cpu(lcpu, &book->mask); |
90 | cpu_book_id[lcpu] = book->id; | 90 | cpu_book_id[lcpu] = book->id; |
91 | #endif | ||
92 | cpumask_set_cpu(lcpu, &core->mask); | 91 | cpumask_set_cpu(lcpu, &core->mask); |
93 | if (z10) { | 92 | if (z10) { |
94 | cpu_core_id[lcpu] = rcpu; | 93 | cpu_core_id[lcpu] = rcpu; |
@@ -96,7 +95,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu, | |||
96 | } else { | 95 | } else { |
97 | cpu_core_id[lcpu] = core->id; | 96 | cpu_core_id[lcpu] = core->id; |
98 | } | 97 | } |
99 | smp_cpu_polarization[lcpu] = tl_cpu->pp; | 98 | cpu_set_polarization(lcpu, tl_cpu->pp); |
100 | } | 99 | } |
101 | } | 100 | } |
102 | return core; | 101 | return core; |
@@ -111,13 +110,11 @@ static void clear_masks(void) | |||
111 | cpumask_clear(&info->mask); | 110 | cpumask_clear(&info->mask); |
112 | info = info->next; | 111 | info = info->next; |
113 | } | 112 | } |
114 | #ifdef CONFIG_SCHED_BOOK | ||
115 | info = &book_info; | 113 | info = &book_info; |
116 | while (info) { | 114 | while (info) { |
117 | cpumask_clear(&info->mask); | 115 | cpumask_clear(&info->mask); |
118 | info = info->next; | 116 | info = info->next; |
119 | } | 117 | } |
120 | #endif | ||
121 | } | 118 | } |
122 | 119 | ||
123 | static union topology_entry *next_tle(union topology_entry *tle) | 120 | static union topology_entry *next_tle(union topology_entry *tle) |
@@ -129,26 +126,19 @@ static union topology_entry *next_tle(union topology_entry *tle) | |||
129 | 126 | ||
130 | static void tl_to_cores(struct sysinfo_15_1_x *info) | 127 | static void tl_to_cores(struct sysinfo_15_1_x *info) |
131 | { | 128 | { |
132 | #ifdef CONFIG_SCHED_BOOK | ||
133 | struct mask_info *book = &book_info; | ||
134 | struct cpuid cpu_id; | ||
135 | #else | ||
136 | struct mask_info *book = NULL; | ||
137 | #endif | ||
138 | struct mask_info *core = &core_info; | 129 | struct mask_info *core = &core_info; |
130 | struct mask_info *book = &book_info; | ||
139 | union topology_entry *tle, *end; | 131 | union topology_entry *tle, *end; |
132 | struct cpuid cpu_id; | ||
140 | int z10 = 0; | 133 | int z10 = 0; |
141 | 134 | ||
142 | #ifdef CONFIG_SCHED_BOOK | ||
143 | get_cpu_id(&cpu_id); | 135 | get_cpu_id(&cpu_id); |
144 | z10 = cpu_id.machine == 0x2097 || cpu_id.machine == 0x2098; | 136 | z10 = cpu_id.machine == 0x2097 || cpu_id.machine == 0x2098; |
145 | #endif | ||
146 | spin_lock_irq(&topology_lock); | 137 | spin_lock_irq(&topology_lock); |
147 | clear_masks(); | 138 | clear_masks(); |
148 | tle = info->tle; | 139 | tle = info->tle; |
149 | end = (union topology_entry *)((unsigned long)info + info->length); | 140 | end = (union topology_entry *)((unsigned long)info + info->length); |
150 | while (tle < end) { | 141 | while (tle < end) { |
151 | #ifdef CONFIG_SCHED_BOOK | ||
152 | if (z10) { | 142 | if (z10) { |
153 | switch (tle->nl) { | 143 | switch (tle->nl) { |
154 | case 1: | 144 | case 1: |
@@ -165,14 +155,11 @@ static void tl_to_cores(struct sysinfo_15_1_x *info) | |||
165 | tle = next_tle(tle); | 155 | tle = next_tle(tle); |
166 | continue; | 156 | continue; |
167 | } | 157 | } |
168 | #endif | ||
169 | switch (tle->nl) { | 158 | switch (tle->nl) { |
170 | #ifdef CONFIG_SCHED_BOOK | ||
171 | case 2: | 159 | case 2: |
172 | book = book->next; | 160 | book = book->next; |
173 | book->id = tle->container.id; | 161 | book->id = tle->container.id; |
174 | break; | 162 | break; |
175 | #endif | ||
176 | case 1: | 163 | case 1: |
177 | core = core->next; | 164 | core = core->next; |
178 | core->id = tle->container.id; | 165 | core->id = tle->container.id; |
@@ -196,7 +183,7 @@ static void topology_update_polarization_simple(void) | |||
196 | 183 | ||
197 | mutex_lock(&smp_cpu_state_mutex); | 184 | mutex_lock(&smp_cpu_state_mutex); |
198 | for_each_possible_cpu(cpu) | 185 | for_each_possible_cpu(cpu) |
199 | smp_cpu_polarization[cpu] = POLARIZATION_HRZ; | 186 | cpu_set_polarization(cpu, POLARIZATION_HRZ); |
200 | mutex_unlock(&smp_cpu_state_mutex); | 187 | mutex_unlock(&smp_cpu_state_mutex); |
201 | } | 188 | } |
202 | 189 | ||
@@ -215,8 +202,7 @@ static int ptf(unsigned long fc) | |||
215 | 202 | ||
216 | int topology_set_cpu_management(int fc) | 203 | int topology_set_cpu_management(int fc) |
217 | { | 204 | { |
218 | int cpu; | 205 | int cpu, rc; |
219 | int rc; | ||
220 | 206 | ||
221 | if (!MACHINE_HAS_TOPOLOGY) | 207 | if (!MACHINE_HAS_TOPOLOGY) |
222 | return -EOPNOTSUPP; | 208 | return -EOPNOTSUPP; |
@@ -227,7 +213,7 @@ int topology_set_cpu_management(int fc) | |||
227 | if (rc) | 213 | if (rc) |
228 | return -EBUSY; | 214 | return -EBUSY; |
229 | for_each_possible_cpu(cpu) | 215 | for_each_possible_cpu(cpu) |
230 | smp_cpu_polarization[cpu] = POLARIZATION_UNKNWN; | 216 | cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); |
231 | return rc; | 217 | return rc; |
232 | } | 218 | } |
233 | 219 | ||
@@ -239,22 +225,18 @@ static void update_cpu_core_map(void) | |||
239 | spin_lock_irqsave(&topology_lock, flags); | 225 | spin_lock_irqsave(&topology_lock, flags); |
240 | for_each_possible_cpu(cpu) { | 226 | for_each_possible_cpu(cpu) { |
241 | cpu_core_map[cpu] = cpu_group_map(&core_info, cpu); | 227 | cpu_core_map[cpu] = cpu_group_map(&core_info, cpu); |
242 | #ifdef CONFIG_SCHED_BOOK | ||
243 | cpu_book_map[cpu] = cpu_group_map(&book_info, cpu); | 228 | cpu_book_map[cpu] = cpu_group_map(&book_info, cpu); |
244 | #endif | ||
245 | } | 229 | } |
246 | spin_unlock_irqrestore(&topology_lock, flags); | 230 | spin_unlock_irqrestore(&topology_lock, flags); |
247 | } | 231 | } |
248 | 232 | ||
249 | void store_topology(struct sysinfo_15_1_x *info) | 233 | void store_topology(struct sysinfo_15_1_x *info) |
250 | { | 234 | { |
251 | #ifdef CONFIG_SCHED_BOOK | ||
252 | int rc; | 235 | int rc; |
253 | 236 | ||
254 | rc = stsi(info, 15, 1, 3); | 237 | rc = stsi(info, 15, 1, 3); |
255 | if (rc != -ENOSYS) | 238 | if (rc != -ENOSYS) |
256 | return; | 239 | return; |
257 | #endif | ||
258 | stsi(info, 15, 1, 2); | 240 | stsi(info, 15, 1, 2); |
259 | } | 241 | } |
260 | 242 | ||
@@ -313,23 +295,6 @@ static int __init early_parse_topology(char *p) | |||
313 | } | 295 | } |
314 | early_param("topology", early_parse_topology); | 296 | early_param("topology", early_parse_topology); |
315 | 297 | ||
316 | static int __init init_topology_update(void) | ||
317 | { | ||
318 | int rc; | ||
319 | |||
320 | rc = 0; | ||
321 | if (!MACHINE_HAS_TOPOLOGY) { | ||
322 | topology_update_polarization_simple(); | ||
323 | goto out; | ||
324 | } | ||
325 | init_timer_deferrable(&topology_timer); | ||
326 | set_topology_timer(); | ||
327 | out: | ||
328 | update_cpu_core_map(); | ||
329 | return rc; | ||
330 | } | ||
331 | __initcall(init_topology_update); | ||
332 | |||
333 | static void __init alloc_masks(struct sysinfo_15_1_x *info, | 298 | static void __init alloc_masks(struct sysinfo_15_1_x *info, |
334 | struct mask_info *mask, int offset) | 299 | struct mask_info *mask, int offset) |
335 | { | 300 | { |
@@ -357,10 +322,107 @@ void __init s390_init_cpu_topology(void) | |||
357 | store_topology(info); | 322 | store_topology(info); |
358 | pr_info("The CPU configuration topology of the machine is:"); | 323 | pr_info("The CPU configuration topology of the machine is:"); |
359 | for (i = 0; i < TOPOLOGY_NR_MAG; i++) | 324 | for (i = 0; i < TOPOLOGY_NR_MAG; i++) |
360 | printk(" %d", info->mag[i]); | 325 | printk(KERN_CONT " %d", info->mag[i]); |
361 | printk(" / %d\n", info->mnest); | 326 | printk(KERN_CONT " / %d\n", info->mnest); |
362 | alloc_masks(info, &core_info, 1); | 327 | alloc_masks(info, &core_info, 1); |
363 | #ifdef CONFIG_SCHED_BOOK | ||
364 | alloc_masks(info, &book_info, 2); | 328 | alloc_masks(info, &book_info, 2); |
365 | #endif | ||
366 | } | 329 | } |
330 | |||
331 | static int cpu_management; | ||
332 | |||
333 | static ssize_t dispatching_show(struct sysdev_class *class, | ||
334 | struct sysdev_class_attribute *attr, | ||
335 | char *buf) | ||
336 | { | ||
337 | ssize_t count; | ||
338 | |||
339 | mutex_lock(&smp_cpu_state_mutex); | ||
340 | count = sprintf(buf, "%d\n", cpu_management); | ||
341 | mutex_unlock(&smp_cpu_state_mutex); | ||
342 | return count; | ||
343 | } | ||
344 | |||
345 | static ssize_t dispatching_store(struct sysdev_class *dev, | ||
346 | struct sysdev_class_attribute *attr, | ||
347 | const char *buf, | ||
348 | size_t count) | ||
349 | { | ||
350 | int val, rc; | ||
351 | char delim; | ||
352 | |||
353 | if (sscanf(buf, "%d %c", &val, &delim) != 1) | ||
354 | return -EINVAL; | ||
355 | if (val != 0 && val != 1) | ||
356 | return -EINVAL; | ||
357 | rc = 0; | ||
358 | get_online_cpus(); | ||
359 | mutex_lock(&smp_cpu_state_mutex); | ||
360 | if (cpu_management == val) | ||
361 | goto out; | ||
362 | rc = topology_set_cpu_management(val); | ||
363 | if (!rc) | ||
364 | cpu_management = val; | ||
365 | out: | ||
366 | mutex_unlock(&smp_cpu_state_mutex); | ||
367 | put_online_cpus(); | ||
368 | return rc ? rc : count; | ||
369 | } | ||
370 | static SYSDEV_CLASS_ATTR(dispatching, 0644, dispatching_show, | ||
371 | dispatching_store); | ||
372 | |||
373 | static ssize_t cpu_polarization_show(struct sys_device *dev, | ||
374 | struct sysdev_attribute *attr, char *buf) | ||
375 | { | ||
376 | int cpu = dev->id; | ||
377 | ssize_t count; | ||
378 | |||
379 | mutex_lock(&smp_cpu_state_mutex); | ||
380 | switch (cpu_read_polarization(cpu)) { | ||
381 | case POLARIZATION_HRZ: | ||
382 | count = sprintf(buf, "horizontal\n"); | ||
383 | break; | ||
384 | case POLARIZATION_VL: | ||
385 | count = sprintf(buf, "vertical:low\n"); | ||
386 | break; | ||
387 | case POLARIZATION_VM: | ||
388 | count = sprintf(buf, "vertical:medium\n"); | ||
389 | break; | ||
390 | case POLARIZATION_VH: | ||
391 | count = sprintf(buf, "vertical:high\n"); | ||
392 | break; | ||
393 | default: | ||
394 | count = sprintf(buf, "unknown\n"); | ||
395 | break; | ||
396 | } | ||
397 | mutex_unlock(&smp_cpu_state_mutex); | ||
398 | return count; | ||
399 | } | ||
400 | static SYSDEV_ATTR(polarization, 0444, cpu_polarization_show, NULL); | ||
401 | |||
402 | static struct attribute *topology_cpu_attrs[] = { | ||
403 | &attr_polarization.attr, | ||
404 | NULL, | ||
405 | }; | ||
406 | |||
407 | static struct attribute_group topology_cpu_attr_group = { | ||
408 | .attrs = topology_cpu_attrs, | ||
409 | }; | ||
410 | |||
411 | int topology_cpu_init(struct cpu *cpu) | ||
412 | { | ||
413 | return sysfs_create_group(&cpu->sysdev.kobj, &topology_cpu_attr_group); | ||
414 | } | ||
415 | |||
416 | static int __init topology_init(void) | ||
417 | { | ||
418 | if (!MACHINE_HAS_TOPOLOGY) { | ||
419 | topology_update_polarization_simple(); | ||
420 | goto out; | ||
421 | } | ||
422 | init_timer_deferrable(&topology_timer); | ||
423 | set_topology_timer(); | ||
424 | out: | ||
425 | update_cpu_core_map(); | ||
426 | return sysdev_class_create_file(&cpu_sysdev_class, &attr_dispatching); | ||
427 | } | ||
428 | device_initcall(topology_init); | ||