Merge tag 'printk-for-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/printk...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 4 Sep 2023 20:20:19 +0000 (13:20 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 4 Sep 2023 20:20:19 +0000 (13:20 -0700)
Pull printk updates from Petr Mladek:

 - Do not try to get the console lock when it is not need or useful in
   panic()

 - Replace the global console_suspended state by a per-console flag

 - Export symbols needed for dumping the raw printk buffer in panic()

 - Fix documentation of printf formats for integer types

 - Moved Sergey Senozhatsky to the reviewer role

 - Misc cleanups

* tag 'printk-for-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux:
  printk: export symbols for debug modules
  lib: test_scanf: Add explicit type cast to result initialization in test_number_prefix()
  printk: ringbuffer: Fix truncating buffer size min_t cast
  printk: Rename abandon_console_lock_in_panic() to other_cpu_in_panic()
  printk: Add per-console suspended state
  printk: Consolidate console deferred printing
  printk: Do not take console lock for console_flush_on_panic()
  printk: Keep non-panic-CPUs out of console lock
  printk: Reduce console_unblank() usage in unsafe scenarios
  kdb: Do not assume write() callback available
  docs: printk-formats: Treat char as always unsigned
  docs: printk-formats: Fix hex printing of signed values
  MAINTAINERS: adjust printk/vsprintf entries

Documentation/core-api/printk-formats.rst
MAINTAINERS
include/linux/console.h
kernel/debug/kdb/kdb_io.c
kernel/printk/internal.h
kernel/printk/printk.c
kernel/printk/printk_ringbuffer.c
kernel/printk/printk_safe.c
lib/test_scanf.c

index dfe7e75a71dece99cd691ffe419365152e4a01f7..4451ef501936133998b059d5b0482328845e02ba 100644 (file)
@@ -15,9 +15,10 @@ Integer types
 
        If variable is of Type,         use printk format specifier:
        ------------------------------------------------------------
-               char                    %d or %x
+               signed char             %d or %hhx
                unsigned char           %u or %x
-               short int               %d or %x
+               char                    %u or %x
+               short int               %d or %hx
                unsigned short int      %u or %x
                int                     %d or %x
                unsigned int            %u or %x
@@ -27,9 +28,9 @@ Integer types
                unsigned long long      %llu or %llx
                size_t                  %zu or %zx
                ssize_t                 %zd or %zx
-               s8                      %d or %x
+               s8                      %d or %hhx
                u8                      %u or %x
-               s16                     %d or %x
+               s16                     %d or %hx
                u16                     %u or %x
                s32                     %d or %x
                u32                     %u or %x
index 1cc0be8ea75afefb75440a42e4c78823bcb52119..fa695ab1791acaa8aa2801b3b7be5a4513ce33c6 100644 (file)
@@ -17176,9 +17176,9 @@ F:      kernel/sched/psi.c
 
 PRINTK
 M:     Petr Mladek <pmladek@suse.com>
-M:     Sergey Senozhatsky <senozhatsky@chromium.org>
 R:     Steven Rostedt <rostedt@goodmis.org>
 R:     John Ogness <john.ogness@linutronix.de>
+R:     Sergey Senozhatsky <senozhatsky@chromium.org>
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git
 F:     include/linux/printk.h
@@ -23072,9 +23072,9 @@ F:      drivers/net/vrf.c
 VSPRINTF
 M:     Petr Mladek <pmladek@suse.com>
 M:     Steven Rostedt <rostedt@goodmis.org>
-M:     Sergey Senozhatsky <senozhatsky@chromium.org>
 R:     Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 R:     Rasmus Villemoes <linux@rasmusvillemoes.dk>
+R:     Sergey Senozhatsky <senozhatsky@chromium.org>
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git
 F:     Documentation/core-api/printk-formats.rst
