Skip to main content
 首页 » 操作系统

Linux kernel syscore 学习笔记

2022年07月19日29lonelyxmas

一、syscore简介

1. syscore 作为低功耗休眠唤醒流程的一部分,其涉及的文件主要有 syscore_ops.h 和 syscore.c,这一级别的回调函数是在完全屏蔽中断的场景下进行的。

2. 相关结构

//syscore_ops.h 
struct syscore_ops { 
    struct list_head node; 
    int (*suspend)(void); //syscore_suspend()中遍历调用此回调 
    void (*resume)(void); //syscore_resume()中遍历调用此回调 
    void (*shutdown)(void); //syscore_shutdown()中遍历调用此回调 
};

使用时可以提供一个或多个回调函数的实现。

3. 相关函数

//syscore.c 
void register_syscore_ops(struct syscore_ops *ops); 
void unregister_syscore_ops(struct syscore_ops *ops);

二、使用方法

1. 使用时定义一个 struct syscore_ops 结构变量,然后实现感兴趣的回调函数。之后调用 register_syscore_ops() 进行注册,当移除驱动时,调用 unregister_syscore_ops() 去掉注册。

2. register_syscore_ops()/unregister_syscore_ops() 的实现

//syscore.c 
void register_syscore_ops(struct syscore_ops *ops) 
{ 
    mutex_lock(&syscore_ops_lock); 
    list_add_tail(&ops->node, &syscore_ops_list); //注意是尾插法 
    mutex_unlock(&syscore_ops_lock); 
} 
EXPORT_SYMBOL_GPL(register_syscore_ops); 
 
void unregister_syscore_ops(struct syscore_ops *ops) 
{ 
    mutex_lock(&syscore_ops_lock); 
    list_del(&ops->node); 
    mutex_unlock(&syscore_ops_lock); 
} 
EXPORT_SYMBOL_GPL(unregister_syscore_ops);

三、调用时机

1. suspend 回调位置

int syscore_suspend(void) 
{ 
    struct syscore_ops *ops; 
    int ret = 0; 
 
    trace_suspend_resume(TPS("syscore_suspend"), 0, true); 
    pm_pr_dbg("Checking wakeup interrupts\n"); 
 
    if (pm_wakeup_pending()) 
        return -EBUSY; 
 
    WARN_ONCE(!irqs_disabled(), 
        "Interrupts enabled before system core suspend.\n"); 
 
    //注意反向遍历,也就是最后注册的suspend回调函数最先被调用 
    list_for_each_entry_reverse(ops, &syscore_ops_list, node) 
        if (ops->suspend) { 
            pm_pr_dbg("Calling %pS\n", ops->suspend); 
            ret = ops->suspend(); 
            if (ret) 
                goto err_out; 
            WARN_ONCE(!irqs_disabled(), 
                "Interrupts enabled after %pS\n", ops->suspend); 
        } 
 
    trace_suspend_resume(TPS("syscore_suspend"), 0, false); 
 
    return 0; 
 
    //只要有任何一个suspend回调失败了,就完整地回调一遍resume回调,注意不是从断点位置回调的! 
 err_out: 
    log_suspend_abort_reason("System core suspend callback %pS failed", ops->suspend); 
    pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); 
 
    list_for_each_entry_continue(ops, &syscore_ops_list, node) 
        if (ops->resume) 
            ops->resume(); 
 
    return ret; 
} 
EXPORT_SYMBOL_GPL(syscore_suspend);

2. resume 回调位置

//syscore.c 
void syscore_resume(void) 
{ 
    struct syscore_ops *ops; 
 
    trace_suspend_resume(TPS("syscore_resume"), 0, true); 
    //说明是关着中断回调resume回调的 
    WARN_ONCE(!irqs_disabled(), "Interrupts enabled before system core resume.\n"); 
 
    //注意,这个是正向遍历的,先注册的resume回调先被调用 
    list_for_each_entry(ops, &syscore_ops_list, node) 
        if (ops->resume) { 
            pm_pr_dbg("Calling %pS\n", ops->resume); 
            ops->resume(); 
            WARN_ONCE(!irqs_disabled(), "Interrupts enabled after %pS\n", ops->resume); 
        } 
    trace_suspend_resume(TPS("syscore_resume"), 0, false); 
} 
EXPORT_SYMBOL_GPL(syscore_resume);

3. shutdown 回调位置

void syscore_shutdown(void) 
{ 
    struct syscore_ops *ops; 
 
    mutex_lock(&syscore_ops_lock); 
 
    //注意,这个也是反向调用的,最后注册的最先被调用 
    list_for_each_entry_reverse(ops, &syscore_ops_list, node) 
        if (ops->shutdown) { 
            if (initcall_debug) 
                pr_info("PM: Calling %pS\n", ops->shutdown); 
            ops->shutdown(); 
        } 
 
    mutex_unlock(&syscore_ops_lock); 
}

4. 整机调用时机

echo mem > /sys/power/state 
    state_store 
        pm_suspend 
            enter_state 
                suspend_devices_and_enter 
                    suspend_enter 
                        platform_suspend_prepare 
                        dpm_suspend_late 
                        platform_suspend_prepare_late 
                        dpm_suspend_noirq 
                        platform_suspend_prepare_noirq 
                        suspend_disable_secondary_cpus //关非boot cpu 
                        arch_suspend_disable_irqs //关中断 
                        syscore_suspend //syscore .suspend 回调 
                        suspend_ops->enter //完全休眠下去,之后唤醒了从这里开始执行------------------- 
                        syscore_resume //syscore .resume 回调 
                        arch_suspend_enable_irqs //开中断 
                        suspend_enable_secondary_cpus //开非boot cpu 
                        platform_resume_noirq 
                        dpm_resume_noirq 
                        platform_resume_early 
                        dpm_resume_early

4. 调用时机汇总

这些syscore ops的回调是关中断,关非boot cpu的情况下调用的。suspend/shutdown回调注册的越早越靠后调用,resume回调注册的越早越靠前调用。syscore的suspend回调是在所有驱动的suspend回调之后被调用,syscore的resume回调在所有驱动的resume回调之前被调用。

四、依赖关系处理

在probe()驱动的时候有考虑依赖关系,若发现自己不适合被probe,probe()函数就返回 -EPROBE_DEFER 来延迟probe()。

参考:
https://blog.csdn.net/adaptiver/article/details/52013245


本文参考链接:https://www.cnblogs.com/hellokitty2/p/16225836.html