summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeiko Carstens <hca@linux.ibm.com>2023-09-11 22:40:06 +0300
committerVasily Gorbik <gor@linux.ibm.com>2023-09-19 14:26:56 +0300
commitcce2c8606f8ed3b0285842f4ad21e906d511dba1 (patch)
treec03fec44da02d4b2500e457e022e38502acbc994
parentd11d5c8c8426a4abdcb2252999f1b57e3aa79e5b (diff)
downloadlinux-cce2c8606f8ed3b0285842f4ad21e906d511dba1.tar.xz
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>
-rw-r--r--arch/s390/kernel/ctlreg.c41
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);