Linux 休眠唤醒机制详解(中)

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 性能优化建议

减少休眠时间

  1. 优化进程冻结

    • 减少需要冻结的进程数量
    • 优化进程冻结超时时间
  2. 优化设备 suspend

    • 减少设备数量
    • 优化设备 suspend 回调
    • 并行执行设备 suspend
  3. 优化平台 suspend

    • 优化平台特定的 suspend 实现
    • 减少不必要的操作

减少唤醒时间

  1. 优化设备 resume

    • 优化设备 resume 回调
    • 并行执行设备 resume
  2. 优化进程解冻

    • 减少需要解冻的进程数量
    • 优化进程解冻顺序

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:系统无法进入休眠

排查步骤

  1. 检查是否有活动的唤醒源:
bash 复制代码
cat /sys/kernel/debug/wakeup_sources | grep active
  1. 检查设备是否阻止休眠:
bash 复制代码
dmesg | grep -i "device.*prevent.*suspend"
  1. 检查进程是否无法冻结:
bash 复制代码
dmesg | grep -i "freeze.*failed"

问题 2:系统无法唤醒

排查步骤

  1. 检查唤醒源是否启用:
bash 复制代码
cat /sys/devices/.../power/wakeup
  1. 检查硬件是否支持唤醒:
bash 复制代码
cat /proc/acpi/wakeup
  1. 检查中断是否正常:
bash 复制代码
cat /proc/interrupts

问题 3:唤醒后系统异常

排查步骤

  1. 检查设备 resume 是否失败:
bash 复制代码
dmesg | grep -i "device.*resume.*failed"
  1. 检查设备状态是否正确恢复:
bash 复制代码
# 查看设备状态
cat /sys/devices/.../power/state
  1. 检查进程是否正常解冻:
bash 复制代码
dmesg | grep -i "thaw.*failed"

13. 总结

13.1 关键点总结

  1. 休眠状态

    • Linux 支持多种休眠状态(mem、disk、freeze 等)
    • 每种状态有不同的功耗和恢复时间
  2. 休眠流程

    • 冻结进程 → 设备 suspend → 保存状态 → 进入休眠
    • 各阶段都有详细的实现和回调
  3. 唤醒流程

    • 唤醒事件 → 恢复状态 → 设备 resume → 解冻进程
    • 需要正确处理各种唤醒源
  4. 设备支持

    • 设备驱动需要实现完整的 PM 回调
    • 需要正确管理唤醒源

13.2 最佳实践

  1. 设备驱动开发

    • 正确实现所有 PM 回调函数
    • 正确保存和恢复设备状态
    • 正确管理唤醒源
  2. 性能优化

    • 减少休眠和唤醒时间
    • 优化设备 suspend/resume 流程
    • 优化进程冻结和解冻
  3. 调试和排查

    • 使用调试工具查看日志
    • 检查唤醒源状态
    • 检查设备 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 系统的休眠流程:

  1. 用户按下电源键或屏幕超时
  2. Android Framework 调用 PowerManager.goToSleep()
  3. 通过 sysfs 触发休眠:echo mem > /sys/power/state
  4. Linux 内核执行休眠流程
  5. 系统进入 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 缓存一致性和内存屏障

缓存一致性的问题

在休眠和唤醒过程中,数据可能存在于多个位置:

  1. CPU 缓存(L1、L2、L3)
  2. 内存(RAM)
  3. 设备缓冲区(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;
}
相关推荐
Shawn_CH2 小时前
Linux 休眠唤醒机制详解(上)
嵌入式
charlie1145141917 小时前
在上位机上熟悉FreeRTOS API
笔记·学习·嵌入式·c·freertos·工程
Shawn_CH8 小时前
Linux 零拷贝技术详解
嵌入式
Shawn_CH8 小时前
Linux ROS与进程间通信详解
嵌入式
华清远见成都中心20 小时前
成都理工大学&华清远见成都中心实训,助力电商人才培养
大数据·人工智能·嵌入式
切糕师学AI20 小时前
ARM 架构中的 CONTROL 寄存器
arm开发·硬件架构·嵌入式·芯片·寄存器
fzm52981 天前
C语言单元测试在嵌入式软件开发中的作用及专业工具的应用
自动化测试·单元测试·汽车·嵌入式·白盒测试
大聪明-PLUS1 天前
Linux 系统中的电池衰减
linux·嵌入式·arm·smarc
Shawn_CH1 天前
Linux 共享内存详解
嵌入式