linux待机流程

linux待机是通过上层应用往sysfs节点/sys/power/state中写入mem,
从而执行整个系统的待机流程。
待机过程首先是冻结用户进程,内核线程,workqueue,接着依次
执行各种设备的suspend操作,最后cpu core进入low power模式,
等待外部中断或者事件唤醒整个系统。
恢复的过程跟待机过程刚好相反,首先cpu core收到中断后,唤醒cpu core,
接着执行各种设备的resume操作,最后唤醒用户进程,内核线程,workqueue等,
最后执行调度函数,选取优先级高的进程执行。

下面是代码流程的解读。

//user往sys节点写state后,先调用
int pm_suspend(suspend_state_t state)
{
	int error;

	if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
		return -EINVAL;

	pm_suspend_marker("entry");
	error = enter_state(state);
	if (error) {
		suspend_stats.fail++;
		dpm_save_failed_errno(error);
	} else {
		suspend_stats.success++;
	}
	pm_suspend_marker("exit");
	return error;
}
//待机唤醒主体函数,这个函数里面完成了待机跟唤醒的完整过程,
//这里面用到的suspend_ops通过suspend_set_ops完成注册,
//对于高通平台,实际使用的接口在lpm_level.c中定义
static int enter_state(suspend_state_t state)
{
	int error;

	if (state == PM_SUSPEND_FREEZE) {
#ifdef CONFIG_PM_DEBUG
		if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
			pr_warning("PM: Unsupported test mode for freeze state,"
				   "please choose none/freezer/devices/platform.\n");
			return -EAGAIN;
		}
#endif
	} else if (!valid_state(state)) {
		return -EINVAL;
	}
	if (!mutex_trylock(&pm_mutex))
		return -EBUSY;

	if (state == PM_SUSPEND_FREEZE)
		freeze_begin();

	//文件系统同步,防止数据损坏
	printk(KERN_INFO "PM: Syncing filesystems ... \n");
	sys_sync();
	printk("done.\n");

	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label);
	//待机前准备操作
	error = suspend_prepare(state);
	if (error)
		goto Unlock;

	if (suspend_test(TEST_FREEZER))
		goto Finish;

	pr_debug("PM: Entering %s sleep\n", pm_states[state].label);
	pm_restrict_gfp_mask();
	//这里面进入待机,同时会从待机点依次恢复
	error = suspend_devices_and_enter(state);
	pm_restore_gfp_mask();

 Finish:
	pr_debug("PM: Finishing wakeup.\n");
	//唤醒整个系统
	suspend_finish();

 Unlock:
	mutex_unlock(&pm_mutex);
	return error;
}

static int suspend_prepare(suspend_state_t state)
{
	int error;

	if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
		return -EPERM;
	//将当前console切换到一个虚拟console并重定向内核的kmsg(需要的话)。该功能称作VT switch
	pm_prepare_console();
	//通知device系统准备进入suspend状态,某些特殊设备
	//在设备模型之外通过PM notifier来接受PM消息,执行自身suspend/resume操作
	error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
	if (error)
		goto Finish;
	//冻结用户进程,内核线程,work_queue,失败的话就唤醒已经被冻结task
	error = suspend_freeze_processes();
	if (!error)
		return 0;
	log_suspend_abort_reason("One or more tasks refusing to freeze");
	suspend_stats.failed_freeze++;
	dpm_save_failed_step(SUSPEND_FREEZE);
 Finish:
	pm_notifier_call_chain(PM_POST_SUSPEND);
	pm_restore_console();
	return error;
}

int suspend_devices_and_enter(suspend_state_t state)
{
	int error;
	bool wakeup = false;

	//suspend_ops 是在lpm_probe函数中通过suspend_set_ops进行注册的
	if (need_suspend_ops(state) && !suspend_ops)
		return -ENOSYS;

	trace_machine_suspend(state);
	if (need_suspend_ops(state) && suspend_ops->begin) {
		error = suspend_ops->begin(state);
		if (error)
			goto Close;
	}
	//获取console lock,阻止console被访问
	suspend_console();
	ftrace_stop();//停止ftrace功能
	suspend_test_start();
	//dpm-device PM, 设备suspend操作
	error = dpm_suspend_start(PMSG_SUSPEND);
	if (error) {
		printk(KERN_ERR "PM: Some devices failed to suspend\n");
		log_suspend_abort_reason("Some devices failed to suspend");
		goto Recover_platform;
	}
	suspend_test_finish("suspend devices");
	if (suspend_test(TEST_DEVICES))
		goto Recover_platform;

	do {
		error = suspend_enter(state, &wakeup);//待机,唤醒操作
	} while (!error && !wakeup && need_suspend_ops(state)
		&& suspend_ops->suspend_again && suspend_ops->suspend_again());

 Resume_devices:
	suspend_test_start();
	dpm_resume_end(PMSG_RESUME);
	suspend_test_finish("resume devices");
	ftrace_start();
	resume_console();
 Close:
	if (need_suspend_ops(state) && suspend_ops->end)
		suspend_ops->end();
	trace_machine_suspend(PWR_EVENT_EXIT);
	return error;

 Recover_platform:
	if (need_suspend_ops(state) && suspend_ops->recover)
		suspend_ops->recover();
	goto Resume_devices;
}

