Skip to content

Commit e1b9c27

Browse files
committed
s390/smp: ensure global control register contents are in sync
Globally setting a bit in control registers is done with smp_ctl_set_clear_bit(). This is using on_each_cpu() to execute a function which actually sets the control register bit on each online CPU. This can be problematic since on_each_cpu() does not prevent that new CPUs come online while it is executed, which in turn means that control register updates could be missing on new CPUs. In order to prevent this problem make sure that global control register contents cannot change until new CPUs have initialized their control registers, and marked themselves online, so they are included in subsequent on_each_cpu() calls. Reviewed-by: Sven Schnelle <svens@linux.ibm.com> Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
1 parent 481daa5 commit e1b9c27

1 file changed

Lines changed: 12 additions & 3 deletions

File tree

arch/s390/kernel/smp.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,9 @@ static void pcpu_free_lowcore(struct pcpu *pcpu)
253253

254254
static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
255255
{
256-
struct lowcore *lc = lowcore_ptr[cpu];
256+
struct lowcore *lc, *abs_lc;
257257

258+
lc = lowcore_ptr[cpu];
258259
cpumask_set_cpu(cpu, &init_mm.context.cpu_attach_mask);
259260
cpumask_set_cpu(cpu, mm_cpumask(&init_mm));
260261
lc->cpu_nr = cpu;
@@ -267,7 +268,9 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
267268
lc->machine_flags = S390_lowcore.machine_flags;
268269
lc->user_timer = lc->system_timer =
269270
lc->steal_timer = lc->avg_steal_timer = 0;
270-
__ctl_store(lc->cregs_save_area, 0, 15);
271+
abs_lc = get_abs_lowcore();
272+
memcpy(lc->cregs_save_area, abs_lc->cregs_save_area, sizeof(lc->cregs_save_area));
273+
put_abs_lowcore(abs_lc);
271274
lc->cregs_save_area[1] = lc->kernel_asce;
272275
lc->cregs_save_area[7] = lc->user_asce;
273276
save_access_regs((unsigned int *) lc->access_regs_save_area);
@@ -607,8 +610,8 @@ void smp_ctl_set_clear_bit(int cr, int bit, bool set)
607610
ctlreg = (ctlreg & parms.andval) | parms.orval;
608611
abs_lc->cregs_save_area[cr] = ctlreg;
609612
put_abs_lowcore(abs_lc);
610-
spin_unlock(&ctl_lock);
611613
on_each_cpu(smp_ctl_bit_callback, &parms, 1);
614+
spin_unlock(&ctl_lock);
612615
}
613616
EXPORT_SYMBOL(smp_ctl_set_clear_bit);
614617

@@ -928,12 +931,18 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
928931
rc = pcpu_alloc_lowcore(pcpu, cpu);
929932
if (rc)
930933
return rc;
934+
/*
935+
* Make sure global control register contents do not change
936+
* until new CPU has initialized control registers.
937+
*/
938+
spin_lock(&ctl_lock);
931939
pcpu_prepare_secondary(pcpu, cpu);
932940
pcpu_attach_task(pcpu, tidle);
933941
pcpu_start_fn(pcpu, smp_start_secondary, NULL);
934942
/* Wait until cpu puts itself in the online & active maps */
935943
while (!cpu_online(cpu))
936944
cpu_relax();
945+
spin_unlock(&ctl_lock);
937946
return 0;
938947
}
939948

0 commit comments

Comments
 (0)