aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2012-07-13 09:45:33 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-07-17 04:34:27 -0400
commit0008204ffe85d23382d6fd0f971f3f0fbe70bae2 (patch)
tree0efd42411102e2b75a7707b959be0933ba70383f /arch/s390
parent8738e07d5c9b5b02f113c7e0ba67f0d8d6a5b23c (diff)
s390/idle: fix sequence handling vs cpu hotplug
The s390 idle accounting code uses a sequence counter which gets used when the per cpu idle statistics get updated and read. One assumption on read access is that only when the sequence counter is even and did not change while reading all values the result is valid. On cpu hotplug however the per cpu data structure gets initialized via a cpu hotplug notifier on CPU_ONLINE. CPU_ONLINE however is too late, since the onlined cpu is already running and might access the per cpu data. Worst case is that the data structure gets initialized while an idle thread is updating its idle statistics. This will result in an uneven sequence counter after an update. As a result user space tools like top, which access /proc/stat in order to get idle stats, will busy loop waiting for the sequence counter to become even again, which will never happen until the queried cpu will update its idle statistics again. And even then the sequence counter will only have an even value for a couple of cpu cycles. Fix this by moving the initialization of the per cpu idle statistics to cpu_init(). I prefer that solution in favor of changing the notifier to CPU_UP_PREPARE, which would be a different solution to the problem. Cc: stable@vger.kernel.org Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/kernel/processor.c2
-rw-r--r--arch/s390/kernel/smp.c3
2 files changed, 2 insertions, 3 deletions
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 6e0073e43f54..07c7bf47d618 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -26,12 +26,14 @@ static DEFINE_PER_CPU(struct cpuid, cpu_id);
26void __cpuinit cpu_init(void) 26void __cpuinit cpu_init(void)
27{ 27{
28 struct cpuid *id = &per_cpu(cpu_id, smp_processor_id()); 28 struct cpuid *id = &per_cpu(cpu_id, smp_processor_id());
29 struct s390_idle_data *idle = &__get_cpu_var(s390_idle);
29 30
30 get_cpu_id(id); 31 get_cpu_id(id);
31 atomic_inc(&init_mm.mm_count); 32 atomic_inc(&init_mm.mm_count);
32 current->active_mm = &init_mm; 33 current->active_mm = &init_mm;
33 BUG_ON(current->mm); 34 BUG_ON(current->mm);
34 enter_lazy_tlb(&init_mm, current); 35 enter_lazy_tlb(&init_mm, current);
36 memset(idle, 0, sizeof(*idle));
35} 37}
36 38
37/* 39/*
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index dc602a61233f..22257f241d07 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -959,14 +959,11 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,
959 unsigned int cpu = (unsigned int)(long)hcpu; 959 unsigned int cpu = (unsigned int)(long)hcpu;
960 struct cpu *c = &pcpu_devices[cpu].cpu; 960 struct cpu *c = &pcpu_devices[cpu].cpu;
961 struct device *s = &c->dev; 961 struct device *s = &c->dev;
962 struct s390_idle_data *idle;
963 int err = 0; 962 int err = 0;
964 963
965 switch (action) { 964 switch (action) {
966 case CPU_ONLINE: 965 case CPU_ONLINE:
967 case CPU_ONLINE_FROZEN: 966 case CPU_ONLINE_FROZEN:
968 idle = &per_cpu(s390_idle, cpu);
969 memset(idle, 0, sizeof(struct s390_idle_data));
970 err = sysfs_create_group(&s->kobj, &cpu_online_attr_group); 967 err = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
971 break; 968 break;
972 case CPU_DEAD: 969 case CPU_DEAD: