aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/debug
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.com>2016-12-14 18:05:55 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-14 19:04:08 -0500
commitd5d8d3d0d4adcc3aec6e2e0fb656165014a712b7 (patch)
tree090aa03f64ff9535310f073d3574fb692f720fa9 /kernel/debug
parentd1bd8ead126668a2d6c42d97cc3664e95b3fa1dc (diff)
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.com Signed-off-by: Petr Mladek <pmladek@suse.com> Reviewed-by: Daniel 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: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/debug')
-rw-r--r--kernel/debug/kdb/kdb_io.c30
-rw-r--r--kernel/debug/kdb/kdb_private.h1
2 files changed, 13 insertions, 18 deletions
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 46f477bebe0c..daa76154fb1b 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -555,16 +555,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
555 int colcount; 555 int colcount;
556 int logging, saved_loglevel = 0; 556 int logging, saved_loglevel = 0;
557 int saved_trap_printk; 557 int saved_trap_printk;
558 int got_printf_lock = 0;
559 int retlen = 0; 558 int retlen = 0;
560 int fnd, len; 559 int fnd, len;
560 int this_cpu, old_cpu;
561 static int kdb_printf_cpu = -1;
561 char *cp, *cp2, *cphold = NULL, replaced_byte = ' '; 562 char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
562 char *moreprompt = "more> "; 563 char *moreprompt = "more> ";
563 struct console *c = console_drivers; 564 struct console *c = console_drivers;
564 static DEFINE_SPINLOCK(kdb_printf_lock);
565 unsigned long uninitialized_var(flags); 565 unsigned long uninitialized_var(flags);
566 566
567 preempt_disable(); 567 local_irq_save(flags);
568 saved_trap_printk = kdb_trap_printk; 568 saved_trap_printk = kdb_trap_printk;
569 kdb_trap_printk = 0; 569 kdb_trap_printk = 0;
570 570
@@ -572,12 +572,13 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
572 * But if any cpu goes recursive in kdb, just print the output, 572 * But if any cpu goes recursive in kdb, just print the output,
573 * even if it is interleaved with any other text. 573 * even if it is interleaved with any other text.
574 */ 574 */
575 if (!KDB_STATE(PRINTF_LOCK)) { 575 this_cpu = smp_processor_id();
576 KDB_STATE_SET(PRINTF_LOCK); 576 for (;;) {
577 spin_lock_irqsave(&kdb_printf_lock, flags); 577 old_cpu = cmpxchg(&kdb_printf_cpu, -1, this_cpu);
578 got_printf_lock = 1; 578 if (old_cpu == -1 || old_cpu == this_cpu)
579 } else { 579 break;
580 __acquire(kdb_printf_lock); 580
581 cpu_relax();
581 } 582 }
582 583
583 diag = kdbgetintenv("LINES", &linecount); 584 diag = kdbgetintenv("LINES", &linecount);
@@ -846,15 +847,10 @@ kdb_print_out:
846 suspend_grep = 0; /* end of what may have been a recursive call */ 847 suspend_grep = 0; /* end of what may have been a recursive call */
847 if (logging) 848 if (logging)
848 console_loglevel = saved_loglevel; 849 console_loglevel = saved_loglevel;
849 if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) { 850 /* kdb_printf_cpu locked the code above. */
850 got_printf_lock = 0; 851 smp_store_release(&kdb_printf_cpu, old_cpu);
851 spin_unlock_irqrestore(&kdb_printf_lock, flags);
852 KDB_STATE_CLEAR(PRINTF_LOCK);
853 } else {
854 __release(kdb_printf_lock);
855 }
856 kdb_trap_printk = saved_trap_printk; 852 kdb_trap_printk = saved_trap_printk;
857 preempt_enable(); 853 local_irq_restore(flags);
858 return retlen; 854 return retlen;
859} 855}
860 856
diff --git a/kernel/debug/kdb/kdb_private.h b/kernel/debug/kdb/kdb_private.h
index 75014d7f4568..fc224fbcf954 100644
--- a/kernel/debug/kdb/kdb_private.h
+++ b/kernel/debug/kdb/kdb_private.h
@@ -132,7 +132,6 @@ extern int kdb_state;
132#define KDB_STATE_PAGER 0x00000400 /* pager is available */ 132#define KDB_STATE_PAGER 0x00000400 /* pager is available */
133#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching 133#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching
134 * back to initial cpu */ 134 * back to initial cpu */
135#define KDB_STATE_PRINTF_LOCK 0x00001000 /* Holds kdb_printf lock */
136#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */ 135#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */
137#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */ 136#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */
138#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been 137#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been