Linux电源管理-Suspend/Resume流程

前言

根据上一节linux电源管理-概述可知,linux电源管理存在的几种方式,如何查看这几种方式,以及最后的如何睡眠唤醒等。
通过echo mem > /sys/power/state就可以达到睡眠,所以可以根据此节点的sys代码分析suspend的流程。

suspend代码分析

在手机端执行如下命令:
echo mem > /sys/power/state
根据sys节点的属性命令规则,可以此节点的实现代码为:  state_store

state_store函数分析

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
int pm_autosleep_lock(void)
{
	return mutex_lock_interruptible(&autosleep_lock);
}
获得autosleep锁,锁住autosleep功能,此功能在后面分析。
2. 判断当前autosleep的状态,如果当前状态大于PM_SUSPEND_ON则,返回退出。关于suspend的状态如下:
#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函数分析

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")
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函数分析

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函数用来判断该平台是否支持该状态睡眠。
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睡眠的函数
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函数分析

/**
 * 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函数
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消息。
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的设备,子系统会在这个时候处理自己的事情。
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函数分析

/**
 * 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的一些设置,每个平台的操作都不一样,这里不详细说明。
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表示。
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回调。
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函数分析

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函数分析

/**
 * 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中的。
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函数分析

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回调函数。
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函数分析

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。
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中。
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
	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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值