前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux电源管理-Suspend/Resume流程

Linux电源管理-Suspend/Resume流程

作者头像
DragonKingZhu
发布2020-03-24 14:44:56
7.8K0
发布2020-03-24 14:44:56
举报

前言

根据上一节linux电源管理-概述可知,linux电源管理存在的几种方式,如何查看这几种方式,以及最后的如何睡眠唤醒等。

通过echo mem > /sys/power/state就可以达到睡眠,所以可以根据此节点的sys代码分析suspend的流程。

suspend代码分析

在手机端执行如下命令:

代码语言:javascript
复制
echo mem > /sys/power/state

根据sys节点的属性命令规则,可以此节点的实现代码为: state_store

state_store函数分析

代码语言:javascript
复制
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
			   const char *buf, size_t n)
{
	suspend_state_t state;
	int error;

	error = pm_autosleep_lock();
	if (error)
		return error;

	if (pm_autosleep_state() > PM_SUSPEND_ON) {
		error = -EBUSY;
		goto out;
	}

	state = decode_state(buf, n);
	if (state < PM_SUSPEND_MAX)
		error = pm_suspend(state);
	else if (state == PM_SUSPEND_MAX)
		error = hibernate();
	else
		error = -EINVAL;

 out:
	pm_autosleep_unlock();
	return error ? error : n;
}

1) pm_autosleep_lock

代码语言:javascript
复制
int pm_autosleep_lock(void)
{
	return mutex_lock_interruptible(&autosleep_lock);
}

获得autosleep锁,锁住autosleep功能,此功能在后面分析。

2. 判断当前autosleep的状态,如果当前状态大于PM_SUSPEND_ON则,返回退出。关于suspend的状态如下:

代码语言:javascript
复制
#define PM_SUSPEND_ON		((__force suspend_state_t) 0)
#define PM_SUSPEND_FREEZE	((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY	((__force suspend_state_t) 2)
#define PM_SUSPEND_MEM		((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN		PM_SUSPEND_FREEZE
#define PM_SUSPEND_MAX		((__force suspend_state_t) 4)

3. 解析当前传入的state。如果state小于PM_SUSPEND_MAX就走suspend流程,等于PM_SUSPEND_MAX就走hibernate流程。加入我们传入的是mem, 则就会走suspend流程。

pm_suspend函数分析

代码语言:javascript
复制
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;
}

1. 依然会再次判断当前的state是否在PM_SUSPEND_ON和PM_SUSPEND_MAX之间

2. pm_suspend_marker("entry")

代码语言:javascript
复制
static void pm_suspend_marker(char *annotation)
{
	struct timespec ts;
	struct rtc_time tm;

	getnstimeofday(&ts);
	rtc_time_to_tm(ts.tv_sec, &tm);
	pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
		annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
		tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}

在suspend之间记录时间,用于统计或者调试suspend花费的时间

3. 调用enter_state进入suspend的下一步,如果执行suspend成功,增加suspend.success的引用计数,否则增加suspend.fail的引用计数。

enter_state函数分析

代码语言:javascript
复制
static int enter_state(suspend_state_t state)
{
	int error;

	trace_suspend_resume(TPS("suspend_enter"), state, true);
	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();

	trace_suspend_resume(TPS("sync_filesystems"), 0, true);
	printk(KERN_INFO "PM: Syncing filesystems ... ");
	sys_sync();
	printk("done.\n");
	trace_suspend_resume(TPS("sync_filesystems"), 0, false);

	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
	error = suspend_prepare(state);
	if (error)
		goto Unlock;

	if (suspend_test(TEST_FREEZER))
		goto Finish;

	trace_suspend_resume(TPS("suspend_enter"), state, false);
	pr_debug("PM: Entering %s sleep\n", pm_states[state]);
	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;
}

1. 通过vaild_state函数用来判断该平台是否支持该状态睡眠。

代码语言:javascript
复制
static bool valid_state(suspend_state_t state)
{
	/*
	 * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
	 * support and need to be valid to the low level
	 * implementation, no valid callback implies that none are valid.
	 */
	return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}

