printk: rename cpulock functions
authorJohn Ogness <john.ogness@linutronix.de>
Thu, 21 Apr 2022 21:22:36 +0000 (23:28 +0206)
committerPetr Mladek <pmladek@suse.com>
Fri, 22 Apr 2022 19:30:57 +0000 (21:30 +0200)
Since the printk cpulock is CPU-reentrant and since it is used
in all contexts, its usage must be carefully considered and
most likely will require programming locklessly. To avoid
mistaking the printk cpulock as a typical lock, rename it to
cpu_sync. The main functions then become:

    printk_cpu_sync_get_irqsave(flags);
    printk_cpu_sync_put_irqrestore(flags);

Add extra notes of caution in the function description to help
developers understand the requirements for correct usage.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20220421212250.565456-2-john.ogness@linutronix.de
include/linux/printk.h
kernel/printk/printk.c
lib/dump_stack.c
lib/nmi_backtrace.c

index 1522df223c0f7650a80cd42b1c43fedb14c8a8c7..859323a529850ecf38048d099e4cec805dd76940 100644 (file)
@@ -277,43 +277,55 @@ static inline void printk_trigger_flush(void)
 #endif
 
 #ifdef CONFIG_SMP
-extern int __printk_cpu_trylock(void);
-extern void __printk_wait_on_cpu_lock(void);
-extern void __printk_cpu_unlock(void);
+extern int __printk_cpu_sync_try_get(void);
+extern void __printk_cpu_sync_wait(void);
+extern void __printk_cpu_sync_put(void);
 
 /**
- * printk_cpu_lock_irqsave() - Acquire the printk cpu-reentrant spinning
- *                             lock and disable interrupts.
+ * printk_cpu_sync_get_irqsave() - Acquire the printk cpu-reentrant spinning
+ *                                 lock and disable interrupts.
  * @flags: Stack-allocated storage for saving local interrupt state,
- *         to be passed to printk_cpu_unlock_irqrestore().
+ *         to be passed to printk_cpu_sync_put_irqrestore().
  *
  * If the lock is owned by another CPU, spin until it becomes available.
  * Interrupts are restored while spinning.
+ *
+ * CAUTION: This function must be used carefully. It does not behave like a
+ * typical lock. Here are important things to watch out for...
+ *
+ *     * This function is reentrant on the same CPU. Therefore the calling
+ *       code must not assume exclusive access to data if code accessing the
+ *       data can run reentrant or within NMI context on the same CPU.
+ *
+ *     * If there exists usage of this function from NMI context, it becomes
+ *       unsafe to perform any type of locking or spinning to wait for other
+ *       CPUs after calling this function from any context. This includes
+ *       using spinlocks or any other busy-waiting synchronization methods.
  */
-#define printk_cpu_lock_irqsave(flags)         \
-       for (;;) {                              \
-               local_irq_save(flags);          \
-               if (__printk_cpu_trylock())     \
-                       break;                  \
-               local_irq_restore(flags);       \
-               __printk_wait_on_cpu_lock();    \
+#define printk_cpu_sync_get_irqsave(flags)             \
+       for (;;) {                                      \
+               local_irq_save(flags);                  \
+               if (__printk_cpu_sync_try_get())        \
+                       break;                          \
+               local_irq_restore(flags);               \
+               __printk_cpu_sync_wait();               \
        }
 
 /**
- * printk_cpu_unlock_irqrestore() - Release the printk cpu-reentrant spinning
- *                                  lock and restore interrupts.
- * @flags: Caller's saved interrupt state, from printk_cpu_lock_irqsave().
+ * printk_cpu_sync_put_irqrestore() - Release the printk cpu-reentrant spinning
+ *                                    lock and restore interrupts.
+ * @flags: Caller's saved interrupt state, from printk_cpu_sync_get_irqsave().
  */
-#define printk_cpu_unlock_irqrestore(flags)    \
+#define printk_cpu_sync_put_irqrestore(flags)  \
        do {                                    \
-               __printk_cpu_unlock();          \
+               __printk_cpu_sync_put();        \
                local_irq_restore(flags);       \
-       } while (0)                             \
+       } while (0)
 
 #else
 
