aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Jennings <rcj@linux.vnet.ibm.com>2013-07-24 21:13:21 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-11 21:35:20 -0400
commit027798707afb90e6f08144955813ba575743b04d (patch)
treed37f2935c3d25a060090a45079f4b37114985116
parent8148d0952b58d0664c3a3ddffbe6ded35ad4c790 (diff)
powerpc: VPHN topology change updates all siblings
commit 3be7db6ab45b21345386d1a466da133b19cde5e4 upstream. When an associativity level change is found for one thread, the siblings threads need to be updated as well. This is done today for PRRN in stage_topology_update() but is missing for VPHN in update_cpu_associativity_changes_mask(). This patch will correctly update all thread siblings during a topology change. Without this patch a topology update can result in a CPU in init_sched_groups_power() getting stuck indefinitely in a loop. This loop is built in build_sched_groups(). As a result of the thread moving to a node separate from its siblings the struct sched_group will have its next pointer set to point to itself rather than the sched_group struct of the next thread. This happens because we have a domain without the SD_OVERLAP flag, which is correct, and a topology that doesn't conform with reality (threads on the same core assigned to different numa nodes). When this list is traversed by init_sched_groups_power() it will reach the thread's sched_group structure and loop indefinitely; the cpu will be stuck at this point. The bug was exposed when VPHN was enabled in commit b7abef0 (v3.9). Reported-by: Jan Stancek <jstancek@redhat.com> Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/powerpc/include/asm/smp.h4
-rw-r--r--arch/powerpc/mm/numa.c59
2 files changed, 48 insertions, 15 deletions
diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
index ffbaabebcdca..48cfc858abd6 100644
--- a/arch/powerpc/include/asm/smp.h
+++ b/arch/powerpc/include/asm/smp.h
@@ -145,6 +145,10 @@ extern void __cpu_die(unsigned int cpu);
145#define smp_setup_cpu_maps() 145#define smp_setup_cpu_maps()
146static inline void inhibit_secondary_onlining(void) {} 146static inline void inhibit_secondary_onlining(void) {}
147static inline void uninhibit_secondary_onlining(void) {} 147static inline void uninhibit_secondary_onlining(void) {}
148static inline const struct cpumask *cpu_sibling_mask(int cpu)
149{
150 return cpumask_of(cpu);
151}
148 152
149#endif /* CONFIG_SMP */ 153#endif /* CONFIG_SMP */
150 154
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 2859a1f52279..cafad4017765 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -27,6 +27,7 @@
27#include <linux/seq_file.h> 27#include <linux/seq_file.h>
28#include <linux/uaccess.h> 28#include <linux/uaccess.h>
29#include <linux/slab.h> 29#include <linux/slab.h>
30#include <asm/cputhreads.h>
30#include <asm/sparsemem.h> 31#include <asm/sparsemem.h>
31#include <asm/prom.h> 32#include <asm/prom.h>
32#include <asm/smp.h> 33#include <asm/smp.h>
@@ -1319,7 +1320,8 @@ static int update_cpu_associativity_changes_mask(void)
1319 } 1320 }
1320 } 1321 }
1321 if (changed) { 1322 if (changed) {
1322 cpumask_set_cpu(cpu, changes); 1323 cpumask_or(changes, changes, cpu_sibling_mask(cpu));
1324 cpu = cpu_last_thread_sibling(cpu);
1323 } 1325 }
1324 } 1326 }
1325 1327
@@ -1427,7 +1429,7 @@ static int update_cpu_topology(void *data)
1427 if (!data) 1429 if (!data)
1428 return -EINVAL; 1430 return -EINVAL;
1429 1431
1430 cpu = get_cpu(); 1432 cpu = smp_processor_id();
1431 1433
1432 for (update = data; update; update = update->next) { 1434 for (update = data; update; update = update->next) {
1433 if (cpu != update->cpu) 1435 if (cpu != update->cpu)
@@ -1447,12 +1449,12 @@ static int update_cpu_topology(void *data)
1447 */ 1449 */
1448int arch_update_cpu_topology(void) 1450int arch_update_cpu_topology(void)
1449{ 1451{
1450 unsigned int cpu, changed = 0; 1452 unsigned int cpu, sibling, changed = 0;
1451 struct topology_update_data *updates, *ud; 1453 struct topology_update_data *updates, *ud;
1452 unsigned int associativity[VPHN_ASSOC_BUFSIZE] = {0}; 1454 unsigned int associativity[VPHN_ASSOC_BUFSIZE] = {0};
1453 cpumask_t updated_cpus; 1455 cpumask_t updated_cpus;
1454 struct device *dev; 1456 struct device *dev;
1455 int weight, i = 0; 1457 int weight, new_nid, i = 0;
1456 1458
1457 weight = cpumask_weight(&cpu_associativity_changes_mask); 1459 weight = cpumask_weight(&cpu_associativity_changes_mask);
1458 if (!weight) 1460 if (!weight)
@@ -1465,19 +1467,46 @@ int arch_update_cpu_topology(void)
1465 cpumask_clear(&updated_cpus); 1467 cpumask_clear(&updated_cpus);
1466 1468
1467 for_each_cpu(cpu, &cpu_associativity_changes_mask) { 1469 for_each_cpu(cpu, &cpu_associativity_changes_mask) {
1468 ud = &updates[i++]; 1470 /*
1469 ud->cpu = cpu; 1471 * If siblings aren't flagged for changes, updates list
1470 vphn_get_associativity(cpu, associativity); 1472 * will be too short. Skip on this update and set for next
1471 ud->new_nid = associativity_to_nid(associativity); 1473 * update.
1472 1474 */
1473 if (ud->new_nid < 0 || !node_online(ud->new_nid)) 1475 if (!cpumask_subset(cpu_sibling_mask(cpu),
1474 ud->new_nid = first_online_node; 1476 &cpu_associativity_changes_mask)) {
1477 pr_info("Sibling bits not set for associativity "
1478 "change, cpu%d\n", cpu);
1479 cpumask_or(&cpu_associativity_changes_mask,
1480 &cpu_associativity_changes_mask,
1481 cpu_sibling_mask(cpu));
1482 cpu = cpu_last_thread_sibling(cpu);
1483 continue;
1484 }
1475 1485
1476 ud->old_nid = numa_cpu_lookup_table[cpu]; 1486 /* Use associativity from first thread for all siblings */
1477 cpumask_set_cpu(cpu, &updated_cpus); 1487 vphn_get_associativity(cpu, associativity);
1488 new_nid = associativity_to_nid(associativity);
1489 if (new_nid < 0 || !node_online(new_nid))
1490 new_nid = first_online_node;
1491
1492 if (new_nid == numa_cpu_lookup_table[cpu]) {
1493 cpumask_andnot(&cpu_associativity_changes_mask,
1494 &cpu_associativity_changes_mask,
1495 cpu_sibling_mask(cpu));
1496 cpu = cpu_last_thread_sibling(cpu);
1497 continue;
1498 }
1478 1499
1479 if (i < weight) 1500 for_each_cpu(sibling, cpu_sibling_mask(cpu)) {
1480 ud->next = &updates[i]; 1501 ud = &updates[i++];
1502 ud->cpu = sibling;
1503 ud->new_nid = new_nid;
1504 ud->old_nid = numa_cpu_lookup_table[sibling];
1505 cpumask_set_cpu(sibling, &updated_cpus);
1506 if (i < weight)
1507 ud->next = &updates[i];
1508 }
1509 cpu = cpu_last_thread_sibling(cpu);
1481 } 1510 }
1482 1511
1483 stop_machine(update_cpu_topology, &updates[0], &updated_cpus); 1512 stop_machine(update_cpu_topology, &updates[0], &updated_cpus);