diff options
author | Heiko Carstens <hca@linux.ibm.com> | 2023-09-11 21:40:06 +0200 |
---|---|---|
committer | Vasily Gorbik <gor@linux.ibm.com> | 2023-09-19 13:26:56 +0200 |
commit | cce2c8606f8ed3b0285842f4ad21e906d511dba1 (patch) | |
tree | c03fec44da02d4b2500e457e022e38502acbc994 /arch/s390/kernel | |
parent | s390/ctltreg: make initialization of control register save area explicit (diff) | |
download | linux-cce2c8606f8ed3b0285842f4ad21e906d511dba1.tar.xz linux-cce2c8606f8ed3b0285842f4ad21e906d511dba1.zip |
s390/ctlreg: allow to call system_ctl_set/clear_bit() early
Allow to call system_ctl_set_bit() and system_clt_clear_bit() early, so
that users do not have to take care when the control register save area
has been initialized. Users are supposed to use system_ctl_set_bit() and
system:clt_clear_bit() for all control register changes which are supposed
to be seen globally.
Depending on the system state such calls will change:
- local control register contents
- save area and local control register contents
- save area and global control register contents
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r-- | arch/s390/kernel/ctlreg.c | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/arch/s390/kernel/ctlreg.c b/arch/s390/kernel/ctlreg.c index 7acc40804874..27ba8db1d038 100644 --- a/arch/s390/kernel/ctlreg.c +++ b/arch/s390/kernel/ctlreg.c @@ -3,9 +3,12 @@ * Copyright IBM Corp. 1999, 2023 */ +#include <linux/irqflags.h> #include <linux/spinlock.h> +#include <linux/kernel.h> #include <linux/init.h> #include <linux/smp.h> +#include <linux/cache.h> #include <asm/abs_lowcore.h> #include <asm/ctlreg.h> @@ -28,6 +31,8 @@ void system_ctlreg_unlock(void) spin_unlock(&system_ctl_lock); } +static bool system_ctlreg_area_init __ro_after_init; + void __init system_ctlreg_init_save_area(struct lowcore *lc) { struct lowcore *abs_lc; @@ -36,6 +41,7 @@ void __init system_ctlreg_init_save_area(struct lowcore *lc) __local_ctl_store(0, 15, lc->cregs_save_area); __local_ctl_store(0, 15, abs_lc->cregs_save_area); put_abs_lowcore(abs_lc); + system_ctlreg_area_init = true; } struct ctl_bit_parms { @@ -55,6 +61,23 @@ static void ctl_bit_callback(void *info) __local_ctl_load(0, 15, regs); } +static void system_ctl_bit_update(void *info) +{ + unsigned long flags; + + if (system_state == SYSTEM_BOOTING) { + /* + * For very early calls do not call on_each_cpu() + * since not everything might be setup. + */ + local_irq_save(flags); + ctl_bit_callback(info); + local_irq_restore(flags); + } else { + on_each_cpu(ctl_bit_callback, info, 1); + } +} + void system_ctl_set_clear_bit(unsigned int cr, unsigned int bit, bool set) { struct ctl_bit_parms pp = { .cr = cr, }; @@ -62,12 +85,16 @@ void system_ctl_set_clear_bit(unsigned int cr, unsigned int bit, bool set) pp.orval = set ? 1UL << bit : 0; pp.andval = set ? -1UL : ~(1UL << bit); - system_ctlreg_lock(); - abs_lc = get_abs_lowcore(); - abs_lc->cregs_save_area[cr].val &= pp.andval; - abs_lc->cregs_save_area[cr].val |= pp.orval; - put_abs_lowcore(abs_lc); - on_each_cpu(ctl_bit_callback, &pp, 1); - system_ctlreg_unlock(); + if (system_ctlreg_area_init) { + system_ctlreg_lock(); + abs_lc = get_abs_lowcore(); + abs_lc->cregs_save_area[cr].val &= pp.andval; + abs_lc->cregs_save_area[cr].val |= pp.orval; + put_abs_lowcore(abs_lc); + system_ctl_bit_update(&pp); + system_ctlreg_unlock(); + } else { + system_ctl_bit_update(&pp); + } } EXPORT_SYMBOL(system_ctl_set_clear_bit); |