在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分支可知,命令和函数的对应关系为:

函数指针函数
restartkernel_restart
haltkernel_halt
poweroffkernel_power_off
suspendhibernate
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_restartkernel_haltkernel_power_off
1kernel_restart_preparekernel_shutdown_preparekernel_shutdown_prepare
2migrate_to_reboot_cpumigrate_to_reboot_cpumigrate_to_reboot_cpu
3syscore_shutdownsyscore_shutdownsyscore_shutdown
4kmsg_dumpkmsg_dumpkmsg_dump
5machine_restartmachine_haltmachine_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过程


回到第一节,有一个hibernate函数,我们还没有分析;
这个函数的代码在2.4节已经贴出来了,涉及的代码比较多,我还没完全理解,推荐两个链接:

Linux进程冻结技术

android 休眠唤醒机制分析(三) — suspend

六、休眠对于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、设置设备为挂载状态。








Logo

更多推荐