
然后是进入函数
// kernel\power\suspends.c
/**
* 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;
pm_suspend_target_state = state;
if (state == PM_SUSPEND_TO_IDLE)
pm_set_suspend_no_platform();
error = platform_suspend_begin(state);
if (error)
goto Close;
suspend_console();
suspend_test_start();
error = dpm_suspend_start(PMSG_SUSPEND);
if (error) {
pr_err("Some devices failed to suspend, or early wake event detected\n");
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);
pm_suspend_target_state = PM_SUSPEND_ON;
return error;
Recover_platform:
platform_recover(state);
goto Resume_devices;
}
然后进入函数 platform_suspend_begin(state)中,判断平台代码中是否有begain函数,如果有就调用相关的begain函数。
// kernel\power\suspends.c
static int platform_suspend_begin(suspend_state_t state)
{
if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->begin)
return s2idle_ops->begin();
else if (suspend_ops && suspend_ops->begin)
return suspend_ops->begin(state);
else
return 0;
}
然后调用suspend_console挂起控制台,此时printk()函数无法显示。
// kernel\printk\printk.c
/**
* suspend_console - suspend the console subsystem
*
* This disables printk() while we go into suspend states
*/
void suspend_console(void)
{
if (!console_suspend_enabled)
return;
pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
console_lock();
console_suspended = 1;
up_console_sem();
}
然后是进入到函数dpm_suspend_start(PMSG_SUSPEND)
// devices\base\power\main.c
/**
* dpm_suspend_start - Prepare devices for PM transition and suspend them.
* @state: PM transition of the system being carried out.
*
* Prepare all non-sysdev devices for system PM transition and execute "suspend"
* callbacks for them.
*/
int dpm_suspend_start(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error;
error = dpm_prepare(state);
if (error) {
suspend_stats.failed_prepare++;
dpm_save_failed_step(SUSPEND_PREPARE);
} else
error = dpm_suspend(state);
dpm_show_time(starttime, state, error, "start");
return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);
上边这个函数主要实现了电源管理的准备过程和设备的挂起过程。
通过error = dpm_prepare(state);实现了流程图中的过程3。
通过error = dpm_suspend(state);实现了流程图中的过程4。
// drivers\base\power\main.c
/**
* 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();
/*
* Give a chance for the known devices to complete their probes, before
* disable probing of devices. This sync point is important at least
* at boot time + hibernation restore.
*/
wait_for_device_probe();
/*
* It is unsafe if probing of devices will happen during suspend or
* hibernation and system behavior will be unpredictable in this case.
* So, let's prohibit device's probing here and defer their probes
* instead. The normal behavior will be restored in dpm_complete().
*/
device_block_probing();
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);
trace_device_pm_callback_start(dev, "", state.event);
error = device_prepare(dev, state);
trace_device_pm_callback_end(dev, error);
mutex_lock(&dpm_list_mtx);
if (error) {
if (error == -EAGAIN) {
put_device(dev);
error = 0;
continue;
}
pr_info("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;
}
其中dpm_list为活动设备的列表。
// drivers\base\power\power.h
/* drivers/base/power/main.c */
extern struct list_head dpm_list; /* The active device list */
这里是遍历所有活动设备的列表dpm_list,然后依次进入device_prepare(dev, state)函数,如果执行成功,将dev->power.is_prepared(就是struct dev_pm_info类型的变量)设为TRUE,表示设备已经prepared了。同时,将该设备添加到dpm_prepared_list中(该链表保存了所有已经处于prepared状态的设备)。
关于dpm_list,设备模型在添加设备(device_add)时,会调用device_pm_add接口,将该设备添加到全局链表dpm_list中,以方便后续的遍历操作。如下所示。
// driver\base\power\main.c
/**
* device_pm_add - Add a device to the PM core's list of active devices.
* @dev: Device to add to the list.
*/
void device_pm_add(struct device *dev)
{
/* Skip PM setup/initialization. */
if (device_pm_not_required(dev))
return;
pr_debug("Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
device_pm_check_callbacks(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);
dev->power.in_dpm_list = true;
mutex_unlock(&dpm_list_mtx);
}
调用device_prepare执行实际的prepare动作。
// drivers\base\power\main.c
/**
* device_prepare - Prepare a device for system power transition.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
*
* Execute the ->prepare() callback(s) for given device. No new children of the
* device may be registered after this function has returned.
*/
static int device_prepare(struct device *dev, pm_message_t state)
{
int (*callback)(struct device *) = 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 = false;
if (dev->power.no_pm_callbacks)
goto unlock;
if (dev->pm_domain)
callback = dev->pm_domain->ops.prepare;
else if (dev->type && dev->type->pm)
callback = dev->type->pm->prepare;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->prepare;
else if (dev->bus && dev->bus->pm)
callback = dev->bus->pm->prepare;
if (!callback && dev->driver && dev->driver->pm)
callback = dev->driver->pm->prepare;
if (callback)
ret = callback(dev);
unlock:
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 = state.event == PM_EVENT_SUSPEND &&
(ret > 0 || dev->power.no_pm_callbacks) &&
!dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
spin_unlock_irq(&dev->power.lock);
return 0;
}
内部接口device_prepare的执行动作为:
1)根据dev->power.syscore,断该设备是否为syscore设备。如果是,则直接返回(因为syscore设备会单独处理)。
2)在prepare时期,调用pm_runtime_get_noresume接口,关闭Runtime suspend功能。以避免由Runtime suspend造成的不能正常唤醒的Issue。该功能会在complete时被重新开启。
【注:pm_runtime_get_noresume的实现很简单,就是增加该设备power变量的引用计数(dev->power.usage_count),Runtime PM会根据该计数是否大于零,判断是否开启Runtime PM功能。】
3)该设备默认不是一个wakeup path(记录在dev->power.wakeup_path中)。
【注:设备的wake up功能,是指系统在低功耗状态下(如suspend、hibernate),某些设备具备唤醒系统的功能。这是电源管理过程的一部分。】
4)根据优先顺序,获得用于prepare的callback函数。由于设备模型有bus、driver、device等多个层级,而prepare接口可能由任意一个层级实现。这里的优先顺序是指,只要优先级高的层级注册了prepare,就会优先使用它,而不会使用优先级低的prepare。优先顺序为:dev->pm_domain->ops、dev->type->pm、dev->class->pm、dev->bus->pm、dev->driver->pm(这个优先顺序同样适用于其它callbacks)。
5)如果得到有限的prepare函数,调用并返回结果。

本文深入探讨了Linux内核在设备挂起过程中的pm_prepare阶段,包括platform_suspend_begin函数的调用,控制台的挂起,以及dpm_suspend_start函数中设备的准备和挂起步骤。在设备挂起过程中,系统遍历活动设备列表,调用device_prepare进行设备的准备工作,关闭Runtime suspend功能,并根据设备的wakeup_path和callback函数进行相应处理。
1930

被折叠的 条评论
为什么被折叠?