根据注释可知,standby和mem状态是处于低功耗状态下的,需要平台代码来支持实现的。因此内核使用platform_suspend_ops来定义各个平台的pm实现,然后通过suspend_set_ops函数设置具体平台pm到suspend_ops中。最终还是通过vaild函数来判断该平台是否支持需要睡眠的状态。

内核也提供了只支持mem睡眠的函数

代码语言:javascript
复制
int suspend_valid_only_mem(suspend_state_t state)
{
	return state == PM_SUSPEND_MEM;
}

当然了如果state状态是freeze的话直接继续执行。

2. 调用mutex_trylock获得一个mutex锁,防止在suspend的时候再次suspend。

3. 如果当前state是PM_SUSPEND_FREEZE,则调用freeze_begin做开始准备工作。

4. 同步文件系统。

5. 调用suspend_prepare做进一步suspend前期准备工作,准备控制台,冻结内核线程等。

6. 调用suspend_devices_and_enter做设备以及系统相关的susupend操作。

7. 调用suspend_finish做最后的恢复工作。

suspend_prepare函数分析

代码语言:javascript
复制
/**
 * suspend_prepare - Prepare for entering system sleep state.
 *
 * Common code run for every system sleep state that can be entered (except for
 * hibernation).  Run suspend notifiers, allocate the "suspend" console and
 * freeze processes.
 */
static int suspend_prepare(suspend_state_t state)
{
	int error;

	if (!sleep_state_supported(state))
		return -EPERM;

	pm_prepare_console();

	error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
	if (error)
		goto Finish;

	trace_suspend_resume(TPS("freeze_processes"), 0, true);
	error = suspend_freeze_processes();
	trace_suspend_resume(TPS("freeze_processes"), 0, false);
	if (!error)
		return 0;

	suspend_stats.failed_freeze++;
	dpm_save_failed_step(SUSPEND_FREEZE);
 Finish:
	pm_notifier_call_chain(PM_POST_SUSPEND);
	pm_restore_console();
	return error;
}

1. 检测该平台suspend_ops是否实现了enter函数

代码语言:javascript
复制
static bool sleep_state_supported(suspend_state_t state)
{
	return state == PM_SUSPEND_FREEZE || (suspend_ops && suspend_ops->enter);
}

2. 调用pm_prepare_console函数切换控制台,重新分配一个suspend模式下控制台,然后重定向kmsg。

3. 通过调用pm通知链,发送PM_SUSPEND_PREPARE消息。

代码语言:javascript
复制
int pm_notifier_call_chain(unsigned long val)
{
	int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL);

	return notifier_to_errno(ret);
}

那谁会收到这类消息呢? 只有通过register_pm_notifier的设备,子系统会在这个时候处理自己的事情。

代码语言:javascript
复制
int register_pm_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&pm_chain_head, nb);
}

4. 调用suspend_freeze_processes冻结userhelper进程,已经内核线程。如果冻结出现失败,记录失败的引用计数。

5. 接着会通过通知链恢复suspend,已经恢复控制台。

suspend_devices_and_enter函数分析

代码语言:javascript
复制
/**
 * suspend_devices_and_enter - Suspend devices and enter system sleep state.
 * @state: System sleep state to enter.
 */
int suspend_devices_and_enter(suspend_state_t state)
{
	int error;
	bool wakeup = false;

	if (!sleep_state_supported(state))
		return -ENOSYS;

	error = platform_suspend_begin(state);
	if (error)
		goto Close;

	suspend_console();
	suspend_test_start();
	error = dpm_suspend_start(PMSG_SUSPEND);
	if (error) {
		pr_err("PM: Some devices failed to suspend, or early wake event detected\n");
		log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected");
		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 && platform_suspend_again(state));

 Resume_devices:
	suspend_test_start();
	dpm_resume_end(PMSG_RESUME);
	suspend_test_finish("resume devices");
	trace_suspend_resume(TPS("resume_console"), state, true);
	resume_console();
	trace_suspend_resume(TPS("resume_console"), state, false);

 Close:
	platform_resume_end(state);
	return error;

 Recover_platform:
	platform_recover(state);
	goto Resume_devices;
}