-#define printk_cpu_lock_irqsave(flags) ((void)flags)
-#define printk_cpu_unlock_irqrestore(flags) ((void)flags)
+#define printk_cpu_sync_get_irqsave(flags) ((void)flags)
+#define printk_cpu_sync_put_irqrestore(flags) ((void)flags)
 
 #endif /* CONFIG_SMP */
 
index da03c15ecc898076242623123e30e9c2d6e26c5d..13a1eebe72afbbfb76c578588b04a8d19552f7fd 100644 (file)
@@ -3667,26 +3667,26 @@ EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
 #endif
 
 #ifdef CONFIG_SMP
-static atomic_t printk_cpulock_owner = ATOMIC_INIT(-1);
-static atomic_t printk_cpulock_nested = ATOMIC_INIT(0);
+static atomic_t printk_cpu_sync_owner = ATOMIC_INIT(-1);
+static atomic_t printk_cpu_sync_nested = ATOMIC_INIT(0);
 
 /**
- * __printk_wait_on_cpu_lock() - Busy wait until the printk cpu-reentrant
- *                               spinning lock is not owned by any CPU.
+ * __printk_cpu_sync_wait() - Busy wait until the printk cpu-reentrant
+ *                            spinning lock is not owned by any CPU.
  *
  * Context: Any context.
  */
-void __printk_wait_on_cpu_lock(void)
+void __printk_cpu_sync_wait(void)
 {
        do {
                cpu_relax();
-       } while (atomic_read(&printk_cpulock_owner) != -1);
+       } while (atomic_read(&printk_cpu_sync_owner) != -1);
 }
-EXPORT_SYMBOL(__printk_wait_on_cpu_lock);
+EXPORT_SYMBOL(__printk_cpu_sync_wait);
 
 /**
- * __printk_cpu_trylock() - Try to acquire the printk cpu-reentrant
- *                          spinning lock.
+ * __printk_cpu_sync_try_get() - Try to acquire the printk cpu-reentrant
+ *                               spinning lock.
  *
  * If no processor has the lock, the calling processor takes the lock and
  * becomes the owner. If the calling processor is already the owner of the
@@ -3695,7 +3695,7 @@ EXPORT_SYMBOL(__printk_wait_on_cpu_lock);
  * Context: Any context. Expects interrupts to be disabled.
  * Return: 1 on success, otherwise 0.
  */
-int __printk_cpu_trylock(void)
+int __printk_cpu_sync_try_get(void)
 {
        int cpu;
        int old;
@@ -3705,79 +3705,80 @@ int __printk_cpu_trylock(void)
        /*
         * Guarantee loads and stores from this CPU when it is the lock owner
         * are _not_ visible to the previous lock owner. This pairs with
-        * __printk_cpu_unlock:B.
+        * __printk_cpu_sync_put:B.
         *
         * Memory barrier involvement:
         *
-        * If __printk_cpu_trylock:A reads from __printk_cpu_unlock:B, then
-        * __printk_cpu_unlock:A can never read from __printk_cpu_trylock:B.
+        * If __printk_cpu_sync_try_get:A reads from __printk_cpu_sync_put:B,
+        * then __printk_cpu_sync_put:A can never read from
+        * __printk_cpu_sync_try_get:B.
         *
         * Relies on:
         *
-        * RELEASE from __printk_cpu_unlock:A to __printk_cpu_unlock:B
+        * RELEASE from __printk_cpu_sync_put:A to __printk_cpu_sync_put:B
         * of the previous CPU
         *    matching
-        * ACQUIRE from __printk_cpu_trylock:A to __printk_cpu_trylock:B
-        * of this CPU
+        * ACQUIRE from __printk_cpu_sync_try_get:A to
+        * __printk_cpu_sync_try_get:B of this CPU
         */
-       old = atomic_cmpxchg_acquire(&printk_cpulock_owner, -1,
-                                    cpu); /* LMM(__printk_cpu_trylock:A) */
+       old = atomic_cmpxchg_acquire(&printk_cpu_sync_owner, -1,
+                                    cpu); /* LMM(__printk_cpu_sync_try_get:A) */
        if (old == -1) {
                /*
                 * This CPU is now the owner and begins loading/storing
-                * data: LMM(__printk_cpu_trylock:B)
+                * data: LMM(__printk_cpu_sync_try_get:B)
                 */
                return 1;
 
        } else if (old == cpu) {
                /* This CPU is already the owner. */
-               atomic_inc(&printk_cpulock_nested);
+               atomic_inc(&printk_cpu_sync_nested);
                return 1;
        }
 
        return 0;
 }