index d3195664baa5a4288660698f62ed10ff318a7a47..7de11c763eb35deed528272727bf2f2bf4d18701 100644 (file)
@@ -154,6 +154,8 @@ static inline int con_debug_leave(void)
  *                     receiving the printk spam for obvious reasons.
  * @CON_EXTENDED:      The console supports the extended output format of
  *                     /dev/kmesg which requires a larger output buffer.
+ * @CON_SUSPENDED:     Indicates if a console is suspended. If true, the
+ *                     printing callbacks must not be called.
  */
 enum cons_flags {
        CON_PRINTBUFFER         = BIT(0),
@@ -163,6 +165,7 @@ enum cons_flags {
        CON_ANYTIME             = BIT(4),
        CON_BRL                 = BIT(5),
        CON_EXTENDED            = BIT(6),
+       CON_SUSPENDED           = BIT(7),
 };
 
 /**
index 813cb6cf72d6b59bcf73edf75c761334e0b7fd73..9443bc63c5a245548a1390ee85dd096091649607 100644 (file)
@@ -590,6 +590,8 @@ static void kdb_msg_write(const char *msg, int msg_len)
                        continue;
                if (c == dbg_io_ops->cons)
                        continue;
+               if (!c->write)
+                       continue;
                /*
                 * Set oops_in_progress to encourage the console drivers to
                 * disregard their internal spin locks: in the current calling
index 2a17704136f1dbbb3394a73813a4de40873d684b..7d4979d5c3ce6f1ded693de5f44df1db12e7d389 100644 (file)
@@ -103,3 +103,5 @@ struct printk_message {
        u64                     seq;
        unsigned long           dropped;
 };
+
+bool other_cpu_in_panic(void);
index 357a4d18f6387cc5a626346dea2d3518fc2e4c86..96fc38cb2e8415f6d1572384833ea8a55b3b90b7 100644 (file)
@@ -88,7 +88,7 @@ EXPORT_SYMBOL(oops_in_progress);
 static DEFINE_MUTEX(console_mutex);
 
 /*
- * console_sem protects updates to console->seq and console_suspended,
+ * console_sem protects updates to console->seq
  * and also provides serialization for console printing.
  */
 static DEFINE_SEMAPHORE(console_sem, 1);
@@ -361,7 +361,7 @@ static bool panic_in_progress(void)
  * paths in the console code where we end up in places I want
  * locked without the console semaphore held).
  */
-static int console_locked, console_suspended;
+static int console_locked;
 
 /*
  *     Array of consoles built from command line options (console=)
@@ -538,12 +538,14 @@ char *log_buf_addr_get(void)
 {
        return log_buf;
 }
+EXPORT_SYMBOL_GPL(log_buf_addr_get);
 
 /* Return log buffer size */
 u32 log_buf_len_get(void)
 {
        return log_buf_len;
 }
+EXPORT_SYMBOL_GPL(log_buf_len_get);
 
 /*
  * Define how much of the log buffer we could take at maximum. The value
@@ -2308,7 +2310,11 @@ asmlinkage int vprintk_emit(int facility, int level,
                preempt_enable();
        }
 
-       wake_up_klogd();
+       if (in_sched)
+               defer_console_output();
+       else
+               wake_up_klogd();
+
        return printed_len;
 }
 EXPORT_SYMBOL(vprintk_emit);
@@ -2547,22 +2553,46 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig
  */
 void suspend_console(void)
 {
+       struct console *con;
+
        if (!console_suspend_enabled)
                return;
        pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
        pr_flush(1000, true);
-       console_lock();
-       console_suspended = 1;
-       up_console_sem();
+
+       console_list_lock();
+       for_each_console(con)
+               console_srcu_write_flags(con, con->flags | CON_SUSPENDED);
+       console_list_unlock();
+
+       /*
+        * Ensure that all SRCU list walks have completed. All printing
+        * contexts must be able to see that they are suspended so that it
+        * is guaranteed that all printing has stopped when this function
+        * completes.
+        */
+       synchronize_srcu(&console_srcu);
 }
 
 void resume_console(void)
 {
+       struct console *con;
+
        if (!console_suspend_enabled)
                return;
-       down_console_sem();
-       console_suspended = 0;
-       console_unlock();
+
+       console_list_lock();
+       for_each_console(con)
+               console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED);
+       console_list_unlock();
+
+       /*
+        * Ensure that all SRCU list walks have completed. All printing
+        * contexts must be able to see they are no longer suspended so
+        * that they are guaranteed to wake up and resume printing.
+        */
+       synchronize_srcu(&console_srcu);
+
        pr_flush(1000, true);
 }
 