1. 调用sleep_state_supported函数判断当前平台是否实现了suspend_ops,已经suspend_ops->enter函数。

2. 如果当前状态是freeze,就调用freeze_ops的begin函数。否则就调用平台相关的begin函数。这里的begin主要是各个平台pm的一些设置,每个平台的操作都不一样,这里不详细说明。

代码语言:javascript
复制
static int platform_suspend_begin(suspend_state_t state)
{
	if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->begin)
		return freeze_ops->begin();
	else if (suspend_ops->begin)
		return suspend_ops->begin(state);
	else
		return 0;
}

3. 调用suspend_console挂起控制台,防止其它代码访问该控制台。

4. 调用suspend_test_start记录当前suspend刚开始的时候的时间,使用jiffies表示。

代码语言:javascript
复制
void suspend_test_start(void)
{
	/* FIXME Use better timebase than "jiffies", ideally a clocksource.
	 * What we want is a hardware counter that will work correctly even
	 * during the irqs-are-off stages of the suspend/resume cycle...
	 */
	suspend_test_start_time = jiffies;
}

5. 调用dpm_suspend_start函数,该函数主要是调用所有设备的prepare和suspend回调函数。如果出现suspend失败,则会打印"fail suspend"的log,以及调用platform_recover函数执行平台相关的recover回调。

代码语言:javascript
复制
static void platform_recover(suspend_state_t state)
{
	if (state != PM_SUSPEND_FREEZE && suspend_ops->recover)
		suspend_ops->recover();
}

6. 调用suspend_enter使整个系统进入suspend状态。

dpm_suspend_start函数分析

代码语言:javascript
复制
int dpm_suspend_start(pm_message_t state)
{
	int error;

	error = dpm_prepare(state);
	if (error) {
		suspend_stats.failed_prepare++;
		dpm_save_failed_step(SUSPEND_PREPARE);
	} else
		error = dpm_suspend(state);
	return error;
}

1. 调用dpm_prepare函数,执行所有设备的prepare回调函数。执行顺序是pm_domain-type-class-bus-driver,如果失败设置failed_prepare的引用计数值。

2. 调用dpm_suspend函数,执行所有设备的suspend回调函数。

dpm_prepare函数分析

代码语言:javascript
复制
/**
 * dpm_prepare - Prepare all non-sysdev devices for a system PM transition.
 * @state: PM transition of the system being carried out.
 *
 * Execute the ->prepare() callback(s) for all devices.
 */
int dpm_prepare(pm_message_t state)
{
	int error = 0;

	trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
	might_sleep();

	mutex_lock(&dpm_list_mtx);
	while (!list_empty(&dpm_list)) {
		struct device *dev = to_device(dpm_list.next);

		get_device(dev);
		mutex_unlock(&dpm_list_mtx);

		error = device_prepare(dev, state);

		mutex_lock(&dpm_list_mtx);
		if (error) {
			if (error == -EAGAIN) {
				put_device(dev);
				error = 0;
				continue;
			}
			printk(KERN_INFO "PM: Device %s not prepared "
				"for power transition: code %d\n",
				dev_name(dev), error);
			put_device(dev);
			break;
		}
		dev->power.is_prepared = true;
		if (!list_empty(&dev->power.entry))
			list_move_tail(&dev->power.entry, &dpm_prepared_list);
		put_device(dev);
	}
	mutex_unlock(&dpm_list_mtx);
	trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
	return error;
}

1. 判断dpm_list是否为空。那这个dpm_list是在哪里设置的呢? dpm_list是在device_add的时候调用device_pm_add函数,将当前的设备添加到dpm_list中的。