-EXPORT_SYMBOL(__printk_cpu_trylock);
+EXPORT_SYMBOL(__printk_cpu_sync_try_get);
 
 /**
- * __printk_cpu_unlock() - Release the printk cpu-reentrant spinning lock.
+ * __printk_cpu_sync_put() - Release the printk cpu-reentrant spinning lock.
  *
  * The calling processor must be the owner of the lock.
  *
  * Context: Any context. Expects interrupts to be disabled.
  */
-void __printk_cpu_unlock(void)
+void __printk_cpu_sync_put(void)
 {
-       if (atomic_read(&printk_cpulock_nested)) {
-               atomic_dec(&printk_cpulock_nested);
+       if (atomic_read(&printk_cpu_sync_nested)) {
+               atomic_dec(&printk_cpu_sync_nested);
                return;
        }
 
        /*
         * This CPU is finished loading/storing data:
-        * LMM(__printk_cpu_unlock:A)
+        * LMM(__printk_cpu_sync_put:A)
         */
 
        /*
         * Guarantee loads and stores from this CPU when it was the
         * lock owner are visible to the next lock owner. This pairs
-        * with __printk_cpu_trylock:A.
+        * with __printk_cpu_sync_try_get:A.
         *
         * Memory barrier involvement:
         *
-        * If __printk_cpu_trylock:A reads from __printk_cpu_unlock:B,
-        * then __printk_cpu_trylock:B reads from __printk_cpu_unlock:A.
+        * If __printk_cpu_sync_try_get:A reads from __printk_cpu_sync_put:B,
+        * then __printk_cpu_sync_try_get:B reads from __printk_cpu_sync_put:A.
         *
         * Relies on:
         *
-        * RELEASE from __printk_cpu_unlock:A to __printk_cpu_unlock:B
+        * RELEASE from __printk_cpu_sync_put:A to __printk_cpu_sync_put:B
         * of this CPU
         *    matching
-        * ACQUIRE from __printk_cpu_trylock:A to __printk_cpu_trylock:B
-        * of the next CPU
+        * ACQUIRE from __printk_cpu_sync_try_get:A to
+        * __printk_cpu_sync_try_get:B of the next CPU
         */
-       atomic_set_release(&printk_cpulock_owner,
-                          -1); /* LMM(__printk_cpu_unlock:B) */
+       atomic_set_release(&printk_cpu_sync_owner,
+                          -1); /* LMM(__printk_cpu_sync_put:B) */
 }
-EXPORT_SYMBOL(__printk_cpu_unlock);
+EXPORT_SYMBOL(__printk_cpu_sync_put);
 #endif /* CONFIG_SMP */
index 6b7f1bf6715d8abb493a04ede2539018eaef3287..83471e81501a7249306ef62e173c4dce7960adc7 100644 (file)
@@ -102,9 +102,9 @@ asmlinkage __visible void dump_stack_lvl(const char *log_lvl)
         * Permit this cpu to perform nested stack dumps while serialising
         * against other CPUs
         */
-       printk_cpu_lock_irqsave(flags);
+       printk_cpu_sync_get_irqsave(flags);
        __dump_stack(log_lvl);
-       printk_cpu_unlock_irqrestore(flags);
+       printk_cpu_sync_put_irqrestore(flags);
 }
 EXPORT_SYMBOL(dump_stack_lvl);
 
index 199ab201d5019c7c216f4db0f32da947e1d4503c..d01aec6ae15c824bdea3a3614d1c5c291a1aafc0 100644 (file)
@@ -99,7 +99,7 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
                 * Allow nested NMI backtraces while serializing
                 * against other CPUs.
                 */
-               printk_cpu_lock_irqsave(flags);
+               printk_cpu_sync_get_irqsave(flags);
                if (!READ_ONCE(backtrace_idle) && regs && cpu_in_idle(instruction_pointer(regs))) {
                        pr_warn("NMI backtrace for cpu %d skipped: idling at %pS\n",
                                cpu, (void *)instruction_pointer(regs));
@@ -110,7 +110,7 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
                        else
                                dump_stack();
                }
-               printk_cpu_unlock_irqrestore(flags);
+               printk_cpu_sync_put_irqrestore(flags);
                cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
                return true;
        }