@@ -2585,6 +2615,26 @@ static int console_cpu_notify(unsigned int cpu)
        return 0;
 }
 
+/*
+ * Return true if a panic is in progress on a remote CPU.
+ *
+ * On true, the local CPU should immediately release any printing resources
+ * that may be needed by the panic CPU.
+ */
+bool other_cpu_in_panic(void)
+{
+       if (!panic_in_progress())
+               return false;
+
+       /*
+        * We can use raw_smp_processor_id() here because it is impossible for
+        * the task to be migrated to the panic_cpu, or away from it. If
+        * panic_cpu has already been set, and we're not currently executing on
+        * that CPU, then we never will be.
+        */
+       return atomic_read(&panic_cpu) != raw_smp_processor_id();
+}
+
 /**
  * console_lock - block the console subsystem from printing
  *
@@ -2597,9 +2647,11 @@ void console_lock(void)
 {
        might_sleep();
 
+       /* On panic, the console_lock must be left to the panic cpu. */
+       while (other_cpu_in_panic())
+               msleep(1000);
+
        down_console_sem();
-       if (console_suspended)
-               return;
        console_locked = 1;
        console_may_schedule = 1;
 }
@@ -2615,12 +2667,11 @@ EXPORT_SYMBOL(console_lock);
  */
 int console_trylock(void)
 {
-       if (down_trylock_console_sem())
+       /* On panic, the console_lock must be left to the panic cpu. */
+       if (other_cpu_in_panic())
                return 0;
-       if (console_suspended) {
-               up_console_sem();
+       if (down_trylock_console_sem())
                return 0;
-       }
        console_locked = 1;
        console_may_schedule = 0;
        return 1;
@@ -2633,25 +2684,6 @@ int is_console_locked(void)
 }
 EXPORT_SYMBOL(is_console_locked);
 
-/*
- * Return true when this CPU should unlock console_sem without pushing all
- * messages to the console. This reduces the chance that the console is
- * locked when the panic CPU tries to use it.
- */
-static bool abandon_console_lock_in_panic(void)
-{
-       if (!panic_in_progress())
-               return false;
-
-       /*
-        * We can use raw_smp_processor_id() here because it is impossible for
-        * the task to be migrated to the panic_cpu, or away from it. If
-        * panic_cpu has already been set, and we're not currently executing on
-        * that CPU, then we never will be.
-        */
-       return atomic_read(&panic_cpu) != raw_smp_processor_id();
-}
-
 /*
  * Check if the given console is currently capable and allowed to print
  * records.
@@ -2665,6 +2697,9 @@ static inline bool console_is_usable(struct console *con)
        if (!(flags & CON_ENABLED))
                return false;
 
+       if ((flags & CON_SUSPENDED))
+               return false;
+
        if (!con->write)
                return false;
 
@@ -2948,7 +2983,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove
                        any_progress = true;
 
                        /* Allow panic_cpu to take over the consoles safely. */
-                       if (abandon_console_lock_in_panic())
+                       if (other_cpu_in_panic())
                                goto abandon;
 
                        if (do_cond_resched)
@@ -2983,11 +3018,6 @@ void console_unlock(void)
        bool flushed;
        u64 next_seq;
 
