[PATCH] init: handle kexec clean reboot

Jeremy Kerr jk at ozlabs.org
Mon May 20 05:49:31 UTC 2013


Currently, busybox init won't allow us to do a clean kexec; we have to
invoke kexec -e, which calls the reboot(LINUX_REBOOT_CMD_KEXEC) system
call directly, bypassing any shutdown/reboot init actions.

It'd be better if we can halt the system properly before the kernel
kexec, in order to unmount filesystems, take network devices down, etc.

As far as I can tell, this is handled in other inits in differring ways:
A kexec on systemd is performed by invoking `systemctl kexec`,
(alternatively `shutdown -r` will detect the presence of a loaded kexec
and change the reboot() magic accordingly). Upstart doesn't seem to
have native kexec support, so a clean kexec on Ubuntu is implemented
with an init script that calls `kexec -e` late in the shutdown path and
never returns.

This change adds a '-k' option to /sbin/reboot, which tells init (via
a SIGALRM) that we're requesting a kexec rather than a normal reboot.
init then handles this by specifying the correct magic to the reboot
syscall.

RFC: a couple of open items:

 * Not sure that SIGALRM is the best signal for this, let me know if
   there's something more appropriate

 * We could handle kexec in a method similar to upstart, by adding an
   rc.d script that performs the reboot() syscall with the correct
   kexec magic. However, this is a little inconsistent with the existing
   reboot methods.

Signed-off-by: Jeremy Kerr <jk at ozlabs.org>

---
 init/halt.c   |   12 +++++++++---
 init/init.c   |    9 +++++++--
 init/reboot.h |    6 ++++++
 3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/init/halt.c b/init/halt.c
index 7974adb..68a60a7 100644
--- a/init/halt.c
+++ b/init/halt.c
@@ -65,6 +65,7 @@
 //usage:     "\n	-d SEC	Delay interval"
 //usage:     "\n	-n	Do not sync"
 //usage:     "\n	-f	Force (don't go through init)"
+//usage:     "\n	-k	Reboot to preloaded kexec kernel"
 
 #include "libbb.h"
 #include "reboot.h"
@@ -101,9 +102,11 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
 	static const int magic[] = {
 		RB_HALT_SYSTEM,
 		RB_POWER_OFF,
-		RB_AUTOBOOT
+		RB_AUTOBOOT,
+		RB_KEXEC,
 	};
-	static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
+	static const smallint signals[] =
+		{ SIGUSR1, SIGUSR2, SIGTERM, SIGALRM };
 
 	int delay = 0;
 	int which, flags, rc;
@@ -118,7 +121,7 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
 	 * in order to not break scripts.
 	 * -i (shut down network interfaces) is ignored.
 	 */
-	flags = getopt32(argv, "d:nfwi", &delay);
+	flags = getopt32(argv, "d:nfwik", &delay);
 
 	sleep(delay);
 
@@ -130,6 +133,9 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
 	if (!(flags & 2)) /* no -n */
 		sync();
 
+	if (applet_name[0] == 'r' && flags & 0x20)
+		which = 4;
+
 	/* Perform action. */
 	rc = 1;
 	if (!(flags & 4)) { /* no -f */
diff --git a/init/init.c b/init/init.c
index 15aad47..7c806d7 100644
--- a/init/init.c
+++ b/init/init.c
@@ -175,8 +175,8 @@
 #define CTRLALTDEL  0x20
 /*
  * Start these before killing all processes in preparation for
- * running RESTART actions or doing low-level halt/reboot/poweroff
- * (initiated by SIGUSR1/SIGTERM/SIGUSR2).
+ * running RESTART actions or doing low-level halt/reboot/poweroff/kexec
+ * (initiated by SIGUSR1/SIGTERM/SIGUSR2/SIGALRM).
  * Wait for completion before proceeding.
  */
 #define SHUTDOWN    0x40
@@ -393,6 +393,7 @@ static void reset_sighandlers_and_unblock_sigs(void)
 		+ (1 << SIGHUP)
 		+ (1 << SIGTSTP)
 		+ (1 << SIGSTOP)
+		+ (1 << SIGALRM)
 		, SIG_DFL);
 	sigprocmask_allsigs(SIG_UNBLOCK);
 }
@@ -811,6 +812,9 @@ static void halt_reboot_pwoff(int sig)
 	} else if (sig == SIGUSR2) {
 		m = "poweroff";
 		rb = RB_POWER_OFF;
+	} else if (sig == SIGALRM) {
+		m = "kexec";
+		rb = RB_KEXEC;
 	}
 	message(L_CONSOLE, "Requesting system %s", m);
 	pause_and_low_level_reboot(rb);
@@ -1124,6 +1128,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
 			+ (1 << SIGUSR1) /* halt */
 			+ (1 << SIGTERM) /* reboot */
 			+ (1 << SIGUSR2) /* poweroff */
+			+ (1 << SIGALRM) /* kexec */
 			, halt_reboot_pwoff);
 		signal(SIGQUIT, restart_handler); /* re-exec another init */
 
diff --git a/init/reboot.h b/init/reboot.h
index 9497639..ea8b1ac 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,12 @@
 # endif
 #endif
 
+#ifdef LINUX_REBOOT_CMD_KEXEC
+# define RB_KEXEC	LINUX_REBOOT_CMD_KEXEC
+#else
+# define RB_KEXEC	0
+#endif
+
 /* Stop system and switch power off if possible.  */
 #ifndef RB_POWER_OFF
 # if defined(RB_POWERDOWN)


More information about the busybox mailing list