代码语言:javascript
复制
void device_pm_add(struct device *dev)
{
	pr_debug("PM: Adding info for %s:%s\n",
		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
	mutex_lock(&dpm_list_mtx);
	if (dev->parent && dev->parent->power.is_prepared)
		dev_warn(dev, "parent %s should not be sleeping\n",
			dev_name(dev->parent));
	list_add_tail(&dev->power.entry, &dpm_list);
	mutex_unlock(&dpm_list_mtx);
}

2. 调用get_device增加设备的引用计数,然后调用device_prepare函数调用设备的prepare回调。如果失败减少设备的引用计数。

3. 设置该设备的is_prepared标志位,然后将该设备添加到dom_prepared_list链表中。

device_prepare函数分析

代码语言:javascript
复制
static int device_prepare(struct device *dev, pm_message_t state)
{
	int (*callback)(struct device *) = NULL;
	char *info = NULL;
	int ret = 0;

	if (dev->power.syscore)
		return 0;

	/*
	 * If a device's parent goes into runtime suspend at the wrong time,
	 * it won't be possible to resume the device.  To prevent this we
	 * block runtime suspend here, during the prepare phase, and allow
	 * it again during the complete phase.
	 */
	pm_runtime_get_noresume(dev);

	device_lock(dev);

	dev->power.wakeup_path = device_may_wakeup(dev);

	if (dev->pm_domain) {
		info = "preparing power domain ";
		callback = dev->pm_domain->ops.prepare;
	} else if (dev->type && dev->type->pm) {
		info = "preparing type ";
		callback = dev->type->pm->prepare;
	} else if (dev->class && dev->class->pm) {
		info = "preparing class ";
		callback = dev->class->pm->prepare;
	} else if (dev->bus && dev->bus->pm) {
		info = "preparing bus ";
		callback = dev->bus->pm->prepare;
	}

	if (!callback && dev->driver && dev->driver->pm) {
		info = "preparing driver ";
		callback = dev->driver->pm->prepare;
	}

	if (callback) {
		trace_device_pm_callback_start(dev, info, state.event);
		ret = callback(dev);
		trace_device_pm_callback_end(dev, ret);
	}

	device_unlock(dev);

	if (ret < 0) {
		suspend_report_result(callback, ret);
		pm_runtime_put(dev);
		return ret;
	}
	/*
	 * A positive return value from ->prepare() means "this device appears
	 * to be runtime-suspended and its state is fine, so if it really is
	 * runtime-suspended, you can leave it in that state provided that you
	 * will do the same thing with all of its descendants".  This only
	 * applies to suspend transitions, however.
	 */
	spin_lock_irq(&dev->power.lock);
	dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND;
	spin_unlock_irq(&dev->power.lock);
	return 0;
}

此函数就是从设备的pm_domain, type, class,bus,driver一直调用下来。通常情况下就会调用到driver中的prepare函数中。

dpm_suspend函数分析

当对系统中的所有设备调用prepare回调函数之后,就会调用所有设备的suspend回调函数。

代码语言:javascript
复制
int dpm_suspend(pm_message_t state)
{
	ktime_t starttime = ktime_get();
	int error = 0;

	trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
	might_sleep();

	cpufreq_suspend();

	mutex_lock(&dpm_list_mtx);
	pm_transition = state;
	async_error = 0;
	while (!list_empty(&dpm_prepared_list)) {
		struct device *dev = to_device(dpm_prepared_list.prev);

		get_device(dev);
		mutex_unlock(&dpm_list_mtx);

		error = device_suspend(dev);

		mutex_lock(&dpm_list_mtx);
		if (error) {
			pm_dev_err(dev, state, "", error);
			dpm_save_failed_dev(dev_name(dev));
			put_device(dev);
			break;
		}
		if (!list_empty(&dev->power.entry))
			list_move(&dev->power.entry, &dpm_suspended_list);
		put_device(dev);
		if (async_error)
			break;
	}
	mutex_unlock(&dpm_list_mtx);
	async_synchronize_full();
	if (!error)
		error = async_error;
	if (error) {
		suspend_stats.failed_suspend++;
		dpm_save_failed_step(SUSPEND_SUSPEND);
	} else
		dpm_show_time(starttime, state, NULL);
	trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
	return error;
}

对之前加入dpm_prepared_list链表的设备,调用device_suspend函数。然后该此设备又加入到dpm_suspend_list链表中。如果出现suspend失败,就打印log,更新failed_suspend的值。在调用到device_suspend函数中,会判断是否支持异步suspend操作,这里不关心细节,主要分析主流程,最后调用到__device_suspend函数中。

__device_suspend函数分析

代码语言:javascript
复制
static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{
	pm_callback_t callback = NULL;
	char *info = NULL;
	int error = 0;
	struct timer_list timer;
	struct dpm_drv_wd_data data;
	char suspend_abort[MAX_SUSPEND_ABORT_LEN];
	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()) {
		pm_get_active_wakeup_sources(suspend_abort,
			MAX_SUSPEND_ABORT_LEN);
		log_suspend_abort_reason(suspend_abort);
		async_error = -EBUSY;
		goto Complete;
	}

	if (dev->power.syscore)
		goto Complete;
	
	data.dev = dev;
	data.tsk = get_current();
	init_timer_on_stack(&timer);
	timer.expires = jiffies + HZ * 12;
	timer.function = dpm_drv_timeout;
	timer.data = (unsigned long)&data;
	add_timer(&timer);

	if (dev->power.direct_complete) {
		if (pm_runtime_status_suspended(dev)) {
			pm_runtime_disable(dev);
			if (pm_runtime_suspended_if_enabled(dev))
				goto Complete;

			pm_runtime_enable(dev);
		}
		dev->power.direct_complete = false;
	}

	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) {
		struct device *parent = dev->parent;

		dev->power.is_suspended = true;
		if (parent) {
			spin_lock_irq(&parent->power.lock);

			dev->parent->power.direct_complete = false;
			if (dev->power.wakeup_path
			    && !dev->parent->power.ignore_children)
				dev->parent->power.wakeup_path = true;

			spin_unlock_irq(&parent->power.lock);
		}
	}

	device_unlock(dev);
	dpm_watchdog_clear(&wd);

	del_timer_sync(&timer);
	destroy_timer_on_stack(&timer);

 Complete:
	complete_all(&dev->power.completion);
	if (error)
		async_error = error;

	return error;
}

