Commit d5d8d3d0 authored by Petr Mladek's avatar Petr Mladek Committed by Linus Torvalds

kdb: properly synchronize vkdb_printf() calls with other CPUs

kdb_printf_lock does not prevent other CPUs from entering the critical
section because it is ignored when KDB_STATE_PRINTF_LOCK is set.

The problematic situation might look like:

CPU0					CPU1

vkdb_printf()
  if (!KDB_STATE(PRINTF_LOCK))
    KDB_STATE_SET(PRINTF_LOCK);
    spin_lock_irqsave(&kdb_printf_lock, flags);

					vkdb_printf()
					  if (!KDB_STATE(PRINTF_LOCK))

BANG: The PRINTF_LOCK state is set and CPU1 is entering the critical
section without spinning on the lock.

The problem is that the code tries to implement locking using two state
variables that are not handled atomically.  Well, we need a custom
locking because we want to allow reentering the critical section on the
very same CPU.

Let's use solution from Petr Zijlstra that was proposed for a similar
scenario, see
https://lkml.kernel.org/r/20161018171513.734367391@infradead.org

This patch uses the same trick with cmpxchg().  The only difference is
that we want to handle only recursion from the same context and
therefore we disable interrupts.

In addition, KDB_STATE_PRINTF_LOCK is removed.  In fact, we are not able
to set it a non-racy way.

Link: http://lkml.kernel.org/r/1480412276-16690-3-git-send-email-pmladek@suse.comSigned-off-by: default avatarPetr Mladek <pmladek@suse.com>
Reviewed-by: default avatarDaniel Thompson <daniel.thompson@linaro.org>
Cc: Jason Wessel <jason.wessel@windriver.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d1bd8ead
...@@ -555,16 +555,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) ...@@ -555,16 +555,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
int colcount; int colcount;
int logging, saved_loglevel = 0; int logging, saved_loglevel = 0;
int saved_trap_printk; int saved_trap_printk;
int got_printf_lock = 0;
int retlen = 0; int retlen = 0;
int fnd, len; int fnd, len;
int this_cpu, old_cpu;
static int kdb_printf_cpu = -1;
char *cp, *cp2, *cphold = NULL, replaced_byte = ' '; char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
char *moreprompt = "more> "; char *moreprompt = "more> ";
struct console *c = console_drivers; struct console *c = console_drivers;
static DEFINE_SPINLOCK(kdb_printf_lock);
unsigned long uninitialized_var(flags); unsigned long uninitialized_var(flags);
preempt_disable(); local_irq_save(flags);
saved_trap_printk = kdb_trap_printk; saved_trap_printk = kdb_trap_printk;
kdb_trap_printk = 0; kdb_trap_printk = 0;
...@@ -572,12 +572,13 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) ...@@ -572,12 +572,13 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
* But if any cpu goes recursive in kdb, just print the output, * But if any cpu goes recursive in kdb, just print the output,
* even if it is interleaved with any other text. * even if it is interleaved with any other text.
*/ */
if (!KDB_STATE(PRINTF_LOCK)) { this_cpu = smp_processor_id();
KDB_STATE_SET(PRINTF_LOCK); for (;;) {
spin_lock_irqsave(&kdb_printf_lock, flags); old_cpu = cmpxchg(&kdb_printf_cpu, -1, this_cpu);
got_printf_lock = 1; if (old_cpu == -1 || old_cpu == this_cpu)
} else { break;
__acquire(kdb_printf_lock);
cpu_relax();
} }
diag = kdbgetintenv("LINES", &linecount); diag = kdbgetintenv("LINES", &linecount);
...@@ -846,15 +847,10 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) ...@@ -846,15 +847,10 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
suspend_grep = 0; /* end of what may have been a recursive call */ suspend_grep = 0; /* end of what may have been a recursive call */
if (logging) if (logging)
console_loglevel = saved_loglevel; console_loglevel = saved_loglevel;
if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) { /* kdb_printf_cpu locked the code above. */
got_printf_lock = 0; smp_store_release(&kdb_printf_cpu, old_cpu);
spin_unlock_irqrestore(&kdb_printf_lock, flags);
KDB_STATE_CLEAR(PRINTF_LOCK);
} else {
__release(kdb_printf_lock);
}
kdb_trap_printk = saved_trap_printk; kdb_trap_printk = saved_trap_printk;
preempt_enable(); local_irq_restore(flags);
return retlen; return retlen;
} }
......
...@@ -132,7 +132,6 @@ extern int kdb_state; ...@@ -132,7 +132,6 @@ extern int kdb_state;
#define KDB_STATE_PAGER 0x00000400 /* pager is available */ #define KDB_STATE_PAGER 0x00000400 /* pager is available */
#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching #define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching
* back to initial cpu */ * back to initial cpu */
#define KDB_STATE_PRINTF_LOCK 0x00001000 /* Holds kdb_printf lock */
#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */ #define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */
#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */ #define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */
#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been #define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment