Original:https://mp.weixin.qq.com/s/2ACNTsl_JQSMwvWsZf55zw
1. Introduction
During the use of a computer, shutting down and rebooting are the first two operations learned. Similarly, these two operations exist in Linux, allowing for shutdown and reboot. This is the subject to be described here. In the Linux Kernel, mainstream shutdown and reboot are implemented through the “reboot” system call (for specifics, refer to kernel/sys.c). Additionally, besides the commonly used shutdown and restart operations, this system call also provides other reboot methods, which will be explained here one by one.
2. Reboot Methods Supported by the Kernel
You may wonder, since reboot means to restart, it is reasonable to use it to implement a restart, but how can it be used to implement a shutdown operation? The answer is as follows: After shutting down, the system will eventually be powered on again! Therefore, shutdown is a special kind of reboot process, just with a longer duration. Thus, the kernel categorizes reboot into the following methods based on different behaviors:
1: / * 2: * Commands accepted by the _reboot() system call. 3: * 4: * Reboot the system using the default command and mode. 5: * HALT stops the OS and hands control over to the ROM monitor (if any). 6: * CAD_ON Ctrl-Alt-Del sequence causes a RESTART command. 7: * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to the init task. 8: * POWER_OFF stops the OS and disconnects all power from the system if possible. 9: * RESTART2 reboots the system using the given command string. 10: * SW_SUSPEND suspends the system using software suspend (if compiled). 11: * KEXEC reboots the system using a previously loaded Linux kernel 12: * / 13: 14: #define LINUX_REBOOT_CMD_RESTART 0x01234567 15: #define LINUX_REBOOT_CMD_HALT 0xCDEF0123 16: #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF 17: #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 18: #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC 19: #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 20: #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 21: #define LINUX_REBOOT_CMD_KEXEC 0x4558454
|
Operation Type |
Full Name/Alias in English |
Function Description |
Remarks |
|
RESTART |
Restart |
Normal system reboot, reloading the kernel and user space. |
This is the most commonly used reboot method by users, equivalent to the reboot command. |
|
HALT |
Halt |
Stops the operating system and hands control over to other code (like BIOS/UEFI or hypervisor). |
The behavior depends on the specific hardware implementation, which may completely power off or enter a low power state. |
|
CAD_ON/CAD_OFF |
Ctrl+Alt+Del On/Off |
Allows/disallows triggering a reboot (RESTART) via the Ctrl+Alt+Del key combination. |
Requires keyboard driver support, default behavior is determined by kernel configuration. |
|
POWER_OFF |
Power Off |
Normal shutdown, stopping the operating system and cutting off power (ACPI state S5 ). |
Equivalent to the poweroff command, requires hardware support for complete power off. |
|
RESTART2 |
Restart with Command |
Reboots while carrying a custom string ( cmd ), passed to processes or machine-related code concerned with the reboot event. |
Usage is defined by the device manufacturer (e.g., recovery mode in Android). |
|
SW_SUSPEND |
Software Suspend |
Suspends to memory (Suspend-to-RAM) or disk (Hibernate), entering a low power state. |
Depends on the CONFIG_SUSPEND kernel option, see subsequent sleep topics. |
|
KEXEC |
Kernel Execute |
Skips BIOS/UEFI, directly rebooting to a preloaded other kernel image (requires CONFIG_KEXEC support). |
Used for quick kernel hot swapping or crash recovery (e.g., kexec -l to load a new kernel). |
3. Related Operation Processes for Rebooting
In the Linux operating system, commands such as reboot, halt, and power off can initiate a reboot, with the specific operation process as follows:
Generally, Linux operating systems provide a set of tools in user space (such as Busybox commonly used in embedded systems), which include commands related to rebooting, halting, and shutting down. User space programs enter kernel space through the reboot system call, and the kernel space provides three handling functions: kernel_restart, kernel_halt, and kernel_power_off, based on different execution paths to respond to user space reboot requests. The processing flow of these three handling functions is roughly the same, mainly including: sending notification events to processes concerned with the reboot process; calling interfaces provided by the driver core module to shut down all external devices; calling interfaces provided by the driver syscore module to shut down the system core; calling architecture-related handling functions for subsequent processing; and finally, calling computer-related interfaces to achieve a true reboot. Additionally, using the Sysreq mechanism provided by the TTY module, the kernel offers other shutdown methods, such as certain key combinations and writing commands to the /proc file.
4. Internal Actions and Code Analysis of the Reboot Process
4.1 Reboot System Call
The implementation of the reboot system call is located in “kernel/sys.c”, with the function prototype as follows:
1: SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, 2: void __user *arg)
The reboot system call’s internal actions are relatively simple:
1) Determine the caller’s user permissions; if not a superuser, return an error directly (this is why we must be the root user when executing reboot, halt, poweroff commands in user space);
2) Check if the magic numbers match; if not, return an error. This prevents accidental operations;
3) Call the reboot_pid_ns interface to check if the reboot request needs to be handled by this interface.
4) If it is a POWER_OFF command and there is no registered machine handler for power off (pm_power_off), convert this command to a HALT command;
5) Execute specific processing based on the cmd command, including:
If it is a RESTART or RESTART2 command, call kernel_restart.
If it is a CAD_ON or CAD_OFF command, update the C_A_D value to indicate allowing the system to reboot via the Ctrl + Alt + Del key combination.
If it is a HALT command, call kernel_halt.
If it is a POWER_OFF command, call kernel_power_off.
If it is a KEXEC command, call the kernel_kexec interface.
If it is SW_SUSPEND, call the hibernate interface.
6) Return the processing result above, and the system call ends.
4.2 kernel_restart, kernel_halt, and kernel_power_off
1) Call the kernel_xxx_prepare function to prepare for rebooting/stopping/power_off, including:
Calling the blocking_notifier_call_chain interface to send SYS_RESTART, SYS_HALT, or SYS_POWER_OFF events to processes concerned with reboot events. And send them out.
Set the system state to the corresponding state (SYS_RESTART, SYS_HALT, or SYS_POWER_OFF).
Call the usermodehelper_disable interface to disable user mode helpers.
Call device_shutdown to shut down all devices.
2) If it is power_off and there is a PM-related power off prepare function (pm_power_off_prepare), call that function;
3) Call the migrate_to_reboot_cpu interface to move the current process (task) to one CPU;
Note 2: For multi-CPU machines, regardless of which CPU triggered the current system call, the code can run on any CPU. This interface dispatches the code to a specific CPU and prevents the scheduler from dispatching the code to other CPUs. After this interface is executed, only one CPU runs to complete the subsequent reboot actions.
4) Call the syscore_shutdown interface to shut down the system core devices (e.g., interrupts, etc.);
5) Call printk and kmsg_dump to send the last message to the world (print logs);
6) Finally, the machine-core code takes over subsequent processing.
4.3 device_shutdown
The logic related to device_shutdown in the device model includes:
1. Each device (struct device) will save a pointer to its driver (struct device_driver) and a pointer to the bus it is on (struct bus_type).
2. The device driver has a function named “shutdown” that is called to shut down the device during device_shutdown.
3. The bus also has a function named “shutdown” that is called to shut down the device during device_shutdown.
4. All devices in the system exist under the “/sys/devices/” directory, represented by a kset named “devices_kset”. The kset uses a linked list to save all its kobjects (i.e., all devices under “/sys/devices/”). The final result is that, with “devices_kset” as the root directory, all devices in the kernel (represented by the corresponding kobjects) are organized into a tree structure.
The implementation of device_shutdown is located in “drivers/base/core.c”, with the execution logic as follows.
1: / ** 2: * device_shutdown - calls ->shutdown() on each device to shut it down. 3: * / 4: void device_shutdown(void) 5: { 6: struct device *dev, *parent; 7: 8: spin_lock(&devices_kset->list_lock); 9: / * 10: * Move back the device list, shutting down each device in turn. 11: * Note that device unplug events may also start pulling 12: * devices offline, even while the system is shutting down. 13: * / 14: while (!list_empty(&devices_kset->list)) { 15: dev = list_entry(devices_kset->list.prev, struct device, 16: kobj.entry); 17: 18: /* 19: * hold reference count of device's parent to 20: * prevent it from being freed because parent's 21: * lock is to be held 22: */ 23: parent = get_device(dev->parent); 24: get_device(dev); 25: /* 26: * Make sure the device is off the kset list, in the 27: * event that dev->*->shutdown() doesn't remove it. 28: */ 29: list_del_init(&dev->kobj.entry); 30: spin_unlock(&devices_kset->list_lock); 31: 32: /* hold lock to avoid race with probe/release */ 33: if (parent) 34: device_lock(parent); 35: device_lock(dev); 36: 37: /* Don't allow any more runtime suspends */ 38: pm_runtime_get_noresume(dev); 39: pm_runtime_barrier(dev); 40: 41: if (dev->bus && dev->bus->shutdown) { 42: if (initcall_debug) 43: dev_info(dev, "shutdown\n"); 44: dev->bus->shutdown(dev); 45: } else if (dev->driver && dev->driver->shutdown) { 46: if (initcall_debug) 47: dev_info(dev, "shutdown\n"); 48: dev->driver->shutdown(dev); 49: } 50: 51: device_unlock(dev); 52: if (parent) 53: device_unlock(parent); 54: 55: put_device(dev); 56: put_device(parent); 57: 58: spin_lock(&devices_kset->list_lock); 59: } 60: spin_unlock(&devices_kset->list_lock); 61: async_synchronize_full(); 62: }
4.4 system_core_shutdown
The shutdown of the system core is similar to that of devices, also traversing all system cores from a linked list and calling their shutdown interfaces.
4.5 machine_restart, machine_halt, and machine_power_off
Although prefixed with machine_, these three interfaces are architecture-related handling functions, such as for ARM. For example, they are implemented in “arch/arm/kernel/process.c” as follows.
4.5.1 Machine Restart
1: / * 2: * Restart requires auxiliary CPUs to stop executing any activities 3: * while the primary CPU resets the system. Systems with a single CPU can 4: * use soft_restart() as their machine descriptor's .restart hook, as 5: * this will cause the only available CPU to reset. Systems with multiple CPUs must 6: * provide a hardware restart implementation to ensure all CPUs reset immediately. 7: * This is necessary so that any code running after the reset on the primary CPU 8: * does not have to coordinate with other CPUs to ensure they are still not running 9: * executing pre-reset code and using the RAM that the primary CPU hopes to use. 10: * Implementing this coordination is basically impossible. 11: * / 12: void machine_restart(char *cmd) 13: { 14: smp_send_stop(); 15: 16: arm_pm_restart(reboot_mode, cmd); 17: 18: / * Give a 1s grace period for failure * / 19: mdelay(1000); 20: 21: / * Oops - platform cannot restart. Inform the user! * / 22: printk("Restart failed - system halted\n"); 23: local_irq_disable(); 24: while (1); 25: }
0) To paraphrase the comments of this interface;
For multi-CPU machines, it is essential to ensure that other CPUs are inactive before restarting, with one primary CPU responsible for the restart action. Furthermore, a hardware-based restart operation must be implemented to ensure all CPUs synchronize their restart, which is a key design point! For single CPU machines, it is relatively simple, as a software reset can be directly used to achieve a restart.
1) Call the smp_send_stop interface to ensure other CPUs are inactive;
2) Call the machine-related restart interface to implement the actual restart. This interface is a variable function declared by “arch/arm/kernel/process.c” and implemented by specific machine code. The format is as follows:
void (*arm_pm_restart)(char str, const char *cmd) = null_restart;
EXPORT_SYMBOL_GPL(arm_pm_restart);
3) Wait for 1 second;
4) If there is no return, the restart is successful; otherwise, it fails, and an error message is printed.
4.5.2 machine_halt
The halt operation for ARM is straightforward; it simply stops other CPUs and disables interrupts on the current CPU, then enters an infinite loop! Indeed, with interrupts disabled and an infinite loop, it is not surprising that it does not halt. The code is as follows:
1: / * 2: * Stopping only requires auxiliary CPUs to stop executing any operations 3: * (executing tasks, processing interrupts). smp_send_stop() 4: * achieves this goal. 5: * / 6: void machine_halt(void) 7: { 8: smp_send_stop(); 9: 10: local_irq_disable(); 11: while (1); 12: }
4.5.3 machine_power_off
The power off action is similar to restart; it stops other CPUs and calls other functions. Some functions for power off are similar to those for restart, so they will not be elaborated further.
5. Summary and Thoughts
5.1 Concepts of Architecture and Machine
This article is the first time we encounter the concepts of architecture and machine while analyzing the Linux kernel, so let me explain. The most common directory structure in kernel code is: arch/xxx/mach-xxx/ (for example, arch/arm/mach-bcm/). From this directory structure, we can see that architecture (abbreviated as arch) refers to specific architectures, such as ARM, X86, etc. The machine refers to one or more SOCs under a specific architecture, such as bcm, etc.
5.2 Power Management Drivers (and reboot-related parts) Implementation Requirements
From the above analysis, it can be seen that during the reboot process, most of the logic is handled by the kernel, and specific drivers need to focus on two points:
1) Implement their shutdown interfaces to correctly shut down the corresponding devices.
2) Implement machine-related interfaces to ensure that sufficient machines can correctly reboot or power off.
It seems quite simple.