From: Petr Mladek <pmladek@suse.com>
To: Jason Wessel <jason.wessel@windriver.com>
Cc: Daniel Thompson <daniel.thompson@linaro.org>,
Peter Zijlstra <peterz@infradead.org>,
Andrew Morton <akpm@linux-foundation.org>,
Sergey Senozhatsky <sergey.senozhatsky@gmail.com>,
linux-kernel@vger.kernel.org, Petr Mladek <pmladek@suse.com>
Subject: [PATCH 1/2] kdb: Properly synchronize vkdb_printf() calls with other CPUs
Date: Fri, 21 Oct 2016 14:50:34 +0200 [thread overview]
Message-ID: <1477054235-1624-2-git-send-email-pmladek@suse.com> (raw)
In-Reply-To: <1477054235-1624-1-git-send-email-pmladek@suse.com>
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, we want to be on the safe side and update "kdb_event" outside
of the critical section. Therefore we need an extra barrier before it gets
decremented.
Note that "kdb_event" is always incremented/decremented now. But it should
not cause any harm. The important information is whether it is zero or not.
Finally, KDB_STATE_PRINTF_LOCK is removed. In fact, we are not able to set
it a non-racy way.
Signed-off-by: Petr Mladek <pmladek@suse.com>
---
kernel/debug/kdb/kdb_io.c | 36 +++++++++++++++++-------------------
kernel/debug/kdb/kdb_private.h | 1 -
2 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index fc1ef736253c..227b59ec7dbe 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)
int colcount;
int logging, saved_loglevel = 0;
int saved_trap_printk;
- int got_printf_lock = 0;
int retlen = 0;
int fnd, len;
+ int this_cpu, old_cpu;
+ static int kdb_printf_cpu = -1;
char *cp, *cp2, *cphold = NULL, replaced_byte = ' ';
char *moreprompt = "more> ";
struct console *c = console_drivers;
- static DEFINE_SPINLOCK(kdb_printf_lock);
unsigned long uninitialized_var(flags);
- preempt_disable();
+ local_irq_save(flags);
saved_trap_printk = kdb_trap_printk;
kdb_trap_printk = 0;
@@ -572,13 +572,14 @@ 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,
* even if it is interleaved with any other text.
*/
- if (!KDB_STATE(PRINTF_LOCK)) {
- KDB_STATE_SET(PRINTF_LOCK);
- spin_lock_irqsave(&kdb_printf_lock, flags);
- got_printf_lock = 1;
- atomic_inc(&kdb_event);
- } else {
- __acquire(kdb_printf_lock);
+ this_cpu = smp_processor_id();
+ atomic_inc(&kdb_event);
+ for (;;) {
+ old_cpu = cmpxchg(&kdb_printf_cpu, -1, this_cpu);
+ if (old_cpu == -1 || old_cpu == this_cpu)
+ break;
+
+ cpu_relax();
}
diag = kdbgetintenv("LINES", &linecount);
@@ -847,16 +848,13 @@ 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 */
if (logging)
console_loglevel = saved_loglevel;
- if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) {
- got_printf_lock = 0;
- spin_unlock_irqrestore(&kdb_printf_lock, flags);
- KDB_STATE_CLEAR(PRINTF_LOCK);
- atomic_dec(&kdb_event);
- } else {
- __release(kdb_printf_lock);
- }
+ /* kdb_printf_cpu locked the code above. */
+ smp_store_release(&kdb_printf_cpu, old_cpu);
+ /* Update kdb_event around the locked section. */
+ smp_mb__before_atomic();
+ atomic_dec(&kdb_event);
kdb_trap_printk = saved_trap_printk;
- preempt_enable();
+ local_irq_restore(flags);
return retlen;
}
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 kdbgetaddrarg(int, const char **, int*, unsigned long *,
#define KDB_STATE_PAGER 0x00000400 /* pager is available */
#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching
* 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_RECURSE 0x00004000 /* Recursive entry to kdb */
#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been
--
1.8.5.6
next prev parent reply other threads:[~2016-10-21 12:51 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-10-21 12:50 [PATCH 0/2] kdb: Fix locking in vkdb_printf() Petr Mladek
2016-10-21 12:50 ` Petr Mladek [this message]
2016-11-07 10:07 ` [PATCH 1/2] kdb: Properly synchronize vkdb_printf() calls with other CPUs Daniel Thompson
2016-11-22 10:34 ` Petr Mladek
2016-10-21 12:50 ` [PATCH 2/2] kdb: Call vkdb_printf() from vprintk_default() only when wanted Petr Mladek
2016-10-23 13:23 ` Sergey Senozhatsky
2016-11-22 12:14 ` Petr Mladek
2016-11-07 10:24 ` Daniel Thompson
2016-11-22 12:45 ` Petr Mladek
2016-11-22 14:32 ` Daniel Thompson
2016-11-23 16:29 ` Petr Mladek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1477054235-1624-2-git-send-email-pmladek@suse.com \
--to=pmladek@suse.com \
--cc=akpm@linux-foundation.org \
--cc=daniel.thompson@linaro.org \
--cc=jason.wessel@windriver.com \
--cc=linux-kernel@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=sergey.senozhatsky@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome