If you are old enough to remember QEMM from back in the ’90s, along with other tools we used to squeeze every last byte of memory under the 640KB limit, you may remember a rather cool feature it had – warm reboot.
What is a Warm Reboot?
Reboot involves the computer doing a Power-On Self Test (POST). This takes time, often as much as a few minutes on some servers and workstations. While you are setting something up and need to test frequently that things come up correctly at boot time, the POST can make progress painfully slow. If only we had something like the warm reboot feature that QEMM had back in the ’90s, which allowed us to reset the RAM and reboot DOS without rebooting the entire machine and suffer the POST time. Well, such a thing does actually exist in modern Linux.
Enter kexec
kexec allows us to do exactly this – load a new kernel, kill all processes, and hand over control to the new kernel as the bootloader does at boot time. What do we need for this magic to work? On a modern distro, not much, it is all already included. Let’s start with a script that I use and explain what each component does:
#!/bin/bash systemctl isolate multi-user.target rmmod nvidia_drm nvidia_modeset nvidia_uvm rmmod nvidia kexec --load=/boot/vmlinuz-$(uname -r) \ --initrd=/boot/initramfs-$(uname -r).img \ --command-line="$(cat /proc/cmdline)" kexec --exec
Let’s look at the kexec lines first. uname -r
returns the current kernel version. $(uname -r)
bash syntax allows is to take the output of a command and use it as a string in the invoking command. On recent CentOS 8 here is what we get:
$ uname -r
4.18.0-193.6.3.el8_2.centos.plus.x86_64
$ echo $(uname -r)
4.18.0-193.6.3.el8_2.centos.plus.x86_64
The kernel and initial ramdisk usually have the kernel version in their names in /boot/:
$ ls /boot/ initramfs-4.18.0-193.6.3.el8_2.centos.plus.x86_64.img vmlinuz-4.18.0-193.6.3.el8_2.centos.plus.x86_64
So in our warm reboot script, vmlinuz-$(uname -r)
will expand to vmlinuz-4.18.0-193.6.3.el8_2.centos.plus.x86_64
. Similar will happen with the initramfs file name.
Next, what is in /proc/cmdline
? This contains the boot parameters that our currently running kernel was booted with, as provided in our grub conifguration, for example:
$ cat /proc/cmdline
BOOT_IMAGE=(hd0,msdos2)/vmlinuz-4.18.0-193.6.3.el8_2.centos.plus.x86_64 root=ZFS=tank/ROOT quiet elevator=deadline transparent_hugepage=never
This is the minimum needed to boot the kernel. Once we have supplied this information, we initiate the shutdown and process purge, and hand over to the new kernel, using:
kexec --exec
.
But what are the systemctl
and rmmod
lines about? They are mostly to work around finnickiness of Nvidia drivers and GPUs. If you execute kexec
immediately, with the Nvidia driver still running, the GPU won’t reset properly and won’t get properly re-initialised by the driver when the kernel warm-boots. So we have to rmmod
the nvidia
driver. Legacy nvidia driver only includes the nvidia
module. Newer versions also include nvidia_drm
, nvidia_modeset
and nvidia_uvm
which depend on the nvidia
module, so we have to remove those first. But before we do that, we have to make sure that Xorg isn’t running, otherwise we won’t be able to unload the nvidia
driver. To make sure graphical environment isn’t running, we switch the runlevel target to multi-user.target
(on a workstation we are probably running graphical.target
by default). Once Xorg is no longer running, we can proceed with unloading the nvidia driver modules. And with that done, we can proceed with the warm boot and enjoy a reboot time saving.