static int suspend_enter(suspend_state_t state, bool *wakeup)
{
	char suspend_abort[MAX_SUSPEND_ABORT_LEN];
	int error, last_dev;
	//未注册prepare函数
	if (need_suspend_ops(state) && suspend_ops->prepare) {
		error = suspend_ops->prepare();
		if (error)
			goto Platform_finish;
	}
	//执行设备注册的late suspend跟noirq suspend函数,
	error = dpm_suspend_end(PMSG_SUSPEND);
	if (error) {
		last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
		last_dev %= REC_FAILED_NUM;
		printk(KERN_ERR "PM: Some devices failed to power down\n");
		log_suspend_abort_reason("%s device failed to power down",
			suspend_stats.failed_devs[last_dev]);
		goto Platform_finish;
	}
	//执行lpm_suspend_prepare
	if (need_suspend_ops(state) && suspend_ops->prepare_late) {
		error = suspend_ops->prepare_late();
		if (error)
			goto Platform_wake;
	}

	if (suspend_test(TEST_PLATFORM))
		goto Platform_wake;

	/*
	 * PM_SUSPEND_FREEZE equals
	 * frozen processes + suspended devices + idle processors.
	 * Thus we should invoke freeze_enter() soon after
	 * all the devices are suspended.
	 */
	if (state == PM_SUSPEND_FREEZE) {
		freeze_enter();
		goto Platform_wake;
	}
	qiku_regulator_dbg_show(print_gpio_clk_regulate_mask & REG_BIT);  //sunshuai
	//关闭非启动cpu
	error = disable_nonboot_cpus();
	if (error || suspend_test(TEST_CPUS)) {
		log_suspend_abort_reason("Disabling non-boot cpus failed");
		goto Enable_cpus;
	}
	//关中断
	arch_suspend_disable_irqs();
	BUG_ON(!irqs_disabled());

	error = syscore_suspend();
	if (!error) {
		*wakeup = pm_wakeup_pending();
		if (!(suspend_test(TEST_CORE) || *wakeup)) {
			//执行lpm_suspend_enter,这里面CPU进入WFI状态,等待外部中断唤醒cpu
			error = suspend_ops->enter(state);
			events_check_enabled = false;
		} else if (*wakeup) {
			pm_get_active_wakeup_sources(suspend_abort,
				MAX_SUSPEND_ABORT_LEN);
			log_suspend_abort_reason(suspend_abort);
			error = -EBUSY;
		}
		syscore_resume();
	}

	arch_suspend_enable_irqs();
	BUG_ON(irqs_disabled());

 Enable_cpus:
 	//启动nonboot cpu
	enable_nonboot_cpus();

 Platform_wake:
 	//wake 为空
	if (need_suspend_ops(state) && suspend_ops->wake)
		suspend_ops->wake();
	//执行设备noirq/early resume
	dpm_resume_start(PMSG_RESUME);

 Platform_finish:
	if (need_suspend_ops(state) && suspend_ops->finish)
		suspend_ops->finish();

	return error;
}

static void suspend_finish(void)
{	
	//唤醒task
	suspend_thaw_processes();
	//特殊设备执行resume操作
	pm_notifier_call_chain(PM_POST_SUSPEND);
	//切换虚拟终端
	pm_restore_console();
}

//跟cpu core相关的操作:
static int lpm_suspend_enter(suspend_state_t state)
{
	int cpu = raw_smp_processor_id();
	struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
	struct lpm_cpu *lpm_cpu = cluster->cpu;
	const struct cpumask *cpumask = get_cpu_mask(cpu);
	int idx;

	for (idx = lpm_cpu->nlevels - 1; idx >= 0; idx--) {

		if (lpm_cpu_mode_allow(cpu, idx, false))
			break;
	}
	if (idx < 0) {
		pr_err("Failed suspend\n");
		return 0;
	}
	cpu_prepare(cluster, idx, false);
	cluster_prepare(cluster, cpumask, idx, false);
	if (idx > 0)
		update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
					0xdeaffeed, false);

	/*
	 * Print the clocks which are enabled during system suspend
	 * This debug information is useful to know which are the
	 * clocks that are enabled and preventing the system level
	 * LPMs(XO and Vmin).
	 */
	clock_debug_print_enabled();

	if (!use_psci)
		msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, false);//执行wfi,进入睡眠等待中断唤醒
	else
		psci_enter_sleep(cluster, idx, true);

	if (idx > 0)
		update_debug_pc_event(CPU_EXIT, idx, true, 0xdeaffeed,
					false);
	//cpu从待机状态恢复正常
	cluster_unprepare(cluster, cpumask, idx, false);
	cpu_unprepare(cluster, idx, false);
	return 0;
}

msm_cpu_pm_enter_sleep里面执行wfi指令,对于唤醒时间统计可以从这个函数之后进行记时。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值