1. 调用dpm_wait_for_children使用异步等待该设备的所有孩子就绪。

2. 如果此时有wakup事件发生,应该停止系统suspend。

3. 如果没有wakup事件发生,创建一个12s的定时器,然后启动定时器。如果在12s之内suspend没有处理完成,就打印call stack,导致系统panic。

代码语言:javascript
复制
static void dpm_drv_timeout(unsigned long data)
{
	struct dpm_drv_wd_data *wd_data = (void *)data;
	struct device *dev = wd_data->dev;
	struct task_struct *tsk = wd_data->tsk;

	printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev),
	       (dev->driver ? dev->driver->name : "no driver"));

	printk(KERN_EMERG "dpm suspend stack:\n");
	show_stack(tsk, NULL);

	BUG();
}

4. 判断该设备是否在suspend之前已经发生了runtime_suspend。如果该设备已经处于suspend则可以直接返回。

5. 依次调用subsystem-level(pm_domain, type, class, bus)级别的suspend回调函数,如果subsystem-level级别的suspend回调函数都没有实现,则调用driver的suspend回调。

6. 销毁之前创建的定时器。

suspend_enter函数分析

在之前对dpm_suspend_start函数进行了分析,该函数中主要是调用所有设备的prepare和suspend回调函数。而在suspend_enter主要是使系统进入到suspend中。

