Skip to content

Commit

Permalink
slow down printk during boot
Browse files Browse the repository at this point in the history
Optionally add a boot delay after each kernel printk() call, crudely
measured in milliseconds, with a maximum delay of 10 seconds per printk.

Enable CONFIG_BOOT_PRINTK_DELAY=y and then add (e.g.):
"lpj=loops_per_jiffy boot_delay=100"
to the kernel command line.

It has been useful in cases like "during boot, my machine just reboots or the
screen goes black" by slowing down printk, (and adding initcall_debug), we can
usually see the last thing that happened before the lights went out which is
usually a valuable clue.

[[email protected]: not all architectures implement CONFIG_HZ]
[[email protected]: fix lots of stuff]
[[email protected]: kernel/printk.c: make 2 variables static]
[[email protected]: fix slow down printk on boot compile error]
Signed-off-by: Randy Dunlap <[email protected]>
Signed-off-by: Dave Jones <[email protected]>
Signed-off-by: Adrian Bunk <[email protected]>
Signed-off-by: Heiko Carstens <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
rddunlap authored and Linus Torvalds committed Oct 16, 2007
1 parent 1bcf548 commit bfe8df3
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 1 deletion.
5 changes: 5 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@ and is between 256 and 4096 characters. It is defined in the file
blkmtd_bs=
blkmtd_count=

boot_delay= Milliseconds to delay each printk during boot.
Values larger than 10 seconds (10000) are changed to
no delay (0).
Format: integer

bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards)
bttv.radio= Most important insmod options are available as
kernel args too.
Expand Down
2 changes: 2 additions & 0 deletions include/linux/jiffies.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ static inline u64 get_jiffies_64(void)
*/
#define MAX_JIFFY_OFFSET ((LONG_MAX >> 1)-1)

extern unsigned long preset_lpj;

/*
* We want to do realistic conversions of time so we need to use the same
* values the update wall clock code uses as the jiffies size. This value
Expand Down
2 changes: 1 addition & 1 deletion init/calibrate.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#include <asm/timex.h>

static unsigned long preset_lpj;
unsigned long preset_lpj;
static int __init lpj_setup(char *str)
{
preset_lpj = simple_strtoul(str,NULL,0);
Expand Down
59 changes: 59 additions & 0 deletions kernel/printk.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <linux/tty_driver.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/nmi.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h> /* For in_interrupt() */
Expand Down Expand Up @@ -162,6 +164,61 @@ static int __init log_buf_len_setup(char *str)

__setup("log_buf_len=", log_buf_len_setup);

#ifdef CONFIG_BOOT_PRINTK_DELAY

static unsigned int boot_delay; /* msecs delay after each printk during bootup */
static unsigned long long printk_delay_msec; /* per msec, based on boot_delay */

static int __init boot_delay_setup(char *str)
{
unsigned long lpj;
unsigned long long loops_per_msec;

lpj = preset_lpj ? preset_lpj : 1000000; /* some guess */
loops_per_msec = (unsigned long long)lpj / 1000 * HZ;

get_option(&str, &boot_delay);
if (boot_delay > 10 * 1000)
boot_delay = 0;

printk_delay_msec = loops_per_msec;
printk(KERN_DEBUG "boot_delay: %u, preset_lpj: %ld, lpj: %lu, "
"HZ: %d, printk_delay_msec: %llu\n",
boot_delay, preset_lpj, lpj, HZ, printk_delay_msec);
return 1;
}
__setup("boot_delay=", boot_delay_setup);

static void boot_delay_msec(void)
{
unsigned long long k;
unsigned long timeout;

if (boot_delay == 0 || system_state != SYSTEM_BOOTING)
return;

k = (unsigned long long)printk_delay_msec * boot_delay;

timeout = jiffies + msecs_to_jiffies(boot_delay);
while (k) {
k--;
cpu_relax();
/*
* use (volatile) jiffies to prevent
* compiler reduction; loop termination via jiffies
* is secondary and may or may not happen.
*/
if (time_after(jiffies, timeout))
break;
touch_nmi_watchdog();
}
}
#else
static inline void boot_delay_msec(void)
{
}
#endif

/*
* Commands to do_syslog:
*
Expand Down Expand Up @@ -527,6 +584,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
static char printk_buf[1024];
static int log_level_unknown = 1;

boot_delay_msec();

preempt_disable();
if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id())
/* If a crash is occurring during printk() on this CPU,
Expand Down
18 changes: 18 additions & 0 deletions lib/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,24 @@ config FORCED_INLINING
become the default in the future, until then this option is there to
test gcc for this.

config BOOT_PRINTK_DELAY
bool "Delay each boot printk message by N milliseconds"
depends on DEBUG_KERNEL && PRINTK && GENERIC_CALIBRATE_DELAY
help
This build option allows you to read kernel boot messages
by inserting a short delay after each one. The delay is
specified in milliseconds on the kernel command line,
using "boot_delay=N".

It is likely that you would also need to use "lpj=M" to preset
the "loops per jiffie" value.
See a previous boot log for the "lpj" value to use for your
system, and then set "lpj=M" before setting "boot_delay=N".
NOTE: Using this option may adversely affect SMP systems.
I.e., processors other than the first one may not boot up.
BOOT_PRINTK_DELAY also may cause DETECT_SOFTLOCKUP to detect
what it believes to be lockup conditions.

config RCU_TORTURE_TEST
tristate "torture tests for RCU"
depends on DEBUG_KERNEL
Expand Down

0 comments on commit bfe8df3

Please sign in to comment.