11. 性能分析和优化
11.1 休眠时间分析
休眠时间测量:
c
// kernel/power/suspend.c
static int enter_state(suspend_state_t state)
{
ktime_t start, end;
int error;
start = ktime_get();
// ... 休眠流程 ...
end = ktime_get();
pr_info("PM: suspend took %lld ms\n", ktime_to_ms(ktime_sub(end, start)));
return error;
}
各阶段时间分析:
diff
总休眠时间 = 进程冻结时间 + 设备 suspend 时间 + 平台 suspend 时间
进程冻结时间:
- 冻结用户空间进程:通常 100-500ms
- 冻结内核线程:通常 10-50ms
设备 suspend 时间:
- 设备 suspend 回调:取决于设备数量,通常 50-200ms
- 设备 suspend_late:通常 10-50ms
- 设备 suspend_noirq:通常 10-50ms
平台 suspend 时间:
- 平台 suspend_prepare:通常 10-50ms
- 进入休眠状态:通常 10-100ms
11.2 唤醒时间分析
唤醒时间测量:
c
// kernel/power/suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
ktime_t start, end;
int error;
// 进入休眠
start = ktime_get();
error = suspend_ops->enter(state);
end = ktime_get();
pr_info("PM: suspend_enter took %lld ms\n",
ktime_to_ms(ktime_sub(end, start)));
return error;
}
各阶段时间分析:
diff
总唤醒时间 = 平台 resume 时间 + 设备 resume 时间 + 进程解冻时间
平台 resume 时间:
- 从休眠状态恢复:通常 10-100ms
- 平台 resume_prepare:通常 10-50ms
设备 resume 时间:
- 设备 resume_noirq:通常 10-50ms
- 设备 resume_early:通常 10-50ms
- 设备 resume:取决于设备数量,通常 50-200ms
进程解冻时间:
- 解冻内核线程:通常 10-50ms
- 解冻用户空间进程:通常 100-500ms
11.3 性能优化建议
减少休眠时间:
-
优化进程冻结:
- 减少需要冻结的进程数量
- 优化进程冻结超时时间
-
优化设备 suspend:
- 减少设备数量
- 优化设备 suspend 回调
- 并行执行设备 suspend
-
优化平台 suspend:
- 优化平台特定的 suspend 实现
- 减少不必要的操作
减少唤醒时间:
-
优化设备 resume:
- 优化设备 resume 回调
- 并行执行设备 resume
-
优化进程解冻:
- 减少需要解冻的进程数量
- 优化进程解冻顺序
12. 故障排查和调试
12.1 调试工具
查看休眠日志:
bash
# 查看内核日志
dmesg | grep -i suspend
dmesg | grep -i resume
# 查看 PM 调试信息
dmesg | grep -i "PM:"
# 查看设备 suspend/resume 日志
dmesg | grep -i "device.*suspend"
dmesg | grep -i "device.*resume"
查看唤醒源:
bash
# 查看所有唤醒源
cat /sys/kernel/debug/wakeup_sources
# 查看特定设备的唤醒源
cat /sys/devices/.../power/wakeup
# 启用/禁用唤醒源
echo enabled > /sys/devices/.../power/wakeup
echo disabled > /sys/devices/.../power/wakeup
使用 ftrace 跟踪:
bash
# 启用 PM 跟踪
echo 1 > /sys/kernel/debug/tracing/events/power/enable
# 查看跟踪结果
cat /sys/kernel/debug/tracing/trace
12.2 常见问题排查
问题 1:系统无法进入休眠
排查步骤:
- 检查是否有活动的唤醒源:
bash
cat /sys/kernel/debug/wakeup_sources | grep active
- 检查设备是否阻止休眠:
bash
dmesg | grep -i "device.*prevent.*suspend"
- 检查进程是否无法冻结:
bash
dmesg | grep -i "freeze.*failed"
问题 2:系统无法唤醒
排查步骤:
- 检查唤醒源是否启用:
bash
cat /sys/devices/.../power/wakeup
- 检查硬件是否支持唤醒:
bash
cat /proc/acpi/wakeup
- 检查中断是否正常:
bash
cat /proc/interrupts
问题 3:唤醒后系统异常
排查步骤:
- 检查设备 resume 是否失败:
bash
dmesg | grep -i "device.*resume.*failed"
- 检查设备状态是否正确恢复:
bash
# 查看设备状态
cat /sys/devices/.../power/state
- 检查进程是否正常解冻:
bash
dmesg | grep -i "thaw.*failed"
13. 总结
13.1 关键点总结
-
休眠状态:
- Linux 支持多种休眠状态(mem、disk、freeze 等)
- 每种状态有不同的功耗和恢复时间
-
休眠流程:
- 冻结进程 → 设备 suspend → 保存状态 → 进入休眠
- 各阶段都有详细的实现和回调
-
唤醒流程:
- 唤醒事件 → 恢复状态 → 设备 resume → 解冻进程
- 需要正确处理各种唤醒源
-
设备支持:
- 设备驱动需要实现完整的 PM 回调
- 需要正确管理唤醒源
13.2 最佳实践
-
设备驱动开发:
- 正确实现所有 PM 回调函数
- 正确保存和恢复设备状态
- 正确管理唤醒源
-
性能优化:
- 减少休眠和唤醒时间
- 优化设备 suspend/resume 流程
- 优化进程冻结和解冻
-
调试和排查:
- 使用调试工具查看日志
- 检查唤醒源状态
- 检查设备 PM 状态
14. 平台特定的 Suspend 实现
14.1 platform_suspend_ops 结构
platform_suspend_ops 定义:
c
// include/linux/suspend.h
struct platform_suspend_ops {
int (*valid)(suspend_state_t state);
int (*begin)(suspend_state_t state);
int (*prepare)(void);
int (*prepare_late)(void);
bool (*wake)(void);
void (*finish)(void);
void (*end)(void);
void (*recover)(void);
bool (*suspend_again)(void);
int (*enter)(suspend_state_t state);
};
suspend_ops 的注册:
c
// kernel/power/suspend.c
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
lock_system_sleep();
suspend_ops = ops;
if (valid_state(PM_SUSPEND_STANDBY)) {
mem_sleep_default = PM_SUSPEND_STANDBY;
mem_sleep_current = PM_SUSPEND_STANDBY;
}
unlock_system_sleep();
}
14.2 ARM64 平台的 Suspend 实现示例
ARM64 平台的 suspend_ops:
c
// arch/arm64/kernel/suspend.c
static const struct platform_suspend_ops arm64_suspend_ops = {
.valid = arm64_suspend_valid,
.begin = arm64_suspend_begin,
.prepare = arm64_suspend_prepare,
.prepare_late = arm64_suspend_prepare_late,
.enter = arm64_suspend_enter,
.wake = arm64_suspend_wake,
.finish = arm64_suspend_finish,
.end = arm64_suspend_end,
.recover = arm64_suspend_recover,
};
static int __init arm64_pm_init(void)
{
suspend_set_ops(&arm64_suspend_ops);
return 0;
}
arm64_suspend_enter() 实现:
c
// arch/arm64/kernel/suspend.c
static int arm64_suspend_enter(suspend_state_t state)
{
int ret;
// 1. 禁用中断
local_irq_disable();
// 2. 刷新缓存
cpu_suspend(0, arm64_suspend_finisher);
// 3. 恢复中断
local_irq_enable();
return 0;
}
static int arm64_suspend_finisher(unsigned long arg)
{
// 1. 保存 CPU 状态
cpu_suspend_save();
// 2. 调用平台特定的 suspend 函数
ret = cpu_do_suspend(arg);
// 3. 如果返回,恢复 CPU 状态
cpu_suspend_restore();
return ret;
}
14.3 唤醒机制深度解析
唤醒中断的完整流程:
scss
硬件唤醒事件(按键、网络数据包等)
↓
硬件中断控制器
↓
CPU 中断处理
↓
唤醒中断处理函数
↓
pm_wakeup_event()
↓
pm_system_wakeup()
↓
从休眠状态恢复
Wake-on-LAN 实现:
c
// drivers/net/ethernet/example.c
static int example_suspend(struct device *dev)
{
struct net_device *netdev = dev_get_drvdata(dev);
struct example_private *priv = netdev_priv(netdev);
// 1. 停止网络接口
netif_device_detach(netdev);
// 2. 配置 Wake-on-LAN
if (priv->wol_enabled) {
// 启用 Magic Packet 唤醒
iowrite32(REG_WOL_ENABLE | REG_WOL_MAGIC_PACKET,
priv->base + REG_WOL_CTRL);
// 启用网卡唤醒中断
enable_irq_wake(priv->irq);
}
// 3. 进入低功耗模式
example_enter_low_power(priv);
return 0;
}
14.4 实际应用案例
移动设备的休眠唤醒:
Android 系统的休眠流程:
- 用户按下电源键或屏幕超时
- Android Framework 调用 PowerManager.goToSleep()
- 通过 sysfs 触发休眠:
echo mem > /sys/power/state - Linux 内核执行休眠流程
- 系统进入 Suspend to RAM
服务器 Wake-on-LAN 配置:
bash
# 1. 启用网卡的 Wake-on-LAN
ethtool -s eth0 wol g
# 2. 查看 Wake-on-LAN 状态
ethtool eth0 | grep Wake-on
# 3. 进入休眠
echo mem > /sys/power/state
# 4. 从远程发送 Magic Packet 唤醒
wakeonlan <MAC地址>
服务器定时唤醒:
bash
# 1. 设置 RTC 闹钟
rtcwake -m mem -s 3600 # 1小时后唤醒
# 2. 查看 RTC 状态
cat /sys/class/rtc/rtc0/wakealarm
# 3. 进入休眠
echo mem > /sys/power/state
15. 高级主题
15.1 混合休眠(Hybrid Sleep)
混合休眠的定义:
混合休眠是同时执行 Suspend to RAM 和 Suspend to Disk,这样即使断电也能从磁盘恢复。
混合休眠的实现:
c
// kernel/power/hibernate.c
int hibernate(void)
{
int error;
// 1. 保存到磁盘
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (error)
return error;
// 2. 如果支持,同时进入 RAM 休眠
if (hibernation_mode == HIBERNATION_PLATFORM) {
error = hibernation_platform_enter();
} else {
// 3. 否则只进入 RAM 休眠
error = pm_suspend(PM_SUSPEND_MEM);
}
return error;
}
15.2 自动休眠(Autosleep)
自动休眠的实现:
c
// kernel/power/autosleep.c
static int autosleep_thread(void *dummy)
{
while (!kthread_should_stop()) {
// 1. 等待超时或唤醒事件
if (autosleep_state == PM_AUTOSLEEP_ON) {
schedule_timeout_interruptible(
msecs_to_jiffies(pm_autosleep_timeout));
// 2. 检查是否可以休眠
if (pm_get_wakeup_count(&count, false)) {
// 3. 进入休眠
pm_suspend(PM_SUSPEND_MEM);
}
} else {
wait_event_interruptible(autosleep_wait_queue,
autosleep_state != PM_AUTOSLEEP_DISABLED);
}
}
return 0;
}
15.3 运行时电源管理(Runtime PM)
Runtime PM 与 Suspend 的关系:
Runtime PM 是设备级别的电源管理,而 Suspend 是系统级别的电源管理。
Runtime PM 的实现:
c
// drivers/base/power/runtime.c
int pm_runtime_suspend(struct device *dev)
{
int retval;
// 1. 检查是否可以 suspend
retval = rpm_check_suspend_allowed(dev);
if (retval < 0)
return retval;
// 2. 调用设备的 runtime_suspend 回调
if (dev->driver && dev->driver->pm && dev->driver->pm->runtime_suspend) {
retval = dev->driver->pm->runtime_suspend(dev);
}
return retval;
}
16. 底层原理深度解析
16.1 CPU 寄存器保存和恢复的汇编实现
ARM64 架构的寄存器保存:
assembly
// arch/arm64/kernel/sleep.S
/*
* cpu_suspend_enter - 保存 CPU 寄存器并进入休眠
*
* 参数:
* x0: 参数
* x1: suspend_finisher 函数指针
*/
ENTRY(__cpu_suspend_enter)
// 1. 保存通用寄存器(x19-x28 是 callee-saved)
stp x19, x20, [x0, #CPU_CTX_X19]
stp x21, x22, [x0, #CPU_CTX_X21]
stp x23, x24, [x0, #CPU_CTX_X23]
stp x25, x26, [x0, #CPU_CTX_X25]
stp x27, x28, [x0, #CPU_CTX_X27]
stp x29, lr, [x0, #CPU_CTX_X29]
// 2. 保存栈指针
mov x2, sp
str x2, [x0, #CPU_CTX_SP]
// 3. 保存系统寄存器
mrs x2, tpidr_el0
str x2, [x0, #CPU_CTX_TPIDR_EL0]
mrs x2, tpidrro_el0
str x2, [x0, #CPU_CTX_TPIDRRO_EL0]
mrs x2, contextidr_el1
str x2, [x0, #CPU_CTX_CONTEXTIDR_EL1]
// 4. 保存异常链接寄存器
mrs x2, elr_el1
str x2, [x0, #CPU_CTX_ELR_EL1]
mrs x2, spsr_el1
str x2, [x0, #CPU_CTX_SPSR_EL1]
// 5. 保存栈指针(EL0 和 EL1)
mrs x2, sp_el0
str x2, [x0, #CPU_CTX_SP_EL0]
mrs x2, sp_el1
str x2, [x0, #CPU_CTX_SP_EL1]
// 6. 调用 suspend_finisher 函数
mov x0, x1
bl cpu_do_suspend
// 7. 如果返回,恢复寄存器
b __cpu_suspend_exit
ENDPROC(__cpu_suspend_enter)
ARM64 架构的寄存器恢复:
assembly
// arch/arm64/kernel/sleep.S
/*
* __cpu_suspend_exit - 恢复 CPU 寄存器
*
* 参数:
* x0: CPU 上下文结构指针
*/
ENTRY(__cpu_suspend_exit)
// 1. 恢复系统寄存器
ldr x2, [x0, #CPU_CTX_TPIDR_EL0]
msr tpidr_el0, x2
ldr x2, [x0, #CPU_CTX_TPIDRRO_EL0]
msr tpidrro_el0, x2
ldr x2, [x0, #CPU_CTX_CONTEXTIDR_EL1]
msr contextidr_el1, x2
// 2. 恢复异常链接寄存器
ldr x2, [x0, #CPU_CTX_ELR_EL1]
msr elr_el1, x2
ldr x2, [x0, #CPU_CTX_SPSR_EL1]
msr spsr_el1, x2
// 3. 恢复栈指针
ldr x2, [x0, #CPU_CTX_SP_EL0]
msr sp_el0, x2
ldr x2, [x0, #CPU_CTX_SP_EL1]
msr sp_el1, x2
// 4. 恢复通用寄存器
ldp x19, x20, [x0, #CPU_CTX_X19]
ldp x21, x22, [x0, #CPU_CTX_X21]
ldp x23, x24, [x0, #CPU_CTX_X23]
ldp x25, x26, [x0, #CPU_CTX_X25]
ldp x27, x28, [x0, #CPU_CTX_X27]
ldp x29, lr, [x0, #CPU_CTX_X29]
// 5. 恢复栈指针
ldr x2, [x0, #CPU_CTX_SP]
mov sp, x2
// 6. 返回
ret
ENDPROC(__cpu_suspend_exit)
x86_64 架构的寄存器保存:
assembly
// arch/x86/power/hibernate_asm_64.S
/*
* save_processor_state - 保存 x86_64 CPU 状态
*/
ENTRY(save_processor_state)
// 1. 保存通用寄存器
pushq %rbp
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
// 2. 保存段寄存器
movq %ds, %rax
movq %rax, saved_context_ds(%rip)
movq %es, %rax
movq %rax, saved_context_es(%rip)
movq %fs, %rax
movq %rax, saved_context_fs(%rip)
movq %gs, %rax
movq %rax, saved_context_gs(%rip)
// 3. 保存控制寄存器
movq %cr0, %rax
movq %rax, saved_context_cr0(%rip)
movq %cr2, %rax
movq %rax, saved_context_cr2(%rip)
movq %cr3, %rax
movq %rax, saved_context_cr3(%rip)
movq %cr4, %rax
movq %rax, saved_context_cr4(%rip)
// 4. 保存浮点寄存器
fxsave saved_context_fxstate(%rip)
ret
ENDPROC(save_processor_state)
16.2 MMU 和页表的处理
页表在休眠中的处理:
c
// arch/arm64/mm/mmu.c
static void cpu_suspend_set_direct_map(void)
{
// 1. 创建临时页表(identity mapping)
// 这样在 MMU 关闭和重新开启时,物理地址和虚拟地址相同
struct mm_struct *mm = current->mm;
pgd_t *pgd = pgd_offset(mm, 0);
// 2. 创建 identity mapping
create_identity_mapping(pgd, __pa(_text), __pa(_end));
}
static void cpu_resume_set_direct_map(void)
{
// 1. 恢复正常的页表映射
struct mm_struct *mm = current->mm;
pgd_t *pgd = pgd_offset(mm, 0);
// 2. 恢复页表
restore_page_table(pgd);
}
MMU 的禁用和启用:
c
// arch/arm64/kernel/suspend.c
static int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
struct mm_struct *mm = current->mm;
int ret;
unsigned long flags;
// 1. 保存当前页表
if (idmap_pgd) {
cpu_set_reserved_ttbr0();
local_flush_tlb_all();
}
// 2. 执行 suspend
ret = __cpu_suspend_enter(arg, fn);
// 3. 恢复页表
if (idmap_pgd) {
cpu_unset_reserved_ttbr0();
local_flush_tlb_all();
}
return ret;
}
TLB 的刷新:
c
// arch/arm64/mm/tlbflush.c
static inline void local_flush_tlb_all(void)
{
unsigned long flags;
local_irq_save(flags);
// 1. 刷新本地 TLB
__flush_tlb_all();
// 2. 同步所有 CPU
dsb(nsh);
isb();
local_irq_restore(flags);
}
16.3 缓存一致性和内存屏障
缓存一致性的问题:
在休眠和唤醒过程中,数据可能存在于多个位置:
- CPU 缓存(L1、L2、L3)
- 内存(RAM)
- 设备缓冲区(DMA 缓冲区)
需要确保这些位置的数据一致性。
缓存刷新的实现:
c
// arch/arm64/mm/cache.S
/*
* __flush_dcache_all - 刷新所有数据缓存
*/
ENTRY(__flush_dcache_all)
// 1. 获取缓存信息
mrs x0, clidr_el1
and x3, x0, #0x7000000
lsr x3, x3, #23
// 2. 遍历所有缓存级别
mov x10, #0
loop1:
add x2, x10, x10, lsr #1
lsr x1, x0, x2
and x1, x1, #0x7
cmp x1, #2
b.lt skip
// 3. 刷新缓存级别
mov x9, x10
lsl x9, x9, #1
add x17, x16, x9, lsl #2
ldr x17, [x17]
mov x1, x17
lsr x1, x1, #13
and x1, x1, #0x7fffff
mov x2, x17
ubfx x7, x17, #3, #10
ubfx x8, x17, #13, #15
loop2:
mov x3, x7
loop3:
lsl x6, x3, x8
orr x11, x10, x6
lsl x6, x2, x1
orr x11, x11, x6
dc cisw, x11
subs x3, x3, #1
b.ge loop3
subs x2, x2, #1
b.ge loop2
skip:
add x10, x10, #2
cmp x3, x10
b.gt loop1
// 4. 同步
dsb sy
isb
ret
ENDPROC(__flush_dcache_all)
内存屏障的使用:
c
// include/asm-generic/barrier.h
/*
* 内存屏障确保内存操作的顺序
*/
// 1. 写屏障:确保之前的写入操作完成
#define wmb() asm volatile("dmb ishst" ::: "memory")
// 2. 读屏障:确保之前的读取操作完成
#define rmb() asm volatile("dmb ishld" ::: "memory")
// 3. 完整屏障:确保所有内存操作完成
#define mb() asm volatile("dmb ish" ::: "memory")
// 4. 数据同步屏障:确保所有数据访问完成
#define dsb(opt) asm volatile("dsb " #opt ::: "memory")
// 5. 指令同步屏障:确保所有指令完成
#define isb() asm volatile("isb" ::: "memory")
休眠中的缓存处理:
c
// arch/arm64/kernel/suspend.c
static int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
// 1. 刷新数据缓存到内存
flush_cache_all();
// 2. 内存屏障,确保缓存刷新完成
dsb(sy);
// 3. 执行 suspend
ret = __cpu_suspend_enter(arg, fn);
// 4. 如果返回,使缓存失效
invalidate_icache_all();
// 5. 内存屏障,确保缓存失效完成
isb();
return ret;
}
16.4 中断控制器的处理
中断控制器的 suspend:
c
// drivers/irqchip/irq-gic-v3.c
static int gic_suspend(void)
{
void __iomem *dist_base;
u32 val;
int i;
dist_base = gic_data.dist_base;
// 1. 保存中断控制器的寄存器状态
for (i = 0; i < GIC_DIST_REG_NUM; i++) {
gic_saved_regs[i] = readl_relaxed(dist_base + i * 4);
}
// 2. 禁用所有中断
writel_relaxed(0, dist_base + GIC_DIST_ENABLE_SET);
// 3. 配置唤醒中断
// 只保留唤醒源的中断启用
for (i = 0; i < wakeup_irqs_num; i++) {
writel_relaxed(1 << wakeup_irqs[i],
dist_base + GIC_DIST_ENABLE_SET);
}
// 4. 进入低功耗模式
val = readl_relaxed(dist_base + GIC_DIST_CTRL);
val |= GIC_DIST_CTRL_DISABLE;
writel_relaxed(val, dist_base + GIC_DIST_CTRL);
return 0;
}
中断控制器的 resume:
c
// drivers/irqchip/irq-gic-v3.c
static void gic_resume(void)
{
void __iomem *dist_base;
u32 val;
int i;
dist_base = gic_data.dist_base;
// 1. 退出低功耗模式
val = readl_relaxed(dist_base + GIC_DIST_CTRL);
val &= ~GIC_DIST_CTRL_DISABLE;
writel_relaxed(val, dist_base + GIC_DIST_CTRL);
// 2. 恢复中断控制器的寄存器状态
for (i = 0; i < GIC_DIST_REG_NUM; i++) {
writel_relaxed(gic_saved_regs[i], dist_base + i * 4);
}
// 3. 重新初始化中断控制器
gic_dist_init();
gic_cpu_init();
}
16.5 时钟源的处理
时钟源的 suspend:
c
// drivers/clocksource/arm_arch_timer.c
static int arch_timer_suspend(void)
{
// 1. 保存时钟源状态
arch_timer_saved_regs.cntv_ctl = arch_timer_get_cntv_ctl();
arch_timer_saved_regs.cntv_cval = arch_timer_get_cntv_cval();
arch_timer_saved_regs.cntvct = arch_timer_get_cntvct();
// 2. 禁用虚拟定时器
arch_timer_set_cntv_ctl(0);
// 3. 如果支持,进入低功耗模式
if (arch_timer_has_power_management()) {
arch_timer_power_down();
}
return 0;
}
时钟源的 resume:
c
// drivers/clocksource/arm_arch_timer.c
static void arch_timer_resume(void)
{
u64 cval;
u32 ctl;
// 1. 如果支持,退出低功耗模式
if (arch_timer_has_power_management()) {
arch_timer_power_up();
}
// 2. 恢复时钟源状态
cval = arch_timer_saved_regs.cntv_cval;
ctl = arch_timer_saved_regs.cntv_ctl;
// 3. 设置比较值
arch_timer_set_cntv_cval(cval);
// 4. 恢复控制寄存器
arch_timer_set_cntv_ctl(ctl);
// 5. 重新初始化时钟源
arch_timer_init();
}
16.6 平台特定的硬件操作
平台电源管理单元(PMU)的操作:
c
// arch/arm64/mach-xxx/pm.c
static int platform_suspend_enter(suspend_state_t state)
{
void __iomem *pmu_base = ioremap(PMU_BASE, SZ_4K);
u32 val;
// 1. 保存 PMU 寄存器状态
platform_pmu_saved_regs.ctrl = readl(pmu_base + PMU_CTRL);
platform_pmu_saved_regs.status = readl(pmu_base + PMU_STATUS);
platform_pmu_saved_regs.wakeup_mask = readl(pmu_base + PMU_WAKEUP_MASK);
// 2. 配置唤醒源
val = readl(pmu_base + PMU_WAKEUP_MASK);
val |= (1 << WAKEUP_SOURCE_POWER_BUTTON);
val |= (1 << WAKEUP_SOURCE_RTC);
writel(val, pmu_base + PMU_WAKEUP_MASK);
// 3. 配置休眠模式
val = readl(pmu_base + PMU_CTRL);
val &= ~PMU_CTRL_SUSPEND_MODE_MASK;
val |= (state == PM_SUSPEND_MEM ? PMU_CTRL_SUSPEND_MODE_MEM :
PMU_CTRL_SUSPEND_MODE_STANDBY);
writel(val, pmu_base + PMU_CTRL);
// 4. 刷新缓存
flush_cache_all();
dsb(sy);
// 5. 进入休眠
val = readl(pmu_base + PMU_CTRL);
val |= PMU_CTRL_SUSPEND_ENABLE;
writel(val, pmu_base + PMU_CTRL);
// 6. 等待进入休眠(CPU 会在这里停止)
wfi(); // Wait For Interrupt
// 7. 如果返回,说明被唤醒
iounmap(pmu_base);
return 0;
}
WFI(Wait For Interrupt)指令:
c
// arch/arm64/include/asm/suspend.h
/*
* WFI (Wait For Interrupt) 是 ARM 架构的指令
* 使 CPU 进入低功耗状态,等待中断唤醒
*/
static inline void cpu_do_idle(void)
{
asm volatile(
"wfi\n"
:
:
: "memory"
);
}
16.7 内存映射和虚拟地址的处理
虚拟地址到物理地址的映射:
c
// arch/arm64/mm/mmu.c
/*
* 在休眠过程中,需要处理虚拟地址到物理地址的映射
* 因为 MMU 可能会被禁用和重新启用
*/
// 1. 创建 identity mapping(物理地址 = 虚拟地址)
static void create_identity_mapping(pgd_t *pgd, unsigned long start,
unsigned long end)
{
unsigned long addr;
pgd_t *pgdp;
pud_t *pudp;
pmd_t *pmdp;
pte_t *ptep;
for (addr = start; addr < end; addr += PAGE_SIZE) {
// 获取页表项
pgdp = pgd_offset_raw(pgd, addr);
pudp = pud_offset(pgdp, addr);
pmdp = pmd_offset(pudp, addr);
ptep = pte_offset_kernel(pmdp, addr);
// 创建映射(物理地址 = 虚拟地址)
set_pte(ptep, pfn_pte(__phys_to_pfn(addr), PAGE_KERNEL_EXEC));
}
// 刷新 TLB
flush_tlb_all();
}
页表切换的实现:
c
// arch/arm64/kernel/suspend.c
static void cpu_suspend_switch_mm(void)
{
struct mm_struct *mm = current->mm;
struct mm_struct *idmap_mm = &init_mm;
// 1. 切换到 identity mapping 的页表
cpu_switch_mm(idmap_mm->pgd, idmap_mm);
// 2. 刷新 TLB
local_flush_tlb_all();
}
16.8 多核 CPU 的协调
主 CPU 和从 CPU 的协调:
c
// kernel/power/suspend.c
static int suspend_disable_secondary_cpus(void)
{
int cpu, error = 0;
// 1. 禁用所有从 CPU
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
error = cpu_down(cpu);
if (error) {
pr_err("Failed to disable CPU %d: %d\n", cpu, error);
break;
}
}
// 2. 等待所有从 CPU 进入离线状态
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
wait_for_completion(&cpu_hotplug_done);
}
return error;
}
static void suspend_enable_secondary_cpus(void)
{
int cpu;
// 1. 启用所有从 CPU
for_each_possible_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
cpu_up(cpu);
}
}
从 CPU 的 suspend:
c
// arch/arm64/kernel/smp.c
static int cpu_suspend_cpu(unsigned long arg)
{
// 1. 从 CPU 进入 WFI 状态
cpu_do_idle();
// 2. 如果被唤醒,恢复执行
return 0;
}
16.9 设备 DMA 和缓存一致性
DMA 缓存一致性的处理:
c
// arch/arm64/mm/dma-mapping.c
static void dma_cache_maint_page(struct page *page, unsigned long offset,
size_t size, enum dma_data_direction dir,
void (*op)(const void *, size_t))
{
unsigned long pfn = page_to_pfn(page) + offset / PAGE_SIZE;
void *vaddr = page_address(page) + offset;
// 1. 如果是 DMA_TO_DEVICE,刷新缓存到内存
if (dir == DMA_TO_DEVICE) {
__dma_map_area(vaddr, size, DMA_TO_DEVICE);
}
// 2. 如果是 DMA_FROM_DEVICE,使缓存失效
else if (dir == DMA_FROM_DEVICE) {
__dma_unmap_area(vaddr, size, DMA_FROM_DEVICE);
}
// 3. 如果是 DMA_BIDIRECTIONAL,刷新并失效
else {
__dma_map_area(vaddr, size, DMA_BIDIRECTIONAL);
}
}
设备 suspend 中的 DMA 处理:
c
// drivers/example/example_driver.c
static int example_suspend(struct device *dev)
{
struct example_device *edev = dev_get_drvdata(dev);
// 1. 停止 DMA 传输
example_stop_dma(edev);
// 2. 同步 DMA 缓冲区
dma_sync_single_for_cpu(dev, edev->dma_addr, edev->dma_size,
DMA_BIDIRECTIONAL);
// 3. 保存 DMA 状态
edev->saved_dma_state = ioread32(edev->base + REG_DMA_CTRL);
// 4. 进入低功耗模式
example_enter_low_power(edev);
return 0;
}
16.10 中断嵌套和优先级
中断优先级的处理:
c
// arch/arm64/kernel/irq.c
static void suspend_disable_irqs(void)
{
// 1. 禁用所有中断(除了唤醒中断)
local_irq_disable();
// 2. 设置中断优先级掩码
// 只允许高优先级的中断(唤醒中断)
gic_set_irq_priority_mask(0xf0); // 只允许优先级 0-15 的中断
}
static void resume_enable_irqs(void)
{
// 1. 恢复中断优先级掩码
gic_set_irq_priority_mask(0x00); // 允许所有中断
// 2. 启用中断
local_irq_enable();
}
唤醒中断的特殊处理:
c
// drivers/base/power/wakeup.c
static irqreturn_t wakeup_irq_handler(int irq, void *dev_id)
{
struct wakeup_source *ws = dev_id;
// 1. 标记唤醒事件
__pm_stay_awake(ws);
// 2. 如果系统正在休眠,立即唤醒
if (pm_suspend_state()) {
// 3. 设置唤醒标志
pm_wakeup_irq = true;
// 4. 中断休眠流程
pm_suspend_abort();
}
return IRQ_HANDLED;
}
16.11 硬件寄存器的保存和恢复
平台寄存器的保存:
c
// arch/arm64/mach-xxx/pm.c
struct platform_suspend_regs {
u32 pmu_ctrl;
u32 pmu_status;
u32 pmu_wakeup_mask;
u32 clk_ctrl;
u32 clk_status;
u32 gpio_ctrl[32];
u32 uart_ctrl;
u32 i2c_ctrl[4];
// ... 更多寄存器
};
static struct platform_suspend_regs saved_regs;
static int platform_suspend_save_regs(void)
{
void __iomem *pmu_base = ioremap(PMU_BASE, SZ_4K);
void __iomem *clk_base = ioremap(CLK_BASE, SZ_4K);
int i;
// 1. 保存 PMU 寄存器
saved_regs.pmu_ctrl = readl(pmu_base + PMU_CTRL);
saved_regs.pmu_status = readl(pmu_base + PMU_STATUS);
saved_regs.pmu_wakeup_mask = readl(pmu_base + PMU_WAKEUP_MASK);
// 2. 保存时钟寄存器
saved_regs.clk_ctrl = readl(clk_base + CLK_CTRL);
saved_regs.clk_status = readl(clk_base + CLK_STATUS);
// 3. 保存 GPIO 寄存器
for (i = 0; i < 32; i++) {
saved_regs.gpio_ctrl[i] = readl(GPIO_BASE + i * 4);
}
// 4. 保存 UART 寄存器
saved_regs.uart_ctrl = readl(UART_BASE + UART_CTRL);
// 5. 保存 I2C 寄存器
for (i = 0; i < 4; i++) {
saved_regs.i2c_ctrl[i] = readl(I2C_BASE(i) + I2C_CTRL);
}
iounmap(pmu_base);
iounmap(clk_base);
return 0;
}
static int platform_resume_restore_regs(void)
{
void __iomem *pmu_base = ioremap(PMU_BASE, SZ_4K);
void __iomem *clk_base = ioremap(CLK_BASE, SZ_4K);
int i;
// 1. 恢复 PMU 寄存器
writel(saved_regs.pmu_ctrl, pmu_base + PMU_CTRL);
writel(saved_regs.pmu_status, pmu_base + PMU_STATUS);
writel(saved_regs.pmu_wakeup_mask, pmu_base + PMU_WAKEUP_MASK);
// 2. 恢复时钟寄存器
writel(saved_regs.clk_ctrl, clk_base + CLK_CTRL);
writel(saved_regs.clk_status, clk_base + CLK_STATUS);
// 3. 恢复 GPIO 寄存器
for (i = 0; i < 32; i++) {
writel(saved_regs.gpio_ctrl[i], GPIO_BASE + i * 4);
}
// 4. 恢复 UART 寄存器
writel(saved_regs.uart_ctrl, UART_BASE + UART_CTRL);
// 5. 恢复 I2C 寄存器
for (i = 0; i < 4; i++) {
writel(saved_regs.i2c_ctrl[i], I2C_BASE(i) + I2C_CTRL);
}
iounmap(pmu_base);
iounmap(clk_base);
return 0;
}
16.12 电源域(Power Domain)的管理
电源域的定义:
电源域是一组共享电源的硬件模块。在休眠时,可以关闭整个电源域以节省功耗。
电源域的 suspend:
c
// drivers/base/power/domain.c
static int genpd_suspend(struct device *dev)
{
struct generic_pm_domain *genpd = dev_to_genpd(dev);
int ret;
// 1. 检查电源域是否可以 suspend
if (genpd->status == GPD_STATE_POWER_OFF)
return 0;
// 2. 调用电源域的 suspend 回调
if (genpd->suspend) {
ret = genpd->suspend(genpd);
if (ret)
return ret;
}
// 3. 关闭电源域
ret = genpd_power_off(genpd);
if (ret) {
// 如果失败,恢复电源域
if (genpd->resume)
genpd->resume(genpd);
return ret;
}
genpd->status = GPD_STATE_POWER_OFF;
return 0;
}
static int genpd_power_off(struct generic_pm_domain *genpd)
{
// 1. 关闭电源域的时钟
clk_disable_unprepare(genpd->clk);
// 2. 关闭电源域的电源
if (genpd->regulator) {
regulator_disable(genpd->regulator);
}
// 3. 等待电源稳定
udelay(genpd->power_off_delay);
return 0;
}
电源域的 resume:
c
// drivers/base/power/domain.c
static int genpd_resume(struct device *dev)
{
struct generic_pm_domain *genpd = dev_to_genpd(dev);
int ret;
// 1. 检查电源域是否已经开启
if (genpd->status != GPD_STATE_POWER_OFF)
return 0;
// 2. 开启电源域
ret = genpd_power_on(genpd);
if (ret)
return ret;
// 3. 调用电源域的 resume 回调
if (genpd->resume) {
ret = genpd->resume(genpd);
if (ret) {
genpd_power_off(genpd);
return ret;
}
}
genpd->status = GPD_STATE_ACTIVE;
return 0;
}
static int genpd_power_on(struct generic_pm_domain *genpd)
{
// 1. 开启电源域的电源
if (genpd->regulator) {
ret = regulator_enable(genpd->regulator);
if (ret)
return ret;
}
// 2. 等待电源稳定
udelay(genpd->power_on_delay);
// 3. 开启电源域的时钟
ret = clk_prepare_enable(genpd->clk);
if (ret) {
if (genpd->regulator)
regulator_disable(genpd->regulator);
return ret;
}
// 4. 等待时钟稳定
udelay(genpd->clock_latency);
return 0;
}
16.13 时钟和复位管理
时钟的 suspend:
c
// drivers/clk/clk.c
static int clk_suspend(void)
{
struct clk_core *clk;
// 1. 遍历所有时钟
clk_prepare_lock();
hlist_for_each_entry(clk, &clk_root_list, child_node) {
// 2. 保存时钟状态
clk->saved_rate = clk->rate;
clk->saved_parent = clk->parent;
clk->saved_flags = clk->flags;
// 3. 如果时钟可以关闭,关闭时钟
if (clk->flags & CLK_IS_CRITICAL)
continue;
if (clk->enable_count > 0) {
clk_core_disable(clk);
}
}
clk_prepare_unlock();
return 0;
}
复位的处理:
c
// drivers/reset/core.c
static int reset_controller_suspend(void)
{
struct reset_controller_dev *rcdev;
// 1. 遍历所有复位控制器
mutex_lock(&reset_list_mutex);
list_for_each_entry(rcdev, &reset_controller_list, list) {
// 2. 保存复位状态
if (rcdev->ops->save_context) {
rcdev->ops->save_context(rcdev);
}
}
mutex_unlock(&reset_list_mutex);
return 0;
}
16.14 内存控制器的处理
内存控制器的 suspend:
c
// drivers/memory/example-memory-controller.c
static int memory_controller_suspend(struct device *dev)
{
struct memory_controller *mc = dev_get_drvdata(dev);
void __iomem *base = mc->base;
u32 val;
// 1. 保存内存控制器寄存器
mc->saved_regs.ctrl = readl(base + MC_CTRL);
mc->saved_regs.timing = readl(base + MC_TIMING);
mc->saved_regs.power = readl(base + MC_POWER);
// 2. 配置内存进入自刷新模式(Self-Refresh)
val = readl(base + MC_CTRL);
val |= MC_CTRL_SELF_REFRESH_ENABLE;
writel(val, base + MC_CTRL);
// 3. 等待进入自刷新模式
while (!(readl(base + MC_STATUS) & MC_STATUS_SELF_REFRESH_ACTIVE)) {
cpu_relax();
}
// 4. 降低内存频率
val = readl(base + MC_POWER);
val |= MC_POWER_LOW_FREQ;
writel(val, base + MC_POWER);
return 0;
}
内存控制器的 resume:
c
// drivers/memory/example-memory-controller.c
static int memory_controller_resume(struct device *dev)
{
struct memory_controller *mc = dev_get_drvdata(dev);
void __iomem *base = mc->base;
u32 val;
// 1. 恢复内存频率
val = readl(base + MC_POWER);
val &= ~MC_POWER_LOW_FREQ;
writel(val, base + MC_POWER);
// 2. 退出自刷新模式
val = readl(base + MC_CTRL);
val &= ~MC_CTRL_SELF_REFRESH_ENABLE;
writel(val, base + MC_CTRL);
// 3. 等待退出自刷新模式
while (readl(base + MC_STATUS) & MC_STATUS_SELF_REFRESH_ACTIVE) {
cpu_relax();
}
// 4. 恢复内存控制器寄存器
writel(mc->saved_regs.ctrl, base + MC_CTRL);
writel(mc->saved_regs.timing, base + MC_TIMING);
writel(mc->saved_regs.power, base + MC_POWER);
// 5. 初始化内存
memory_controller_init(mc);
return 0;
}
16.15 平台特定的低功耗模式
CPU 低功耗模式的实现:
c
// arch/arm64/kernel/suspend.c
static int cpu_do_suspend(unsigned long arg)
{
struct sleep_stack_data *ctx = (struct sleep_stack_data *)arg;
u32 tmp;
// 1. 保存通用寄存器
cpu_suspend_save(&ctx->cpu_context);
// 2. 保存系统寄存器
mrs(tmp, tpidr_el0);
ctx->cpu_context.tpidr_el0 = tmp;
mrs(tmp, tpidrro_el0);
ctx->cpu_context.tpidrro_el0 = tmp;
mrs(tmp, contextidr_el1);
ctx->cpu_context.contextidr_el1 = tmp;
// 3. 保存异常寄存器
mrs(tmp, elr_el1);
ctx->cpu_context.elr_el1 = tmp;
mrs(tmp, spsr_el1);
ctx->cpu_context.spsr_el1 = tmp;
// 4. 保存栈指针
mrs(tmp, sp_el0);
ctx->cpu_context.sp_el0 = tmp;
mrs(tmp, sp_el1);
ctx->cpu_context.sp_el1 = tmp;
// 5. 配置 CPU 进入低功耗模式
// 设置 CPU 进入 WFI 状态
// 配置唤醒源
// 6. 进入 WFI(Wait For Interrupt)
asm volatile("wfi" ::: "memory");
// 7. 如果返回,恢复寄存器
cpu_suspend_restore(&ctx->cpu_context);
return 0;
}
平台电源管理单元的详细操作:
c
// arch/arm64/mach-xxx/pm.c
static int platform_suspend_enter(suspend_state_t state)
{
void __iomem *pmu_base = ioremap(PMU_BASE, SZ_4K);
u32 val;
int ret = 0;
// 1. 配置休眠模式
val = readl(pmu_base + PMU_CTRL);
val &= ~PMU_CTRL_SUSPEND_MODE_MASK;
switch (state) {
case PM_SUSPEND_STANDBY:
val |= PMU_CTRL_SUSPEND_MODE_STANDBY;
break;
case PM_SUSPEND_MEM:
val |= PMU_CTRL_SUSPEND_MODE_MEM;
break;
default:
ret = -EINVAL;
goto out;
}
writel(val, pmu_base + PMU_CTRL);
// 2. 配置唤醒源
val = readl(pmu_base + PMU_WAKEUP_MASK);
val |= (1 << WAKEUP_SOURCE_POWER_BUTTON);
val |= (1 << WAKEUP_SOURCE_RTC);
val |= (1 << WAKEUP_SOURCE_GPIO);
writel(val, pmu_base + PMU_WAKEUP_MASK);
// 3. 配置电压和频率
val = readl(pmu_base + PMU_VOLTAGE_CTRL);
val &= ~PMU_VOLTAGE_CTRL_VOLTAGE_MASK;
val |= PMU_VOLTAGE_CTRL_VOLTAGE_LOW;
writel(val, pmu_base + PMU_VOLTAGE_CTRL);
val = readl(pmu_base + PMU_FREQ_CTRL);
val &= ~PMU_FREQ_CTRL_FREQ_MASK;
val |= PMU_FREQ_CTRL_FREQ_LOW;
writel(val, pmu_base + PMU_FREQ_CTRL);
// 4. 刷新缓存
flush_cache_all();
dsb(sy);
isb();
// 5. 禁用中断(除了唤醒中断)
local_irq_disable();
// 6. 进入休眠
val = readl(pmu_base + PMU_CTRL);
val |= PMU_CTRL_SUSPEND_ENABLE;
writel(val, pmu_base + PMU_CTRL);
// 7. 执行 WFI
asm volatile("wfi" ::: "memory");
// 8. 如果返回,恢复电压和频率
val = readl(pmu_base + PMU_VOLTAGE_CTRL);
val &= ~PMU_VOLTAGE_CTRL_VOLTAGE_MASK;
val |= PMU_VOLTAGE_CTRL_VOLTAGE_NORMAL;
writel(val, pmu_base + PMU_VOLTAGE_CTRL);
val = readl(pmu_base + PMU_FREQ_CTRL);
val &= ~PMU_FREQ_CTRL_FREQ_MASK;
val |= PMU_FREQ_CTRL_FREQ_NORMAL;
writel(val, pmu_base + PMU_FREQ_CTRL);
// 9. 恢复中断
local_irq_enable();
out:
iounmap(pmu_base);
return ret;
}
16.16 唤醒后的系统恢复顺序
系统恢复的详细顺序:
c
// kernel/power/suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
// ... suspend 流程 ...
// 从休眠状态返回后,按以下顺序恢复:
// 1. 平台特定的恢复(noirq 阶段,中断仍禁用)
platform_resume_noirq(state);
// 2. 设备恢复(noirq 阶段)
dpm_resume_noirq(*wakeup ? PMSG_RESUME : PMSG_RESTORE);
// 3. 平台特定的恢复(early 阶段)
platform_resume_early(state);
// 4. 设备恢复(early 阶段)
dpm_resume_early(*wakeup ? PMSG_RESUME : PMSG_RESTORE);
// 5. 平台特定的恢复(finish 阶段)
platform_resume_finish(state);
// 6. 恢复系统核心服务
syscore_resume();
// 7. 恢复中断
arch_suspend_enable_irqs();
// 8. 启用从 CPU
suspend_enable_secondary_cpus();
// 9. 恢复控制台
resume_console();
// 10. 解冻进程
pm_suspend_thaw();
return 0;
}
平台 resume_noirq 的实现:
c
// arch/arm64/mach-xxx/pm.c
static void platform_resume_noirq(suspend_state_t state)
{
void __iomem *pmu_base = ioremap(PMU_BASE, SZ_4K);
u32 val;
// 1. 退出低功耗模式
val = readl(pmu_base + PMU_CTRL);
val &= ~PMU_CTRL_SUSPEND_ENABLE;
writel(val, pmu_base + PMU_CTRL);
// 2. 恢复基本时钟
clk_prepare_enable(cpu_clk);
clk_prepare_enable(system_clk);
// 3. 恢复基本电源
regulator_enable(core_regulator);
// 4. 恢复中断控制器
gic_resume();
iounmap(pmu_base);
}
16.17 调试和跟踪机制
休眠唤醒的跟踪点:
c
// include/trace/events/power.h
TRACE_EVENT(suspend_resume,
TP_PROTO(const char *action, int val, bool start),
TP_ARGS(action, val, start),
TP_STRUCT__entry(
__field(const char *, action)
__field(int, val)
__field(bool, start)
),
TP_fast_assign(
__entry->action = action;
__entry->val = val;
__entry->start = start;
),
TP_printk("%s[%d] %s", __entry->action, __entry->val,
__entry->start ? "begin" : "end")
);
使用 ftrace 跟踪休眠流程:
bash
# 1. 启用 PM 跟踪
echo 1 > /sys/kernel/debug/tracing/events/power/enable
# 2. 启用函数跟踪
echo function > /sys/kernel/debug/tracing/current_tracer
echo pm_suspend > /sys/kernel/debug/tracing/set_ftrace_filter
echo suspend_enter >> /sys/kernel/debug/tracing/set_ftrace_filter
# 3. 开始跟踪
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 4. 触发休眠
echo mem > /sys/power/state
# 5. 查看跟踪结果
cat /sys/kernel/debug/tracing/trace
16.18 错误恢复机制
休眠失败时的恢复:
c
// kernel/power/suspend.c
static int enter_state(suspend_state_t state)
{
int error;
// ... 休眠流程 ...
// 如果任何阶段失败,执行恢复
if (error) {
// 1. 恢复设备
dpm_resume_noirq(PMSG_RESTORE);
dpm_resume_early(PMSG_RESTORE);
dpm_resume(PMSG_RESTORE);
// 2. 解冻进程
pm_suspend_thaw();
// 3. 恢复控制台
resume_console();
// 4. 恢复平台
if (suspend_ops && suspend_ops->recover)
suspend_ops->recover();
}
return error;
}
设备 suspend 失败的处理:
c
// drivers/base/power/main.c
static int device_suspend(struct device *dev, pm_message_t state)
{
int error = 0;
// 1. 调用设备的 suspend 回调
error = dpm_run_callback(callback, dev, state, info);
// 2. 如果失败,记录错误并恢复
if (error) {
pm_dev_err(dev, state, info, error);
dpm_save_failed_dev(dev_name(dev));
// 3. 恢复已 suspend 的设备
device_resume(dev, state, true);
}
return error;
}