We've been, from time to time, running into various problems early on
the initialization of the kernel, encountering situations where we really
should panic, but we can't because its too early to do so.
(see * if you care to know why this is a problem for us). Anyhow, I've
created this patch to have a little better way of orderly finishing the
bring-up to the point where we can panic safely. Any comments?
*One reason this often happens to us is that we have added hooks
for our drivers to allocate contiguous blocks of memory very early and
our driver team doesn't always communicate when changes have been
made.
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index f4e3184..b2e7bac 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -163,6 +163,10 @@ extern struct atomic_notifier_head panic_notifier_list;
extern long (*panic_blink)(long time);
NORET_TYPE void panic(const char * fmt, ...)
__attribute__ ((NORET_AND format (printf, 1, 2))) __cold;
+extern void panic_later(const char * fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+extern void panic_later_complete(void);
+extern bool panic_later_pending(void);
extern void oops_enter(void);
extern void oops_exit(void);
extern int oops_may_print(void);
diff --git a/init/main.c b/init/main.c
index 5988deb..2a9347a 100644
--- a/init/main.c
+++ b/init/main.c
@@ -185,7 +185,6 @@ __setup("reset_devices", set_reset_devices);
static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
-static const char *panic_later, *panic_param;
extern struct obs_kernel_param __setup_start[], __setup_end[];
@@ -281,7 +280,7 @@ static int __init unknown_bootoption(char *param,
char *val)
return 0;
}
- if (panic_later)
+ if (panic_later_pending())
return 0;
if (val) {
@@ -289,8 +288,7 @@ static int __init unknown_bootoption(char *param,
char *val)
unsigned int i;
for (i = 0; envp_init[i]; i++) {
if (i == MAX_INIT_ENVS) {
- panic_later = "Too many boot env vars at
`%s'";
- panic_param = param;
+ panic_later("Too many boot env vars at
`%s'", param);
}
if (!strncmp(param, envp_init[i], val - param))
break;
@@ -301,8 +299,7 @@ static int __init unknown_bootoption(char *param,
char *val)
unsigned int i;
for (i = 0; argv_init[i]; i++) {
if (i == MAX_INIT_ARGS) {
- panic_later = "Too many boot init vars
at `%s'";
- panic_param = param;
+ panic_later("Too many boot init vars at
`%s'", param);
}
}
argv_init[i] = param;
@@ -621,8 +618,7 @@ asmlinkage void __init start_kernel(void)
* this. But we do want output early, in case something goes wrong.
*/
console_init();
- if (panic_later)
- panic(panic_later, panic_param);
+ panic_later_complete();
lockdep_info();
diff --git a/kernel/panic.c b/kernel/panic.c
index 96b45d0..092bb46 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -23,6 +23,9 @@
#include <linux/nmi.h>
#include <linux/dmi.h>
+#define MAX_PANIC_LATER_LEN 1024
+static char panic_later_msg[MAX_PANIC_LATER_LEN];
+
int panic_on_oops;
static unsigned long tainted_mask;
static int pause_on_oops;
@@ -40,6 +43,28 @@ static long no_blink(long time)
return 0;
}
+void panic_later(const char *fmt, ...)
+{
+ va_list args;
+
+ if (!panic_later_pending()) {
+ va_start(args, fmt);
+ vsnprintf(panic_later_msg, sizeof(panic_later_msg), fmt,
args);
+ va_end(args);
+ }
+}
+
+bool panic_later_pending()
+{
+ return panic_later_msg[0] != 0;
+}
+
+void panic_later_complete()
+{
+ if (panic_later_pending())
+ panic("%s", panic_later_msg);
+}
+
/* Returns how long it waited in ms */
long (*panic_blink)(long time);
EXPORT_SYMBOL(panic_blink);
Michael Sundius <[email protected]> writes:
> We've been, from time to time, running into various problems early on
> the initialization of the kernel, encountering situations where we really
> should panic, but we can't because its too early to do so.
> (see * if you care to know why this is a problem for us). Anyhow, I've
> created this patch to have a little better way of orderly finishing the
> bring-up to the point where we can panic safely. Any comments?
It's (nearly) never too early to panic. The main problem is that you sometimes
cannot see it and sometimes panic requires some state (but that's usually
a bug in panic to be fixed)
I also don't think it makes much sense to continue just to panic later.
Such a continue path is likely untested and buggy.
Some time ago I had patches to do a early_panic() using early_printk,
but didn't end up submitting them because the caller I intended them
for disappeared.
Basically early_panic() is just
void early_panic(char *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
early_printk("PANIC : %s\n", buf);
va_end(ap);
for (;;)
safe_hlt();
}
However it might be better to fix panic to do that automatically.
-Andi
--
[email protected] -- Speaking for myself only.