-       if (console_suspended) {
-               up_console_sem();
-               return;
-       }
-
        /*
         * Console drivers are called with interrupts disabled, so
         * @console_may_schedule should be cleared before; however, we may
@@ -3045,9 +3075,27 @@ EXPORT_SYMBOL(console_conditional_schedule);
 
 void console_unblank(void)
 {
+       bool found_unblank = false;
        struct console *c;
        int cookie;
 
+       /*
+        * First check if there are any consoles implementing the unblank()
+        * callback. If not, there is no reason to continue and take the
+        * console lock, which in particular can be dangerous if
+        * @oops_in_progress is set.
+        */
+       cookie = console_srcu_read_lock();
+       for_each_console_srcu(c) {
+               if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank) {
+                       found_unblank = true;
+                       break;
+               }
+       }
+       console_srcu_read_unlock(cookie);
+       if (!found_unblank)
+               return;
+
        /*
         * Stop console printing because the unblank() callback may
         * assume the console is not within its write() callback.
@@ -3056,6 +3104,16 @@ void console_unblank(void)
         * In that case, attempt a trylock as best-effort.
         */
        if (oops_in_progress) {
+               /* Semaphores are not NMI-safe. */
+               if (in_nmi())
+                       return;
+
+               /*
+                * Attempting to trylock the console lock can deadlock
+                * if another CPU was stopped while modifying the
+                * semaphore. "Hope and pray" that this is not the
+                * current situation.
+                */
                if (down_trylock_console_sem() != 0)
                        return;
        } else
@@ -3085,14 +3143,24 @@ void console_unblank(void)
  */
 void console_flush_on_panic(enum con_flush_mode mode)
 {
+       bool handover;
+       u64 next_seq;
+
        /*
-        * If someone else is holding the console lock, trylock will fail
-        * and may_schedule may be set.  Ignore and proceed to unlock so
-        * that messages are flushed out.  As this can be called from any
-        * context and we don't want to get preempted while flushing,
-        * ensure may_schedule is cleared.
+        * Ignore the console lock and flush out the messages. Attempting a
+        * trylock would not be useful because:
+        *
+        *   - if it is contended, it must be ignored anyway
+        *   - console_lock() and console_trylock() block and fail
+        *     respectively in panic for non-panic CPUs
+        *   - semaphores are not NMI-safe
+        */
+
+       /*
+        * If another context is holding the console lock,
+        * @console_may_schedule might be set. Clear it so that
+        * this context does not call cond_resched() while flushing.
         */
-       console_trylock();
        console_may_schedule = 0;
 
        if (mode == CONSOLE_REPLAY_ALL) {
@@ -3105,15 +3173,15 @@ void console_flush_on_panic(enum con_flush_mode mode)
                cookie = console_srcu_read_lock();
                for_each_console_srcu(c) {
                        /*
-                        * If the above console_trylock() failed, this is an
-                        * unsynchronized assignment. But in that case, the
+                        * This is an unsynchronized assignment, but the
                         * kernel is in "hope and pray" mode anyway.
                         */
                        c->seq = seq;
                }
                console_srcu_read_unlock(cookie);
        }
-       console_unlock();
+
+       console_flush_all(false, &next_seq, &handover);
 }
 
 /*
@@ -3679,8 +3747,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
 
                /*
                 * Hold the console_lock to guarantee safe access to
-                * console->seq and to prevent changes to @console_suspended
-                * until all consoles have been processed.
+                * console->seq.
                 */
                console_lock();
 
@@ -3688,6 +3755,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
                for_each_console_srcu(c) {
                        if (con && con != c)
                                continue;
+                       /*
+                        * If consoles are not usable, it cannot be expected
+                        * that they make forward progress, so only increment
+                        * @diff for usable consoles.
+                        */
                        if (!console_is_usable(c))
                                continue;
                        printk_seq = c->seq;
@@ -3696,18 +3768,12 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
                }
                console_srcu_read_unlock(cookie);
 
