s390/ctlreg: allow to call system_ctl_set/clear_bit() early
authorHeiko Carstens <hca@linux.ibm.com>
Mon, 11 Sep 2023 19:40:06 +0000 (21:40 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Tue, 19 Sep 2023 11:26:56 +0000 (13:26 +0200)
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>
arch/s390/kernel/ctlreg.c

index 7acc40804874330f677933b775785da5482094c7..27ba8db1d0381deb389bd59c98ef51d9024514ee 100644 (file)
@@ -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);