linux reboot函数各分支对比分析
在linux里面,关机、重启可以通过不同的命令实现,这些命令的具体了解,我推荐下面这篇博文:linux 关机命令总结
在linux里面,关机、重启可以通过不同的命令实现,这些命令的具体了解,我推荐下面这篇博文:
说明:下面我贴的代码是基于linux 3.13.0版本内核的代码,和其它版本可能有一定程度的差异。
一、reboot
/*
* Reboot system call: for obvious reasons only root may call it,
* and even root needs to set up some magic numbers in the registers
* so that some mistake won't make this reboot the whole machine.
* You can also set the meaning of the ctrl-alt-del-key here.
*
* reboot doesn't sync: do that yourself before calling this.
*/
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
struct pid_namespace *pid_ns = task_active_pid_ns(current);
char buffer[256];
int ret = 0;
/* We only trust the superuser with rebooting the system. */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 &&
magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B &&
magic2 != LINUX_REBOOT_MAGIC2C))
return -EINVAL;
/*
* If pid namespaces are enabled and the current task is in a child
* pid_namespace, the command is handled by reboot_pid_ns() which will
* call do_exit().
*/
ret = reboot_pid_ns(pid_ns, cmd);
if (ret)
return ret;
/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
mutex_lock(&reboot_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1;
break;
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
do_exit(0);
panic("cannot halt");
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2:
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
#ifdef CONFIG_KEXEC
case LINUX_REBOOT_CMD_KEXEC:
ret = kernel_kexec();
break;
#endif
#ifdef CONFIG_HIBERNATION
case LINUX_REBOOT_CMD_SW_SUSPEND:
ret = hibernate();
break;
#endif
default:
ret = -EINVAL;
break;
}
mutex_unlock(&reboot_mutex);
return ret;
}
在第44行之前,是所有的命令都要执行的,而在本文,我们简要分析不同命令的区别;所以主要分析44行后switch的各分支。
根据switch的case分支可知,命令和函数的对应关系为:
函数指针 | 函数 |
restart | kernel_restart |
halt | kernel_halt |
poweroff | kernel_power_off |
suspend | hibernate |
kexec | 没有具体实现,略过 |
下面我们先把个实现函数的代码贴上来,看一下流程上的差别。
二、各分支函数预览
2.1、kernel_restart
/**
* kernel_restart - reboot the system
* @cmd: pointer to buffer containing command to execute for restart
* or %NULL
*
* Shutdown everything and perform a clean reboot.
* This is not safe to call in interrupt context.
*/
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
migrate_to_reboot_cpu();
syscore_shutdown();
if (!cmd)
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART);
machine_restart(cmd);
}
2.2、kernel_halt
/**
* kernel_halt - halt the system
*
* Shutdown everything and perform a clean system halt.
*/
void kernel_halt(void)
{
kernel_shutdown_prepare(SYSTEM_HALT);
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("System halted\n");
kmsg_dump(KMSG_DUMP_HALT);
machine_halt();
}
2.3、kernel_power_off
/**
* kernel_power_off - power_off the system
*
* Shutdown everything and perform a clean system power_off.
*/
void kernel_power_off(void)
{
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_POWEROFF);
machine_power_off();
}
2.4、hibernate
/**
* hibernate - Carry out system hibernation, including saving the image.
*/
int hibernate(void)
{
int error;
lock_system_sleep();
/* The snapshot device should not be opened while we're running */
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
error = -EBUSY;
goto Unlock;
}
pm_prepare_console();
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
if (error)
goto Exit;
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
error = freeze_processes();
if (error)
goto Exit;
lock_device_hotplug();
/* Allocate memory management structures */
error = create_basic_memory_bitmaps();
if (error)
goto Thaw;
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (error || freezer_test_done)
goto Free_bitmaps;
if (in_suspend) {
unsigned int flags = 0;
if (hibernation_mode == HIBERNATION_PLATFORM)
flags |= SF_PLATFORM_MODE;
if (nocompress)
flags |= SF_NOCOMPRESS_MODE;
else
flags |= SF_CRC32_MODE;
pr_debug("PM: writing image.\n");
error = swsusp_write(flags);
swsusp_free();
if (!error)
power_down();
in_suspend = 0;
pm_restore_gfp_mask();
} else {
pr_debug("PM: Image restored successfully.\n");
}
Free_bitmaps:
free_basic_memory_bitmaps();
Thaw:
unlock_device_hotplug();
thaw_processes();
/* Don't bother checking whether freezer_test_done is true */
freezer_test_done = false;
Exit:
pm_notifier_call_chain(PM_POST_HIBERNATION);
pm_restore_console();
atomic_inc(&snapshot_device_available);
Unlock:
unlock_system_sleep();
return error;
}
看完以上四个函数的代码,可以知道,前面三个函数在流程上差别不是很大,第四个函数hibernate的流程就完全不同。
下面我们以表格的方式来整理一下前三个函数的区别。
三、reboot中各函数流程对比
序号 | kernel_restart | kernel_halt | kernel_power_off |
---|---|---|---|
1 | kernel_restart_prepare | kernel_shutdown_prepare | kernel_shutdown_prepare |
2 | migrate_to_reboot_cpu | migrate_to_reboot_cpu | migrate_to_reboot_cpu |
3 | syscore_shutdown | syscore_shutdown | syscore_shutdown |
4 | kmsg_dump | kmsg_dump | kmsg_dump |
5 | machine_restart | machine_halt | machine_power_off |
上面的三个函数的第2、3、4个步骤都是一样的。
而kernel_restart函数在linux关机时emmc驱动处理流程时主干代码基本分析了,所以在这里第2、3、4步骤就不再分析。
而kernel_halt和kernel_power_off的第一个步骤是一样的。来看一下第一个步骤中kernel_restart_prepare函数和kernel_shutdown_prepare函数的源码实现。
四、prepare函数对比
4.1、kernel_restart_prepare通过第一节中的reboot函数可以知道,reboot传递给kernel_restart的形参cmd的值为NULL;
所以kernel_restart传递给kernel_restart_prepare的参数值也为NULL,下面看一下kernel_restart_prepare的实现:
void kernel_restart_prepare(char *cmd)
{
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
system_state = SYSTEM_RESTART;
usermodehelper_disable();
device_shutdown();
}
这个函数我们在
linux关机时emmc驱动处理流程这篇文章的第二节有分析过,这里就不详细分析了,需要注意的是,这里通知链传递的通知消息为SYS_RESTART。
4.2、kernel_shutdown_prepare
static void kernel_shutdown_prepare(enum system_states state)
{
blocking_notifier_call_chain(&reboot_notifier_list,
(state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL);
system_state = state;
usermodehelper_disable();
device_shutdown();
}
这个函数和4.1的kernel_restart_prepare函数整体差不多,唯一不同的就是传递给通知链的第二个参数;
当调用kernel_halt传递的参数为SYS_HALT,否则传递SYS_POWER_OFF。
五、restart、halt、power_off
machine_restart、machine_halt、machine_power_off函数的实现差异比较大,我暂时还没弄懂,这里有一篇文章,做了相关介绍,推荐看:Linux电源管理(3)_Generic PM之Reboot过程
这个函数的代码在2.4节已经贴出来了,涉及的代码比较多,我还没完全理解,推荐两个链接:
六、休眠对于emmc的影响
在android 休眠唤醒机制分析(三) — suspend一文中,第3节的device_suspend函数如下(我的源代码是3.13.0):
/**
* device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
*/
static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
char *info = NULL;
int error = 0;
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
dpm_wait_for_children(dev, async);
if (async_error)
goto Complete;
/*
* If a device configured to wake up the system from sleep states
* has been suspended at run time and there's a resume request pending
* for it, this is equivalent to the device signaling wakeup, so the
* system suspend operation should be aborted.
*/
if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
pm_wakeup_event(dev, 0);
if (pm_wakeup_pending()) {
async_error = -EBUSY;
goto Complete;
}
if (dev->power.syscore)
goto Complete;
dpm_watchdog_set(&wd, dev);
device_lock(dev);
if (dev->pm_domain) {
info = "power domain ";
callback = pm_op(&dev->pm_domain->ops, state);
goto Run;
}
if (dev->type && dev->type->pm) {
info = "type ";
callback = pm_op(dev->type->pm, state);
goto Run;
}
if (dev->class) {
if (dev->class->pm) {
info = "class ";
callback = pm_op(dev->class->pm, state);
goto Run;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend,
"legacy class ");
goto End;
}
}
if (dev->bus) {
if (dev->bus->pm) {
info = "bus ";
callback = pm_op(dev->bus->pm, state);
} else if (dev->bus->suspend) {
pm_dev_dbg(dev, state, "legacy bus ");
error = legacy_suspend(dev, state, dev->bus->suspend,
"legacy bus ");
goto End;
}
}
Run:
if (!callback && dev->driver && dev->driver->pm) {
info = "driver ";
callback = pm_op(dev->driver->pm, state);
}
error = dpm_run_callback(callback, dev, state, info);
End:
if (!error) {
dev->power.is_suspended = true;
if (dev->power.wakeup_path
&& dev->parent && !dev->parent->power.ignore_children)
dev->parent->power.wakeup_path = true;
}
device_unlock(dev);
dpm_watchdog_clear(&wd);
Complete:
complete_all(&dev->power.completion);
if (error)
async_error = error;
return error;
}
通过追溯源代码,我们可以知道,当系统在深度休眠时传递给此函数的state参数为:PM_EVENT_SUSPEND。
第51行:如果设备注册了pm函数集指针就执行设备的函数集指针,否则执行第64行的函数集指针;从我们的emmc驱动来看,它没有注册设备的函数集指针,但是有注册总线的pm函数集指针,在bus.c文件中有如下代码:
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_groups = mmc_dev_groups,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
而mmc_bus_pm_ops的实现如下:
static const struct dev_pm_ops mmc_bus_pm_ops = {
SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume,
mmc_runtime_idle)
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
};
SET_RUNTIME_PM_OPS宏的实现如下:
#ifdef CONFIG_PM_RUNTIME
#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
.runtime_suspend = suspend_fn, \
.runtime_resume = resume_fn, \
.runtime_idle = idle_fn,
#else
#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)
#endif
SET_SYSTEM_SLEEP_PM_OPS的实现如下:
#ifdef CONFIG_PM_SLEEP
#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
.suspend = suspend_fn, \
.resume = resume_fn, \
.freeze = suspend_fn, \
.thaw = resume_fn, \
.poweroff = suspend_fn, \
.restore = resume_fn,
#else
#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
#endif
__device_suspend函数的第67行:callback = pm_op(dev->class->pm, state);代码展开:
/**
* pm_op - Return the PM operation appropriate for given PM event.
* @ops: PM operations to choose from.
* @state: PM transition of the system being carried out.
*/
static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
{
switch (state.event) {
#ifdef CONFIG_SUSPEND
case PM_EVENT_SUSPEND:
return ops->suspend;
case PM_EVENT_RESUME:
return ops->resume;
#endif /* CONFIG_SUSPEND */
#ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze;
case PM_EVENT_HIBERNATE:
return ops->poweroff;
case PM_EVENT_THAW:
case PM_EVENT_RECOVER:
return ops->thaw;
break;
case PM_EVENT_RESTORE:
return ops->restore;
#endif /* CONFIG_HIBERNATE_CALLBACKS */
}
return NULL;
}
而在本节的前面,我们知道
__device_suspend的参数state值为PM_EVENT_SUSPEND。
所以,这里执行的就是emmc驱动模块的bus.c中的mmc_bus_suspend函数:
static int mmc_bus_suspend(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
if (dev->driver && drv->suspend) {
ret = drv->suspend(card);
if (ret)
return ret;
}
ret = host->bus_ops->suspend(host);
return ret;
}
而该函数最终调用的是驱动的suspend和总线ops的suspend;
在emmc驱动的block.c文件中,驱动的suspend函数指针指向mmc_blk_suspend函数,实现如下:
static int mmc_blk_suspend(struct mmc_card *card)
{
return _mmc_blk_suspend(card);
}
而_mmc_blk_suspend在
linux关机时emmc驱动处理流程的第七节我们详细讲解过,这里就不再重复。
在emmc驱动模块的bus.c文件中,我们知道总线ops的suspend函数,是指向mmc_suspend函数,实现如下:
static int mmc_suspend(struct mmc_host *host)
{
int err;
err = _mmc_suspend(host, true);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
}
return err;
}
由此,我们可以知道,其实它是依赖于_mmc_suspend函数的,而_mmc_suspend函数我们在
linux关机时emmc驱动处理流程的第十一节中详细描述过,这里也不再描述。
本节总结:系统休眠对emmc的影响(从inux关机时emmc驱动处理流程拷过来的):
1、将消息对列挂载起来,暂时不执行,并没有发送什么命令
2、如果设备是挂载状态,则直接返回,否则执行下面的流程:
3、如果设备当前正在执行块请求操作,则发送停止块请求操作(停止编程操作),会涉及到以下命令:
MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
MMC_SWITCH 6 /* ac [31:0] See below R1b */
MMC_SELECT_CARD 7 /* ac [31:16] RCA
#define MMC_STOP_TRANSMISSION 12/* ac R1b */
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
4、将缓存数据写到卡上,发送MMC_SWITCH 命令。
5、根据emmc的当前状态,可能会发送CMD6设置host状态为poweroff状态,或者是发送CMD1命令进入休眠,或者是发送CMD7命令设置host与card的断开;
6、通过设置设备的ocr寄存器设置设备断电。
7、设置设备为挂载状态。
更多推荐
所有评论(0)