-               /*
-                * If consoles are suspended, it cannot be expected that they
-                * make forward progress, so timeout immediately. @diff is
-                * still used to return a valid flush status.
-                */
-               if (console_suspended)
-                       remaining = 0;
-               else if (diff != last_diff && reset_on_progress)
+               if (diff != last_diff && reset_on_progress)
                        remaining = timeout_ms;
 
                console_unlock();
 
+               /* Note: @diff is 0 if there are no usable consoles. */
                if (diff == 0 || remaining == 0)
                        break;
 
@@ -3741,7 +3807,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
  * printer has been seen to make some forward progress.
  *
  * Context: Process context. May sleep while acquiring console lock.
- * Return: true if all enabled printers are caught up.
+ * Return: true if all usable printers are caught up.
  */
 static bool pr_flush(int timeout_ms, bool reset_on_progress)
 {
@@ -3798,11 +3864,33 @@ static void __wake_up_klogd(int val)
        preempt_enable();
 }
 
+/**
+ * wake_up_klogd - Wake kernel logging daemon
+ *
+ * Use this function when new records have been added to the ringbuffer
+ * and the console printing of those records has already occurred or is
+ * known to be handled by some other context. This function will only
+ * wake the logging daemon.
+ *
+ * Context: Any context.
+ */
 void wake_up_klogd(void)
 {
        __wake_up_klogd(PRINTK_PENDING_WAKEUP);
 }
 
+/**
+ * defer_console_output - Wake kernel logging daemon and trigger
+ *     console printing in a deferred context
+ *
+ * Use this function when new records have been added to the ringbuffer,
+ * this context is responsible for console printing those records, but
+ * the current context is not allowed to perform the console printing.
+ * Trigger an irq_work context to perform the console printing. This
+ * function also wakes the logging daemon.
+ *
+ * Context: Any context.
+ */
 void defer_console_output(void)
 {
        /*
@@ -3819,12 +3907,7 @@ void printk_trigger_flush(void)
 
 int vprintk_deferred(const char *fmt, va_list args)
 {
-       int r;
-
-       r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args);
-       defer_console_output();
-
-       return r;
+       return vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args);
 }
 
 int _printk_deferred(const char *fmt, ...)
index 2dc4d5a1f1ff824031a25c8d0cb5dd68925d6b14..fde338606ce83c1a013a270eac64dda039d4e677 100644 (file)
@@ -1735,7 +1735,7 @@ static bool copy_data(struct prb_data_ring *data_ring,
        if (!buf || !buf_size)
                return true;
 
-       data_size = min_t(u16, buf_size, len);
+       data_size = min_t(unsigned int, buf_size, len);
 
        memcpy(&buf[0], data, data_size); /* LMM(copy_data:A) */
        return true;
index ef0f9a2044da10739e4ec865ab35e97db9c02d5f..6d10927a07d835043d1fe9f383053b9254691742 100644 (file)
@@ -38,13 +38,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
         * Use the main logbuf even in NMI. But avoid calling console
         * drivers that might have their own locks.
         */
-       if (this_cpu_read(printk_context) || in_nmi()) {
-               int len;
-
-               len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args);
-               defer_console_output();
-               return len;
-       }
+       if (this_cpu_read(printk_context) || in_nmi())
+               return vprintk_deferred(fmt, args);
 
        /* No obstacles. */
        return vprintk_default(fmt, args);
index b620cf7de503542a98f38c45c76e9043b242746e..a2707af2951ab07ca7cd44aeceed15aff4b79cd1 100644 (file)
@@ -606,7 +606,7 @@ static void __init numbers_slice(void)
 #define test_number_prefix(T, str, scan_fmt, expect0, expect1, n_args, fn)     \
 do {                                                                           \
        const T expect[2] = { expect0, expect1 };                               \
-       T result[2] = {~expect[0], ~expect[1]};                                 \
+       T result[2] = { (T)~expect[0], (T)~expect[1] };                         \
                                                                                \
        _test(fn, &expect, str, scan_fmt, n_args, &result[0], &result[1]);      \
 } while (0)