* printk: Cleanup and softlockup avoidance
@ 2013-12-17 14:48 Jan Kara
2013-12-17 14:48 ` [PATCH 1/3] printk: Release lockbuf_lock before calling console_trylock_for_printk() Jan Kara
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Jan Kara @ 2013-12-17 14:48 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, mhocko, pmladek, Steven Rostedt, Jan Kara
Hello,
this is another piece of the printk softlockup saga series. Let me first
remind the problem:
Currently, console_unlock() prints messages from kernel printk buffer to
console while the buffer is non-empty. When serial console is attached,
printing is slow and thus other CPUs in the system have plenty of time
to append new messages to the buffer while one CPU is printing. Thus the
CPU can spend unbounded amount of time doing printing in console_unlock().
This is especially serious since vprintk_emit() calls console_unlock()
with interrupts disabled.
In practice users have observed a CPU can spend tens of seconds printing
in console_unlock() (usually during boot when hundreds of SCSI devices
are discovered) resulting in RCU stalls (CPU doing printing doesn't
reach quiescent state for a long time), softlockup reports (IPIs for the
printing CPU don't get served and thus other CPUs are spinning waiting
for the printing CPU to process IPIs), and eventually a machine death
(as messages from stalls and lockups append to printk buffer faster than
we are able to print). So these machines are unable to boot with serial
console attached. Also during artificial stress testing SATA disk
disappears from the system because its interrupts aren't served for too
long.
---
Since my previous attempts to fix softlockups in printk under heavy load met
some resistance, I've decided to try a different approach - do not let
CPU out of the console_unlock() loop until there's someone else to take over
the printing. However that approach will need some changes in how
smp_call_function_any() works (as I need to make sure someone will come
sooner rather than later to rescue the printing CPU) and I don't want to
make already contentious series even worse. So to move at least by a bit,
let me post the following three patches that improve the situation by not
calling console_unlock() with interrupts disabled. I think they make sense
on their own (the third patch is actually an independent cleanup).
What do you guys think?
Honza
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] printk: Release lockbuf_lock before calling console_trylock_for_printk()
2013-12-17 14:48 printk: Cleanup and softlockup avoidance Jan Kara
@ 2013-12-17 14:48 ` Jan Kara
2013-12-17 14:48 ` [PATCH 2/3] printk: Enable interrupts " Jan Kara
2013-12-17 14:48 ` [PATCH 3/3] printk: Remove separate printk_sched buffers and use printk buf instead Jan Kara
2 siblings, 0 replies; 5+ messages in thread
From: Jan Kara @ 2013-12-17 14:48 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, mhocko, pmladek, Steven Rostedt, Jan Kara
There's no reason to hold lockbuf_lock when entering
console_trylock_for_printk(). The first thing this function does is
calling down_trylock(console_sem) and if that fails it immediately
unlocks lockbuf_lock. So lockbuf_lock isn't needed for that branch.
When down_trylock() succeeds, the rest of console_trylock() is OK
without lockbuf_lock (it is called without it from other places), and
the only remaining thing in console_trylock_for_printk() is
can_use_console() call. For that call console_sem is enough (it
iterates all consoles and checks CON_ANYTIME flag).
So we drop logbuf_lock before entering console_trylock_for_printk()
which simplifies the code.
Signed-off-by: Jan Kara <jack@suse.cz>
---
kernel/printk/printk.c | 49 +++++++++++++++++--------------------------------
1 file changed, 17 insertions(+), 32 deletions(-)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index be7c86bae576..72ec3637115f 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -250,9 +250,6 @@ static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;
-/* cpu currently holding logbuf_lock */
-static volatile unsigned int logbuf_cpu = UINT_MAX;
-
/* human readable text of the record */
static char *log_text(const struct printk_log *msg)
{
@@ -1338,36 +1335,22 @@ static inline int can_use_console(unsigned int cpu)
* messages from a 'printk'. Return true (and with the
* console_lock held, and 'console_locked' set) if it
* is successful, false otherwise.
- *
- * This gets called with the 'logbuf_lock' spinlock held and
- * interrupts disabled. It should return with 'lockbuf_lock'
- * released but interrupts still disabled.
*/
static int console_trylock_for_printk(unsigned int cpu)
- __releases(&logbuf_lock)
{
- int retval = 0, wake = 0;
-
- if (console_trylock()) {
- retval = 1;
-
- /*
- * If we can't use the console, we need to release
- * the console semaphore by hand to avoid flushing
- * the buffer. We need to hold the console semaphore
- * in order to do this test safely.
- */
- if (!can_use_console(cpu)) {
- console_locked = 0;
- wake = 1;
- retval = 0;
- }
- }
- logbuf_cpu = UINT_MAX;
- raw_spin_unlock(&logbuf_lock);
- if (wake)
+ if (!console_trylock())
+ return 0;
+ /*
+ * If we can't use the console, we need to release the console
+ * semaphore by hand to avoid flushing the buffer. We need to hold the
+ * console semaphore in order to do this test safely.
+ */
+ if (!can_use_console(cpu)) {
+ console_locked = 0;
up(&console_sem);
- return retval;
+ return 0;
+ }
+ return 1;
}
int printk_delay_msec __read_mostly;
@@ -1500,6 +1483,9 @@ asmlinkage int vprintk_emit(int facility, int level,
unsigned long flags;
int this_cpu;
int printed_len = 0;
+ /* cpu currently holding logbuf_lock in this function */
+ static volatile unsigned int logbuf_cpu = UINT_MAX;
+
boot_delay_msec(level);
printk_delay();
@@ -1612,13 +1598,12 @@ asmlinkage int vprintk_emit(int facility, int level,
}
printed_len += text_len;
+ logbuf_cpu = UINT_MAX;
+ raw_spin_unlock(&logbuf_lock);
/*
* Try to acquire and then immediately release the console semaphore.
* The release will print out buffers and wake up /dev/kmsg and syslog()
* users.
- *
- * The console_trylock_for_printk() function will release 'logbuf_lock'
- * regardless of whether it actually gets the console semaphore or not.
*/
if (console_trylock_for_printk(this_cpu))
console_unlock();
--
1.8.1.4
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/3] printk: Enable interrupts before calling console_trylock_for_printk()
2013-12-17 14:48 printk: Cleanup and softlockup avoidance Jan Kara
2013-12-17 14:48 ` [PATCH 1/3] printk: Release lockbuf_lock before calling console_trylock_for_printk() Jan Kara
@ 2013-12-17 14:48 ` Jan Kara
2013-12-17 14:48 ` [PATCH 3/3] printk: Remove separate printk_sched buffers and use printk buf instead Jan Kara
2 siblings, 0 replies; 5+ messages in thread
From: Jan Kara @ 2013-12-17 14:48 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, mhocko, pmladek, Steven Rostedt, Jan Kara
We need interrupts disabled when calling console_trylock_for_printk()
only so that cpu id we pass to can_use_console() remains valid (for
other things console_sem provides all the exclusion we need and
deadlocks on console_sem due to interrupts are impossible because we use
down_trylock()). However if we are rescheduled, we are guaranteed to
run on an online cpu so we can easily just get the cpu id in
can_use_console().
We can loose a bit of performance when we enable interrupts in
vprintk_emit() and then disable them again in console_unlock() but OTOH
it can somewhat reduce interrupt latency caused by console_unlock()
especially since later in the patch series we will want to spin on
console_sem in console_trylock_for_printk().
Signed-off-by: Jan Kara <jack@suse.cz>
---
kernel/printk/printk.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 72ec3637115f..ab0d24a9ab7c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1320,10 +1320,9 @@ static int have_callable_console(void)
/*
* Can we actually use the console at this time on this cpu?
*
- * Console drivers may assume that per-cpu resources have
- * been allocated. So unless they're explicitly marked as
- * being able to cope (CON_ANYTIME) don't call them until
- * this CPU is officially up.
+ * Console drivers may assume that per-cpu resources have been allocated. So
+ * unless they're explicitly marked as being able to cope (CON_ANYTIME) don't
+ * call them until this CPU is officially up.
*/
static inline int can_use_console(unsigned int cpu)
{
@@ -1336,8 +1335,10 @@ static inline int can_use_console(unsigned int cpu)
* console_lock held, and 'console_locked' set) if it
* is successful, false otherwise.
*/
-static int console_trylock_for_printk(unsigned int cpu)
+static int console_trylock_for_printk(void)
{
+ unsigned int cpu = smp_processor_id();
+
if (!console_trylock())
return 0;
/*
@@ -1507,7 +1508,8 @@ asmlinkage int vprintk_emit(int facility, int level,
*/
if (!oops_in_progress && !lockdep_recursing(current)) {
recursion_bug = 1;
- goto out_restore_irqs;
+ local_irq_restore(flags);
+ return 0;
}
zap_locks();
}
@@ -1600,17 +1602,22 @@ asmlinkage int vprintk_emit(int facility, int level,
logbuf_cpu = UINT_MAX;
raw_spin_unlock(&logbuf_lock);
+ lockdep_on();
+ local_irq_restore(flags);
+
+ /*
+ * Disable preemption to avoid being preempted while holding
+ * console_sem which would prevent anyone from printing to console
+ */
+ preempt_disable();
/*
* Try to acquire and then immediately release the console semaphore.
* The release will print out buffers and wake up /dev/kmsg and syslog()
* users.
*/
- if (console_trylock_for_printk(this_cpu))
+ if (console_trylock_for_printk())
console_unlock();
-
- lockdep_on();
-out_restore_irqs:
- local_irq_restore(flags);
+ preempt_enable();
return printed_len;
}
--
1.8.1.4
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/3] printk: Remove separate printk_sched buffers and use printk buf instead
2013-12-17 14:48 printk: Cleanup and softlockup avoidance Jan Kara
2013-12-17 14:48 ` [PATCH 1/3] printk: Release lockbuf_lock before calling console_trylock_for_printk() Jan Kara
2013-12-17 14:48 ` [PATCH 2/3] printk: Enable interrupts " Jan Kara
@ 2013-12-17 14:48 ` Jan Kara
2013-12-17 17:19 ` Joe Perches
2 siblings, 1 reply; 5+ messages in thread
From: Jan Kara @ 2013-12-17 14:48 UTC (permalink / raw)
To: Andrew Morton; +Cc: LKML, mhocko, pmladek, Steven Rostedt, Jan Kara
From: Steven Rostedt <rostedt@goodmis.org>
To prevent deadlocks with doing a printk inside the scheduler,
printk_sched() was created. The issue is that printk has a console_sem
that it can grab and release. The release does a wake up if there's a
task pending on the sem, and this wake up grabs the rq locks that is
held in the scheduler. This leads to a possible deadlock if the wake up
uses the same rq as the one with the rq lock held already.
What printk_sched() does is to save the printk write in a per cpu buffer
and sets the PRINTK_PENDING_SCHED flag. On a timer tick, if this flag is
set, the printk() is done against the buffer.
There's a couple of issues with this approach.
1) If two printk_sched()s are called before the tick, the second one
will overwrite the first one.
2) The temporary buffer is 512 bytes and is per cpu. This is a quite a
bit of space wasted for something that is seldom used.
In order to remove this, the printk_sched() can use the printk buffer
instead, and delay the console_trylock()/console_unlock() to the queued
work.
Because printk_sched() would then be taking the logbuf_lock, the
logbuf_lock must not be held while doing anything that may call into the
scheduler functions, which includes wake ups. Unfortunately, printk()
also has a console_sem that it uses, and on release, the
up(&console_sem) may do a wake up of any pending waiters. This must be
avoided while holding the logbuf_lock.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Jan Kara <jack@suse.cz>
---
kernel/printk/printk.c | 43 ++++++++++++++++++++++++++-----------------
1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index ab0d24a9ab7c..bfbe1a5f6804 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -208,6 +208,9 @@ struct printk_log {
/*
* The logbuf_lock protects kmsg buffer, indices, counters. It is also
* used in interesting ways to provide interlocking in console_unlock();
+ * This can be taken within the scheduler's rq lock. It must be released
+ * before calling console_unlock() or anything else that might wake up
+ * a process.
*/
static DEFINE_RAW_SPINLOCK(logbuf_lock);
@@ -1479,14 +1482,19 @@ asmlinkage int vprintk_emit(int facility, int level,
static int recursion_bug;
static char textbuf[LOG_LINE_MAX];
char *text = textbuf;
- size_t text_len;
+ size_t text_len = 0;
enum log_flags lflags = 0;
unsigned long flags;
int this_cpu;
int printed_len = 0;
+ bool in_sched = false;
/* cpu currently holding logbuf_lock in this function */
static volatile unsigned int logbuf_cpu = UINT_MAX;
+ if (level == -2) {
+ level = -1;
+ in_sched = true;
+ }
boot_delay_msec(level);
printk_delay();
@@ -1533,7 +1541,12 @@ asmlinkage int vprintk_emit(int facility, int level,
* The printf needs to come first; we need the syslog
* prefix which might be passed-in as a parameter.
*/
- text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+ if (in_sched)
+ text_len = scnprintf(text, sizeof(textbuf),
+ KERN_WARNING "[sched_delayed] ");
+
+ text_len += vscnprintf(text + text_len,
+ sizeof(textbuf) - text_len, fmt, args);
/* mark and strip a trailing newline */
if (text_len && text[text_len-1] == '\n') {
@@ -1605,6 +1618,10 @@ asmlinkage int vprintk_emit(int facility, int level,
lockdep_on();
local_irq_restore(flags);
+ /* If called from the scheduler, we can not call up(). */
+ if (in_sched)
+ return printed_len;
+
/*
* Disable preemption to avoid being preempted while holding
* console_sem which would prevent anyone from printing to console
@@ -2426,21 +2443,19 @@ late_initcall(printk_late_init);
/*
* Delayed printk version, for scheduler-internal messages:
*/
-#define PRINTK_BUF_SIZE 512
-
#define PRINTK_PENDING_WAKEUP 0x01
-#define PRINTK_PENDING_SCHED 0x02
+#define PRINTK_PENDING_OUTPUT 0x02
static DEFINE_PER_CPU(int, printk_pending);
-static DEFINE_PER_CPU(char [PRINTK_BUF_SIZE], printk_sched_buf);
static void wake_up_klogd_work_func(struct irq_work *irq_work)
{
int pending = __this_cpu_xchg(printk_pending, 0);
- if (pending & PRINTK_PENDING_SCHED) {
- char *buf = __get_cpu_var(printk_sched_buf);
- pr_warn("[sched_delayed] %s", buf);
+ if (pending & PRINTK_PENDING_OUTPUT) {
+ /* If trylock fails, someone else is doing the printing */
+ if (console_trylock())
+ console_unlock();
}
if (pending & PRINTK_PENDING_WAKEUP)
@@ -2464,21 +2479,15 @@ void wake_up_klogd(void)
int printk_sched(const char *fmt, ...)
{
- unsigned long flags;
va_list args;
- char *buf;
int r;
- local_irq_save(flags);
- buf = __get_cpu_var(printk_sched_buf);
-
va_start(args, fmt);
- r = vsnprintf(buf, PRINTK_BUF_SIZE, fmt, args);
+ r = vprintk_emit(0, -2, NULL, 0, fmt, args);
va_end(args);
- __this_cpu_or(printk_pending, PRINTK_PENDING_SCHED);
+ __this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
irq_work_queue(&__get_cpu_var(wake_up_klogd_work));
- local_irq_restore(flags);
return r;
}
--
1.8.1.4
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 3/3] printk: Remove separate printk_sched buffers and use printk buf instead
2013-12-17 14:48 ` [PATCH 3/3] printk: Remove separate printk_sched buffers and use printk buf instead Jan Kara
@ 2013-12-17 17:19 ` Joe Perches
0 siblings, 0 replies; 5+ messages in thread
From: Joe Perches @ 2013-12-17 17:19 UTC (permalink / raw)
To: Jan Kara; +Cc: Andrew Morton, LKML, mhocko, pmladek, Steven Rostedt
On Tue, 2013-12-17 at 15:48 +0100, Jan Kara wrote:
> From: Steven Rostedt <rostedt@goodmis.org>
[]
> the
> logbuf_lock must not be held while doing anything that may call into the
> scheduler functions, which includes wake ups. Unfortunately, printk()
> also has a console_sem that it uses, and on release, the
> up(&console_sem) may do a wake up of any pending waiters. This must be
> avoided while holding the logbuf_lock.
[]
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
[]
> @@ -1479,14 +1482,19 @@ asmlinkage int vprintk_emit(int facility, int level,
[]
> + if (level == -2) {
> + level = -1;
> + in_sched = true;
> + }
[]
> int printk_sched(const char *fmt, ...)
> {
[]
> + r = vprintk_emit(0, -2, NULL, 0, fmt, args);
Can you please use a #define instead of the magic number -2?
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2013-12-17 17:19 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-17 14:48 printk: Cleanup and softlockup avoidance Jan Kara
2013-12-17 14:48 ` [PATCH 1/3] printk: Release lockbuf_lock before calling console_trylock_for_printk() Jan Kara
2013-12-17 14:48 ` [PATCH 2/3] printk: Enable interrupts " Jan Kara
2013-12-17 14:48 ` [PATCH 3/3] printk: Remove separate printk_sched buffers and use printk buf instead Jan Kara
2013-12-17 17:19 ` Joe Perches
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®