代码语言:javascript
复制
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
	char suspend_abort[MAX_SUSPEND_ABORT_LEN];
	int error, last_dev;

	error = platform_suspend_prepare(state);
	if (error)
		goto Platform_finish;

	error = dpm_suspend_late(PMSG_SUSPEND);
	if (error) {
		last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
		last_dev %= REC_FAILED_NUM;
		printk(KERN_ERR "PM: late suspend of devices failed\n");
		log_suspend_abort_reason("%s device failed to power down",
			suspend_stats.failed_devs[last_dev]);
		goto Platform_finish;
	}
	error = platform_suspend_prepare_late(state);
	if (error)
		goto Devices_early_resume;

	error = dpm_suspend_noirq(PMSG_SUSPEND);
	if (error) {
		last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
		last_dev %= REC_FAILED_NUM;
		printk(KERN_ERR "PM: noirq suspend of devices failed\n");
		log_suspend_abort_reason("noirq suspend of %s device failed",
			suspend_stats.failed_devs[last_dev]);
		goto Platform_early_resume;
	}
	error = platform_suspend_prepare_noirq(state);
	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) {
		trace_suspend_resume(TPS("machine_suspend"), state, true);
		freeze_enter();
		trace_suspend_resume(TPS("machine_suspend"), state, false);
		goto Platform_wake;
	}

	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)) {
			trace_suspend_resume(TPS("machine_suspend"),
				state, true);
			error = suspend_ops->enter(state);
			trace_suspend_resume(TPS("machine_suspend"),
				state, false);
			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:
	enable_nonboot_cpus();

 Platform_wake:
	platform_resume_noirq(state);
	dpm_resume_noirq(PMSG_RESUME);

 Platform_early_resume:
	platform_resume_early(state);

 Devices_early_resume:
	dpm_resume_early(PMSG_RESUME);

 Platform_finish:
	platform_resume_finish(state);
	return error;
}

1. 调用平台相关prepare回调函数,如果平台prepare设置失败,在调用平台相关的finish回调函数。

2. 调用dpm_suspend_late函数。此函数主要调用dpm_suspend_list中的设备的suspend_late回调函数,然后又将这些设备加入到dpm_late_early_list链表中。如果出现失败,则跳到platform_finish做恢复工作。

3. 如果当前休眠状态是PM_SUSPEND_FREEZE的话,调用freeze_ops中的prepare回调。

4. 调用dpm_suspend_noirq函数,此函数主要是从dpm_late_early_list链表中取一个设备,然后调用该设备的suspend_noirq回调,同时将该设备加入到dpm_noirq_list链表中。

5. 回调平台相关的preplate_late函数,做suspend最后关头的事情。

6. 如果休眠状态是PM_SUSPEND_FREEZE,则frozen processes + suspended devices + idle processors

7. disable所有非nonboot的CPU,失败之后启动CPU。

8. 关掉全局cpu中断,如果关掉中断,则报BUG

代码语言:javascript
复制
	arch_suspend_disable_irqs();
	BUG_ON(!irqs_disabled());

9. 执行所有system core的suspend回调函数。

10. 如果执行成功的话,这时候系统还会调用pm_wakeup_pending检查下,是否有唤醒事件发生,如果发生停止suspend,恢复系统。

11. 这时候系统已经睡眠,如果这时候有唤醒事件发生,比如按下手机的power按键,系统又会接着suspend的地方,再次往下执行。也就是suspend的一些列反操作。

suspend/resume过程总结

如下是suspend/resume过程的简图

以上就是整个系统的suspend/resume执行过程,但是对于一般的驱动开发工程师来说主要关心的是Device Suspend和Device Resume过程。

suspend: prepare->suspend->suspend_late->suspend_noirq

resume: resume_noirq->resume_early->resume->complete

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • suspend代码分析
    • state_store函数分析
      • pm_suspend函数分析
        • enter_state函数分析
          • suspend_prepare函数分析
            • suspend_devices_and_enter函数分析
              • dpm_suspend_start函数分析
                • dpm_prepare函数分析
                  • device_prepare函数分析
                    • dpm_suspend函数分析
                      • __device_suspend函数分析
                        • suspend_enter函数分析
                        • suspend/resume过程总结
                        相关产品与服务
                        腾讯云代码分析
                        腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档