diff --git a/NOHZ_Full_Technical_Guide.md b/NOHZ_Full_Technical_Guide.md new file mode 100644 index 00000000000000..f1092b0560d2b0 --- /dev/null +++ b/NOHZ_Full_Technical_Guide.md @@ -0,0 +1,1061 @@ +# NOHZ_FULL 技术详解与应用指南 + +> 完全动态时钟 (Full Dynticks / Adaptive Ticks) 技术深度解析 +> 基于 Linux 内核源码分析 + +--- + +## 目录 + +1. [概述](#1-概述) +2. [Linux Tick 机制演进](#2-linux-tick-机制演进) +3. [NOHZ_FULL 工作原理](#3-nohz_full-工作原理) +4. [内核配置与启动参数](#4-内核配置与启动参数) +5. [Housekeeping CPU 机制](#5-housekeeping-cpu-机制) +6. [Tick 依赖管理](#6-tick-依赖管理) +7. [RCU 回调卸载](#7-rcu-回调卸载) +8. [OS Jitter 来源与消除](#8-os-jitter-来源与消除) +9. [应用场景](#9-应用场景) +10. [最佳实践配置](#10-最佳实践配置) +11. [调试与验证](#11-调试与验证) +12. [已知限制与注意事项](#12-已知限制与注意事项) + +--- + +## 1. 概述 + +### 1.1 什么是 NOHZ_FULL? + +**NOHZ_FULL**(也称为 Full Dynticks 或 Adaptive Ticks)是 Linux 内核的一项特性,允许 CPU 在只有一个可运行任务时完全停止调度时钟中断(tick)。 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 传统 Tick vs NOHZ_FULL │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 传统周期性 Tick (HZ=1000,每毫秒一次中断): │ +│ │ +│ ──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──► 时间 │ +│ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ tick中断 (每1ms一次,无论CPU是否繁忙) │ +│ │ +│ NOHZ_IDLE (空闲时停止 tick): │ +│ │ +│ ──┬──┬──┬────────────────────┬──┬──┬──┬───────────► 时间 │ +│ ↑ ↑ ↑ ↑ ↑ ↑ ↑ │ +│ │ │ │ (空闲,无tick) │ │ │ │ (空闲) │ +│ busy idle busy idle │ +│ │ +│ NOHZ_FULL (单任务运行时也停止 tick): │ +│ │ +│ ──┬─────────────────────────────────────────────────► 时间 │ +│ ↑ │ +│ │ (只有1个任务运行,停止 tick,几乎零内核干扰) │ +│ 进入用户态 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 1.2 为什么需要 NOHZ_FULL? + +| 问题 | 影响 | NOHZ_FULL 解决方案 | +|------|------|-------------------| +| **Tick 中断抖动** | 每次 tick 中断导致 1-10μs 延迟 | 停止不必要的 tick | +| **OS Jitter** | 影响实时应用的确定性 | 最小化内核干扰 | +| **HPC 迭代同步** | 一个 CPU 延迟导致所有 CPU 等待 | 消除周期性中断 | +| **功耗** | 持续中断消耗电量 | 仅在需要时唤醒 | + +### 1.3 核心术语 + +| 术语 | 英文 | 说明 | +|------|------|------| +| **Tick** | Scheduling-clock tick | 调度时钟中断,通常 1000Hz | +| **NOHZ** | No HZ / Dyntick | 动态时钟,按需产生 tick | +| **NOHZ_IDLE** | Dyntick-idle | 空闲时停止 tick | +| **NOHZ_FULL** | Full Dyntick | 单任务运行时也停止 tick | +| **Adaptive Ticks** | - | NOHZ_FULL 的另一种叫法 | +| **Housekeeping CPU** | - | 负责管家任务的 CPU | +| **Isolated CPU** | - | 被隔离的 CPU (运行关键任务) | +| **OS Jitter** | - | 操作系统引起的抖动/延迟 | + +--- + +## 2. Linux Tick 机制演进 + +### 2.1 三代 Tick 机制 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Linux Tick 机制演进 │ +├───────────────┬─────────────────┬─────────────────┬─────────────────────┤ +│ 版本 │ CONFIG 选项 │ 行为 │ 适用场景 │ +├───────────────┼─────────────────┼─────────────────┼─────────────────────┤ +│ 传统周期性 │ HZ_PERIODIC │ 始终产生 tick │ 重负载、短空闲周期 │ +│ (Legacy) │ │ │ │ +├───────────────┼─────────────────┼─────────────────┼─────────────────────┤ +│ 空闲动态时钟 │ NO_HZ_IDLE │ 空闲时停止 tick │ 大多数场景 (默认) │ +│ (2.6.21+) │ │ │ │ +├───────────────┼─────────────────┼─────────────────┼─────────────────────┤ +│ 完全动态时钟 │ NO_HZ_FULL │ 单任务时停止 │ 实时/HPC 应用 │ +│ (3.10+) │ │ tick │ │ +└───────────────┴─────────────────┴─────────────────┴─────────────────────┘ +``` + +### 2.2 Tick 的作用 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 调度时钟 Tick 的主要功能 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. 时间维护 (Timekeeping) │ +│ - 更新 jiffies 计数器 │ +│ - 维护系统时间 (wall clock) │ +│ - 更新 load average │ +│ │ +│ 2. 调度器 (Scheduler) │ +│ - 更新任务运行时间统计 │ +│ - 检查时间片是否到期 │ +│ - 触发负载均衡 │ +│ - 更新 PELT 负载追踪 │ +│ │ +│ 3. 定时器 (Timers) │ +│ - 检查并处理到期的定时器 │ +│ - hrtimer 高精度定时器处理 │ +│ │ +│ 4. RCU (Read-Copy-Update) │ +│ - 检测静止状态 (quiescent state) │ +│ - 处理 RCU 回调 │ +│ │ +│ 5. 其他 │ +│ - POSIX CPU 定时器 │ +│ - perf 事件轮换 │ +│ - 虚拟内存统计 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 3. NOHZ_FULL 工作原理 + +### 3.1 核心条件 + +NOHZ_FULL CPU 停止 tick 的条件: + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 进入 Tickless 模式的条件 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ can_stop_full_tick() 检查 │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌────────────────────┼────────────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ +│ │ 只有1个 │ AND │ 无 tick │ AND │ 无 pending│ │ +│ │ 可运行任务│ │ 依赖 │ │ 回调/定时 │ │ +│ └───────────┘ └───────────┘ └───────────┘ │ +│ │ │ │ │ +│ │ │ │ │ +│ └────────────────────┼────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ 停止 tick │ │ +│ │ 进入 tickless │ │ +│ └─────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 3.2 状态转换 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ NOHZ_FULL 状态转换图 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌───────────────────┐ │ +│ │ 有 Tick 状态 │ │ +│ │ (Tick Running) │ │ +│ └─────────┬─────────┘ │ +│ │ │ +│ ┌───────────────────────┼───────────────────────┐ │ +│ │ │ │ │ +│ ▼ │ ▼ │ +│ ┌───────────────┐ │ ┌───────────────┐ │ +│ │ 用户态运行 │ │ │ Idle 状态 │ │ +│ │ (单任务) │◄──────────────┴──────────────►│ │ │ +│ │ │ │ │ │ +│ └───────┬───────┘ └───────┬───────┘ │ +│ │ │ │ +│ │ 满足条件 满足条件 │ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌───────────────┐ ┌───────────────┐ │ +│ │ Tickless 运行 │ │ Dyntick-idle │ │ +│ │ (Full Dyntick)│ │ (No tick) │ │ +│ └───────┬───────┘ └───────────────┘ │ +│ │ │ +│ │ 需要 tick 的事件发生 │ +│ │ (如: 新任务唤醒、定时器到期、RCU 回调) │ +│ │ │ +│ ▼ │ +│ ┌───────────────┐ │ +│ │ 恢复 Tick │ │ +│ │ (tick_nohz_ │ │ +│ │ full_kick) │ │ +│ └───────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 3.3 关键代码路径 + +```c +/* kernel/time/tick-sched.c */ + +/* 检查是否可以停止 tick */ +static bool can_stop_full_tick(int cpu, struct tick_sched *ts) +{ + /* 必须只有一个可运行任务 */ + if (!sched_can_stop_tick(cpu)) + return false; + + /* 检查 tick 依赖 */ + if (check_tick_dependency(&ts->tick_dep_mask)) + return false; + + /* 检查 POSIX CPU 定时器 */ + if (posix_cpu_timers_can_stop_tick(current)) + return false; + + /* ... 其他检查 ... */ + + return true; +} + +/* 停止 tick */ +static void tick_nohz_full_stop_tick(struct tick_sched *ts, int cpu) +{ + if (tick_nohz_next_event(ts, cpu)) + tick_nohz_stop_tick(ts, cpu); /* 重编程下一次唤醒时间 */ + else + tick_nohz_retain_tick(ts); /* 保持 tick */ +} + +/* 恢复 tick (被依赖触发) */ +void tick_nohz_full_kick(void) +{ + if (!tick_nohz_full_cpu(smp_processor_id())) + return; + irq_work_queue(this_cpu_ptr(&nohz_full_kick_work)); +} +``` + +--- + +## 4. 内核配置与启动参数 + +### 4.1 内核配置选项 + +```kconfig +# 必须启用 +CONFIG_NO_HZ_FULL=y # 启用完全动态时钟 +CONFIG_RCU_NOCB_CPU=y # 启用 RCU 回调卸载 +CONFIG_CPU_ISOLATION=y # 启用 CPU 隔离 + +# 推荐启用 +CONFIG_NO_HZ_FULL_ALL=n # 不要将所有 CPU 设为 nohz_full +CONFIG_CPUSETS=y # 用于 CPU 亲和性控制 + +# 可选优化 +CONFIG_PREEMPT_NONE=y # 非抢占内核 (某些场景) +CONFIG_IRQ_TIME_ACCOUNTING=y # 精确 IRQ 时间统计 +``` + +### 4.2 启动参数 + +```bash +# 基本配置 +nohz_full=1-7 # CPU 1-7 为 adaptive-tick CPUs + # CPU 0 为 housekeeping CPU (必须保留至少一个) + +# RCU 回调卸载 +rcu_nocbs=1-7 # 卸载 CPU 1-7 的 RCU 回调到专用线程 + # nohz_full= 会自动隐含此设置 + +# CPU 隔离 (与 nohz_full 配合使用) +isolcpus=nohz,domain,managed_irq,1-7 +# - nohz: 等同于 nohz_full= +# - domain: 从调度域中移除这些 CPU +# - managed_irq: 不允许 managed IRQ 路由到这些 CPU + +# 示例: 8 CPU 系统完整配置 +nohz_full=1-7 rcu_nocbs=1-7 isolcpus=domain,managed_irq,1-7 \ +irqaffinity=0 nohz=on +``` + +### 4.3 参数关系图 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 启动参数关系与功能 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ nohz_full=1-7 │ +│ │ │ +│ ├──► 停止 tick (当 CPU 只有1个任务时) │ +│ │ │ +│ ├──► 自动启用 rcu_nocbs=1-7 │ +│ │ │ │ +│ │ └──► RCU 回调由 rcuo* kthread 处理 │ +│ │ │ +│ └──► 设置 HK_TYPE_KERNEL_NOISE │ +│ │ │ +│ ├──► HK_TYPE_TICK: 定时器/workqueue 绑定到 housekeeping │ +│ ├──► HK_TYPE_TIMER: 同上 │ +│ ├──► HK_TYPE_RCU: RCU 处理卸载 │ +│ ├──► HK_TYPE_WQ: 非绑定 workqueue 避开 │ +│ └──► HK_TYPE_KTHREAD: kthread 创建避开 │ +│ │ +│ isolcpus=domain,1-7 │ +│ │ │ +│ └──► 从调度器负载均衡域中移除 │ +│ (任务不会被自动迁移到这些 CPU) │ +│ │ +│ isolcpus=managed_irq,1-7 │ +│ │ │ +│ └──► 阻止 managed IRQ 路由到这些 CPU │ +│ │ +│ irqaffinity=0 │ +│ │ │ +│ └──► 将默认 IRQ 亲和性设为 CPU 0 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 5. Housekeeping CPU 机制 + +### 5.1 Housekeeping 概念 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Housekeeping vs Isolated CPUs │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 8 CPU 系统示例 │ +│ │ +│ ┌─────────┐ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │ +│ │ CPU 0 │ │ CPU 1 │ CPU 2 │ CPU 3 │ CPU 4 │ ... │ │ +│ ├─────────┤ ├─────────┴─────────┴─────────┴─────────┴─────────┤ │ +│ │Housekee-│ │ Isolated / nohz_full CPUs │ │ +│ │ping CPU │ │ │ │ +│ ├─────────┤ ├──────────────────────────────────────────────────┤ │ +│ │ │ │ │ │ +│ │ - 时间 │ │ - 运行关键应用任务 │ │ +│ │ 维护 │ │ - 最小化内核干扰 │ │ +│ │ - RCU │ │ - 停止调度时钟 tick │ │ +│ │ 处理 │ │ - 无 RCU 回调处理 │ │ +│ │ - 中断 │ │ - 无非绑定 workqueue │ │ +│ │ 处理 │ │ - 无定时器迁移 │ │ +│ │ - WQ │ │ │ │ +│ │ - 定时器│ │ │ │ +│ │ │ │ │ │ +│ └─────────┘ └──────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 5.2 Housekeeping 类型 + +```c +/* include/linux/sched/isolation.h */ +enum hk_type { + HK_TYPE_DOMAIN, /* 调度域隔离 */ + HK_TYPE_MANAGED_IRQ, /* Managed IRQ 隔离 */ + HK_TYPE_KERNEL_NOISE, /* 内核噪声隔离 (nohz_full 设置) */ + HK_TYPE_MAX, + + /* 以下类型共享 HK_TYPE_KERNEL_NOISE 的值 */ + HK_TYPE_TICK = HK_TYPE_KERNEL_NOISE, /* Tick 相关 */ + HK_TYPE_TIMER = HK_TYPE_KERNEL_NOISE, /* 定时器相关 */ + HK_TYPE_RCU = HK_TYPE_KERNEL_NOISE, /* RCU 相关 */ + HK_TYPE_MISC = HK_TYPE_KERNEL_NOISE, /* 其他杂项 */ + HK_TYPE_WQ = HK_TYPE_KERNEL_NOISE, /* Workqueue 相关 */ + HK_TYPE_KTHREAD = HK_TYPE_KERNEL_NOISE /* Kthread 相关 */ +}; +``` + +### 5.3 Housekeeping API + +```c +/* kernel/sched/isolation.c */ + +/* 检查 CPU 是否为 housekeeping CPU */ +bool housekeeping_test_cpu(int cpu, enum hk_type type); + +/* 获取 housekeeping CPU 掩码 */ +const struct cpumask *housekeeping_cpumask(enum hk_type type); + +/* 获取任意一个 housekeeping CPU */ +int housekeeping_any_cpu(enum hk_type type); + +/* 将任务亲和性设置为 housekeeping CPUs */ +void housekeeping_affine(struct task_struct *t, enum hk_type type); +``` + +--- + +## 6. Tick 依赖管理 + +### 6.1 Tick 依赖类型 + +某些内核子系统需要 tick 才能正常工作,它们可以声明 tick 依赖: + +```c +/* include/linux/tick.h */ +enum tick_dep_bits { + TICK_DEP_BIT_POSIX_TIMER = 0, /* POSIX CPU 定时器 */ + TICK_DEP_BIT_PERF_EVENTS = 1, /* perf 事件 */ + TICK_DEP_BIT_SCHED = 2, /* 调度器 */ + TICK_DEP_BIT_CLOCK_UNSTABLE = 3, /* 时钟不稳定 */ + TICK_DEP_BIT_RCU = 4, /* RCU 需要检测静止状态 */ + TICK_DEP_BIT_RCU_EXP = 5, /* RCU 加速宽限期 */ +}; +``` + +### 6.2 依赖触发示例 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Tick 依赖触发场景 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 依赖类型 触发条件 影响 │ +│ ───────────────────────────────────────────────────────────────── │ +│ POSIX_TIMER 进程设置了 CPU 定时器 恢复 tick │ +│ │ +│ PERF_EVENTS perf 事件需要轮换 恢复 tick │ +│ (硬件 PMU 计数器不足) │ +│ │ +│ SCHED 多个可运行任务 恢复 tick │ +│ (需要时间片调度) │ +│ │ +│ RCU 有待处理的 RCU 回调 恢复 tick │ +│ (未卸载时) │ +│ │ +│ RCU_EXP RCU 加速宽限期进行中 恢复 tick │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 6.3 依赖管理 API + +```c +/* 设置全局 tick 依赖 */ +void tick_nohz_dep_set(enum tick_dep_bits bit); +void tick_nohz_dep_clear(enum tick_dep_bits bit); + +/* 设置特定 CPU 的 tick 依赖 */ +void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit); +void tick_nohz_dep_clear_cpu(int cpu, enum tick_dep_bits bit); + +/* 设置任务的 tick 依赖 */ +void tick_nohz_dep_set_task(struct task_struct *tsk, enum tick_dep_bits bit); +void tick_nohz_dep_clear_task(struct task_struct *tsk, enum tick_dep_bits bit); + +/* 设置信号组 (进程) 的 tick 依赖 */ +void tick_nohz_dep_set_signal(struct task_struct *tsk, enum tick_dep_bits bit); +void tick_nohz_dep_clear_signal(struct task_struct *tsk, enum tick_dep_bits bit); +``` + +--- + +## 7. RCU 回调卸载 + +### 7.1 为什么需要 RCU 回调卸载 + +RCU (Read-Copy-Update) 是内核中广泛使用的同步机制。当使用 `call_rcu()` 注册回调时,这些回调需要在安全时机执行。传统上这需要 tick 中断来触发。 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ RCU 回调处理模式 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 传统模式 (无 RCU_NOCB_CPU): │ +│ ───────────────────────────── │ +│ │ +│ CPU N: call_rcu() ──► 添加回调到本地队列 ──► RCU_SOFTIRQ 处理 │ +│ ↑ │ +│ │ │ +│ 需要 tick │ +│ │ +│ NOCB 模式 (RCU_NOCB_CPU=y + rcu_nocbs=N): │ +│ ────────────────────────────────────────── │ +│ │ +│ CPU N: call_rcu() ──► 添加回调到队列 ──┐ │ +│ │ │ +│ ▼ │ +│ rcuo* kthread │ +│ (可运行在其他 CPU) │ +│ │ │ +│ ▼ │ +│ 处理回调 │ +│ (无需 tick) │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 7.2 NOCB Kthreads + +```bash +# 查看 RCU 卸载线程 +$ ps aux | grep rcuo +root 14 0.0 0.0 0 0 ? S 10:00 0:00 [rcuog/0] +root 15 0.0 0.0 0 0 ? S 10:00 0:00 [rcuop/0] +root 16 0.0 0.0 0 0 ? S 10:00 0:00 [rcuos/0] + +# rcuog: RCU grace-period kthread +# rcuop: RCU offload kthread (preemptible RCU) +# rcuos: RCU offload kthread (SRCU) +``` + +### 7.3 绑定 RCU 线程到 Housekeeping CPU + +```bash +# 将所有 rcuo* 线程绑定到 CPU 0 +for pid in $(pgrep -f "rcuo"); do + taskset -p 0x1 $pid +done + +# 或使用 cgroups +mkdir -p /sys/fs/cgroup/cpuset/housekeeping +echo 0 > /sys/fs/cgroup/cpuset/housekeeping/cpuset.cpus +echo 0 > /sys/fs/cgroup/cpuset/housekeeping/cpuset.mems +for pid in $(pgrep -f "rcuo"); do + echo $pid > /sys/fs/cgroup/cpuset/housekeeping/cgroup.procs +done +``` + +--- + +## 8. OS Jitter 来源与消除 + +### 8.1 OS Jitter 主要来源 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ OS Jitter 来源分类 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ 硬件中断 │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ - 定时器中断 (tick) → nohz_full 消除 │ │ +│ │ - 外设中断 (NIC, 磁盘等) → irqaffinity 绑定 │ │ +│ │ - IPI (处理器间中断) → 避免跨 CPU 操作 │ │ +│ │ - NMI → 难以完全消除 │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ 软中断/Tasklet │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ - TIMER_SOFTIRQ → nohz_full │ │ +│ │ - NET_TX/RX_SOFTIRQ → 中断亲和性 │ │ +│ │ - SCHED_SOFTIRQ → isolcpus=domain │ │ +│ │ - RCU_SOFTIRQ → rcu_nocbs │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ 内核线程 │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ - ksoftirqd → 保持 CPU 在用户态 │ │ +│ │ - kworker → WQ_SYSFS + 亲和性 │ │ +│ │ - rcuop/rcuos → 绑定到 housekeeping │ │ +│ │ - migration → isolcpus=domain │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ 其他来源 │ │ +│ ├────────────────────────────────────────────────────────────────┤ │ +│ │ - 页错误 → mlock + 预分配 │ │ +│ │ - TLB shootdown → 避免模块卸载等 │ │ +│ │ - 系统调用 → 最小化内核进入 │ │ +│ │ - 调度器负载均衡 → isolcpus=domain │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 8.2 per-CPU Kthreads 处理 + +主要的 per-CPU kthreads 及其处理方式: + +| Kthread | 功能 | 消除方法 | +|---------|------|----------| +| `ksoftirqd/%u` | 软中断处理 | 保持 CPU 在用户态 | +| `kworker/%u` | Workqueue 执行 | 绑定非绑定 WQ 到 housekeeping | +| `rcuop/%u` | RCU 回调卸载 | 绑定到 housekeeping CPU | +| `migration/%u` | 任务迁移 | `isolcpus=domain` | +| `irq/%d-%s` | 线程化中断 | IRQ 亲和性设置 | + +--- + +## 9. 应用场景 + +### 9.1 实时应用 (Real-Time) + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 实时应用场景 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 场景描述: │ +│ - 工业控制系统、机器人控制、音视频处理 │ +│ - 需要确定性响应时间 (通常 < 100μs) │ +│ - 任何意外延迟都可能导致系统故障 │ +│ │ +│ NOHZ_FULL 收益: │ +│ - 消除 tick 中断带来的 1-10μs 延迟 │ +│ - 最坏情况延迟可预测 │ +│ - 减少中断处理开销 │ +│ │ +│ 典型配置: │ +│ ┌───────────────────────────────────────────────────────────────┐ │ +│ │ # 8 CPU 系统 │ │ +│ │ nohz_full=1-7 │ │ +│ │ rcu_nocbs=1-7 │ │ +│ │ isolcpus=domain,managed_irq,1-7 │ │ +│ │ irqaffinity=0 │ │ +│ │ nowatchdog # 禁用 watchdog │ │ +│ │ nosoftlockup # 禁用软锁检测 │ │ +│ │ nmi_watchdog=0 # 禁用 NMI watchdog │ │ +│ └───────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 9.2 高性能计算 (HPC) + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ HPC 应用场景 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 场景描述: │ +│ - 科学计算、气象模拟、基因分析 │ +│ - 多 CPU 并行计算,需要同步屏障 │ +│ - 一个 CPU 延迟会导致所有其他 CPU 等待 │ +│ │ +│ 问题示例: │ +│ ┌───────────────────────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ CPU 0: ████████████────────────────────────────────────── │ │ +│ │ CPU 1: ████████████────────────────────────────────────── │ │ +│ │ CPU 2: ████████████────────────────────────────────────── │ │ +│ │ CPU 3: ████████████▓▓▓▓(tick中断)──────────────────────── │ │ +│ │ ↑ │ │ +│ │ 同步屏障 │ │ +│ │ 所有 CPU 必须等待 CPU 3 │ │ +│ │ │ │ +│ │ 延迟放大效应: N 个 CPU 的总等待时间 = (N-1) × tick延迟 │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────────────────┘ │ +│ │ +│ NOHZ_FULL 收益: │ +│ - 消除同步等待时的意外延迟 │ +│ - 迭代时间更加一致 │ +│ - 整体运行时间减少 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 9.3 低延迟交易系统 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 低延迟交易系统场景 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 场景描述: │ +│ - 高频交易 (HFT)、市场数据处理 │ +│ - 延迟敏感,微秒级别差异影响收益 │ +│ - 需要极低的尾延迟 (P99/P99.9) │ +│ │ +│ 关键指标: │ +│ - 平均延迟: 通常 < 10μs │ +│ - P99 延迟: 目标 < 50μs │ +│ - P99.99 延迟: 尽可能低 │ +│ │ +│ NOHZ_FULL 收益: │ +│ - 降低延迟抖动 │ +│ - 减少尾延迟 │ +│ - 更可预测的响应时间 │ +│ │ +│ 额外优化: │ +│ - 使用 busy polling 代替中断驱动 I/O │ +│ - DPDK/SPDK 用户态驱动 │ +│ - 内核旁路 (kernel bypass) │ +│ - NUMA 优化 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 9.4 虚拟化/云计算 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 虚拟化场景 (vCPU Pinning) │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 场景描述: │ +│ - 虚拟机 vCPU 绑定到物理 CPU │ +│ - Host tick 干扰 Guest 执行 │ +│ - 多租户环境需要隔离 │ +│ │ +│ Host 配置: │ +│ ┌───────────────────────────────────────────────────────────────┐ │ +│ │ # 保留 CPU 0-1 给 Host,其余给 VM │ │ +│ │ nohz_full=2-63 │ │ +│ │ isolcpus=domain,managed_irq,2-63 │ │ +│ └───────────────────────────────────────────────────────────────┘ │ +│ │ +│ 收益: │ +│ - 减少 VM Exit / VM Entry │ +│ - 提高 vCPU 执行效率 │ +│ - 更好的 Guest 性能隔离 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 10. 最佳实践配置 + +### 10.1 完整配置示例 + +```bash +#!/bin/bash +# setup_nohz_full.sh - NOHZ_FULL 系统配置脚本 + +# === 假设: 8 CPU 系统,CPU 0 为 housekeeping,CPU 1-7 为 isolated === + +# 1. 内核启动参数 (添加到 GRUB) +CMDLINE="nohz_full=1-7 rcu_nocbs=1-7 \ + isolcpus=domain,managed_irq,1-7 \ + irqaffinity=0 \ + nohz=on \ + nowatchdog \ + nmi_watchdog=0 \ + nosoftlockup \ + skew_tick=1 \ + tsc=reliable \ + rcu_nocb_poll" + +# 2. 运行时 IRQ 亲和性设置 +set_irq_affinity() { + for irq in /proc/irq/*/smp_affinity; do + echo 1 > "$irq" 2>/dev/null || true + done +} + +# 3. 绑定 RCU 线程到 housekeeping CPU +bind_rcu_threads() { + for pid in $(pgrep -f "rcu"); do + taskset -p 0x1 "$pid" 2>/dev/null || true + done +} + +# 4. 绑定其他内核线程 +bind_kthreads() { + for pid in $(pgrep -f "ksoftirqd|kworker|migration"); do + taskset -p 0x1 "$pid" 2>/dev/null || true + done +} + +# 5. 设置 workqueue cpumask +setup_workqueues() { + for wq in /sys/devices/virtual/workqueue/*/cpumask; do + echo 1 > "$wq" 2>/dev/null || true + done +} + +# 6. 写回脏页设置 +setup_vm() { + # 减少写回触发的抖动 + echo 1500 > /proc/sys/vm/dirty_writeback_centisecs + echo 3000 > /proc/sys/vm/dirty_expire_centisecs +} + +# 7. 验证设置 +verify_setup() { + echo "=== NOHZ_FULL CPUs ===" + cat /sys/devices/system/cpu/nohz_full + + echo "=== Isolated CPUs ===" + cat /sys/devices/system/cpu/isolated + + echo "=== Housekeeping CPUs ===" + cat /sys/devices/system/cpu/online | \ + awk -F, '{for(i=1;i<=NF;i++) print $i}' | \ + while read cpu; do + if ! grep -q "^$cpu$" /sys/devices/system/cpu/nohz_full; then + echo $cpu + fi + done +} + +# 执行配置 +set_irq_affinity +bind_rcu_threads +bind_kthreads +setup_workqueues +setup_vm +verify_setup +``` + +### 10.2 应用程序配置 + +```bash +# 将关键应用绑定到 isolated CPU +taskset -c 1-7 ./my_realtime_app + +# 设置实时优先级 +chrt -f 99 taskset -c 1-7 ./my_realtime_app + +# 使用 cgroups v2 +mkdir -p /sys/fs/cgroup/isolated +echo "+cpuset" > /sys/fs/cgroup/cgroup.subtree_control +echo "1-7" > /sys/fs/cgroup/isolated/cpuset.cpus +echo "0" > /sys/fs/cgroup/isolated/cpuset.mems +echo $$ > /sys/fs/cgroup/isolated/cgroup.procs +exec ./my_realtime_app +``` + +### 10.3 应用程序最佳实践 + +```c +/* 应用程序优化建议 */ + +// 1. 锁定内存,避免页错误 +mlockall(MCL_CURRENT | MCL_FUTURE); + +// 2. 预分配资源 +void *buffer = malloc(BUFFER_SIZE); +memset(buffer, 0, BUFFER_SIZE); // 预触发页错误 + +// 3. 避免系统调用 (在热路径中) +// 使用用户态计时器而非 gettimeofday() +// 使用共享内存而非管道/套接字 + +// 4. 避免触发调度 +// 不要使用会导致调度的同步原语 +// 使用 busy waiting 代替 sleep + +// 5. 避免内存分配 +// 预分配所有需要的内存 +// 使用对象池模式 +``` + +--- + +## 11. 调试与验证 + +### 11.1 验证 NOHZ_FULL 状态 + +```bash +# 检查内核配置 +zcat /proc/config.gz | grep -E "NO_HZ|RCU_NOCB|CPU_ISOLATION" + +# 查看 nohz_full CPUs +cat /sys/devices/system/cpu/nohz_full + +# 查看 isolated CPUs +cat /sys/devices/system/cpu/isolated + +# 查看 RCU nocb CPUs +cat /sys/kernel/rcu_nocb_cpus # 如果存在 + +# 检查特定 CPU 的 tick 状态 +cat /proc/interrupts | grep -E "LOC|timer" + +# 查看 tick 统计 +cat /proc/timer_list | grep -A5 "Tick Device" +``` + +### 11.2 使用 Ftrace 追踪 + +```bash +# 设置 ftrace +cd /sys/kernel/tracing + +# 追踪 tick 相关函数 +echo 1 > max_graph_depth +echo function_graph > current_tracer +echo tick_nohz_* > set_ftrace_filter +echo 1 > tracing_on + +# 运行一段时间后 +cat trace + +# 追踪特定 CPU 的中断 +echo 1 > per_cpu/cpu1/trace + +# 清理 +echo 0 > tracing_on +echo nop > current_tracer +``` + +### 11.3 使用 perf 分析 + +```bash +# 查看中断频率 +perf stat -e irq:irq_handler_entry -a -C 1-7 sleep 10 + +# 查看调度器活动 +perf stat -e sched:sched_switch -a -C 1-7 sleep 10 + +# 记录详细事件 +perf record -e irq:* -e sched:* -C 1-7 sleep 10 +perf script + +# 使用 osnoise tracer (内核 5.14+) +echo osnoise > /sys/kernel/tracing/current_tracer +cat /sys/kernel/tracing/trace +``` + +### 11.4 OS Jitter 测试工具 + +```bash +# 使用 rt-tests 工具包 +# 安装: apt install rt-tests + +# cyclictest - 测量调度延迟 +cyclictest -t 1 -p 99 -a 1 -n -m -l 100000 + +# 输出示例: +# T: 0 ( 1234) P:99 I:1000 C: 100000 Min: 1 Act: 2 Avg: 2 Max: 15 + +# hwlatdetect - 检测硬件延迟 +hwlatdetect --duration=60 + +# 自定义测试 (内核自带) +git clone git://git.kernel.org/pub/scm/linux/kernel/git/frederic/dynticks-testing.git +cd dynticks-testing +./run_test.sh +``` + +--- + +## 12. 已知限制与注意事项 + +### 12.1 技术限制 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ NOHZ_FULL 限制 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. 至少需要一个 housekeeping CPU │ +│ - 不能将所有 CPU 都设为 nohz_full │ +│ - 需要有 CPU 负责时间维护和系统任务 │ +│ │ +│ 2. 需要至少 2 个 CPU │ +│ - 单 CPU 系统无法使用 NOHZ_FULL │ +│ │ +│ 3. 每秒至少一次 tick │ +│ - 用于更新负载统计、vruntime 等 │ +│ - 这是目前的实现限制,未来可能改进 │ +│ │ +│ 4. 某些条件会恢复 tick │ +│ - POSIX CPU 定时器 │ +│ - perf 事件轮换 │ +│ - 多任务运行 │ +│ - 某些 RCU 操作 │ +│ │ +│ 5. 用户/内核转换开销略增 │ +│ - 需要通知 RCU 等子系统状态变化 │ +│ │ +│ 6. 重配置需要重启 │ +│ - 运行时无法更改 nohz_full 和 rcu_nocbs 设置 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 12.2 注意事项 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 使用注意事项 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ✓ DO: │ +│ - 仔细规划 CPU 分配 │ +│ - 测试验证配置效果 │ +│ - 绑定所有可能的中断和线程到 housekeeping CPU │ +│ - 使用 mlock() 锁定关键应用内存 │ +│ - 预分配所有资源 │ +│ - 保持应用在用户态运行 │ +│ │ +│ ✗ DON'T: │ +│ - 不要在 isolated CPU 上运行系统服务 │ +│ - 不要使用 POSIX CPU 定时器 (在 isolated CPU 上) │ +│ - 不要期望零 jitter (物理限制) │ +│ - 不要忽略 housekeeping CPU 的负载 │ +│ - 不要在生产环境前跳过测试 │ +│ │ +│ ⚠ WATCH OUT: │ +│ - 调试器 (如 gdb) 可能导致意外 tick │ +│ - 某些系统调用会触发 tick │ +│ - 网络/磁盘 I/O 可能引入中断 │ +│ - 页错误会进入内核 │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 12.3 常见问题排查 + +| 问题 | 可能原因 | 解决方案 | +|------|----------|----------| +| tick 未停止 | 多个可运行任务 | 确保只有一个任务运行 | +| 周期性抖动 | POSIX CPU 定时器 | 避免使用 `setitimer()` | +| 随机抖动 | 硬件中断 | 检查并重定向 IRQ | +| 无法启动 | 配置错误 | 确保保留 housekeeping CPU | +| 性能下降 | 过度隔离 | 保留足够的 housekeeping 资源 | + +--- + +## 参考资料 + +1. **内核文档** + - `Documentation/timers/no_hz.rst` + - `Documentation/admin-guide/kernel-per-CPU-kthreads.rst` + - `Documentation/admin-guide/kernel-parameters.txt` + +2. **核心代码** + - `kernel/time/tick-sched.c` - NOHZ 核心实现 + - `kernel/sched/isolation.c` - CPU 隔离管理 + - `kernel/rcu/tree.c` - RCU 实现 + +3. **相关工具** + - `rt-tests` - 实时性能测试工具 + - `trace-cmd` - Ftrace 前端 + - `perf` - 性能分析工具 + +4. **社区资源** + - LWN.net 相关文章 + - Linux Plumbers Conference 演讲 + +--- + +> **文档版本**: 1.0 +> **最后更新**: 2026年1月 +> **基于内核版本**: Linux 6.19 diff --git a/PCIe_Knowledge_Summary.md b/PCIe_Knowledge_Summary.md new file mode 100644 index 00000000000000..0472772bdb2387 --- /dev/null +++ b/PCIe_Knowledge_Summary.md @@ -0,0 +1,1379 @@ +# PCIe 知识要点与 Linux 内核设备加载流程 + +> 分享人:Linux 内核开发工程师 +> 日期:2026年1月 + +--- + +## 目录 + +1. [PCIe 基础概念](#1-pcie-基础概念) + - 1.1 [什么是 PCIe?](#11-什么是-pcie) + - 1.2 [PCIe vs PCI 对比](#12-pcie-vs-pci-对比) + - 1.3 [PCIe 带宽速率](#13-pcie-带宽速率) + - 1.4 [Lane(通道)概念](#14-lane通道概念) + - 1.5 [PCIe 发展历程](#15-pcie-发展历程) +2. [PCIe 拓扑结构](#2-pcie-拓扑结构) + - 2.1 [整体架构](#21-整体架构) + - 2.2 [核心组件说明](#22-核心组件说明) + - 2.3 [PCIe Switch 内部结构](#23-pcie-switch-内部结构) +3. [PCIe 配置空间](#3-pcie-配置空间) + - 3.1 [配置空间结构](#31-配置空间结构) + - 3.2 [重要寄存器说明](#32-重要寄存器说明) + - 3.3 [BAR (Base Address Register) 解析](#33-bar-base-address-register-解析) +4. [PCIe 事务层协议](#4-pcie-事务层协议) + - 4.1 [PCIe 分层架构](#41-pcie-分层架构) + - 4.2 [TLP (Transaction Layer Packet) 类型](#42-tlp-transaction-layer-packet-类型) + - 4.3 [TLP 包结构](#43-tlp-包结构) +5. [Linux 内核 PCIe 子系统架构](#5-linux-内核-pcie-子系统架构) + - 5.1 [内核 PCIe 目录结构](#51-内核-pcie-目录结构) + - 5.2 [核心数据结构关系](#52-核心数据结构关系) +6. [PCIe 设备枚举流程](#6-pcie-设备枚举流程) + - 6.1 [枚举总体流程](#61-枚举总体流程) + - 6.2 [关键代码路径](#62-关键代码路径) + - 6.3 [BDF 地址编码](#63-bdf-地址编码) +7. [PCIe 驱动加载流程](#7-pcie-驱动加载流程) + - 7.1 [驱动注册与匹配流程](#71-驱动注册与匹配流程) + - 7.2 [驱动结构定义](#72-驱动结构定义) + - 7.3 [Probe 函数典型实现](#73-probe-函数典型实现) + - 7.4 [设备初始化步骤图](#74-设备初始化步骤图) +8. [关键数据结构](#8-关键数据结构) + - 8.1 [struct pci_dev](#81-struct-pci_dev) + - 8.2 [struct pci_driver](#82-struct-pci_driver) + - 8.3 [struct pci_bus](#83-struct-pci_bus) +9. [实践示例](#9-实践示例) + - 9.1 [查看系统 PCIe 设备](#91-查看系统-pcie-设备) + - 9.2 [内核配置空间访问](#92-内核配置空间访问) + - 9.3 [sysfs 接口](#93-sysfs-接口) +10. [常见问题与调试](#10-常见问题与调试) + - 10.1 [调试命令](#101-调试命令) + - 10.2 [常见问题](#102-常见问题) +11. [pci-utils 基本用法](#11-pci-utils-基本用法) + - 11.1 [安装](#111-安装) + - 11.2 [lspci — 列出 PCI 设备](#112-lspci--列出-pci-设备) + - 11.3 [setpci — 读写 PCI 配置空间](#113-setpci--读写-pci-配置空间) + - 11.4 [update-pciids — 更新 PCI ID 数据库](#114-update-pciids--更新-pci-id-数据库) + - 11.5 [实用调试场景](#115-实用调试场景) + - 11.6 [pciutils 库 (libpci) 编程接口](#116-pciutils-库-libpci-编程接口) + +--- + +## 1. PCIe 基础概念 + +### 1.1 什么是 PCIe? + +**PCIe (PCI Express)** 是一种高速串行计算机扩展总线标准,用于连接主板与各种外设(显卡、网卡、SSD等)。 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PCIe 发展历程 │ +├─────────────────────────────────────────────────────────────────┤ +│ PCI (1992) → PCI-X (1998) → PCIe 1.0 (2003) │ +│ ↓ │ +│ PCIe 6.0 (2022) ← PCIe 5.0 (2019) ← ... ← PCIe 2.0 (2007) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.2 PCIe vs PCI 对比 + +| 特性 | PCI | PCIe | +|------|-----|------| +| **连接方式** | 并行总线(共享) | 点对点串行链路 | +| **传输方向** | 半双工 | 全双工 | +| **带宽** | 共享 133MB/s (32位) | 独享,可扩展 (x1~x32) | +| **热插拔** | 有限支持 | 原生支持 | +| **电源管理** | 基础 | 高级 ASPM | + +### 1.3 PCIe 带宽速率 + +``` +┌────────────────────────────────────────────────────────────────┐ +│ PCIe 各版本带宽对比 │ +├──────────┬────────────┬───────────┬───────────┬───────────────┤ +│ 版本 │ 传输速率 │ 编码 │ x1 带宽 │ x16 带宽 │ +├──────────┼────────────┼───────────┼───────────┼───────────────┤ +│ PCIe 1.0 │ 2.5 GT/s │ 8b/10b │ 250 MB/s │ 4 GB/s │ +│ PCIe 2.0 │ 5.0 GT/s │ 8b/10b │ 500 MB/s │ 8 GB/s │ +│ PCIe 3.0 │ 8.0 GT/s │ 128b/130b │ 984 MB/s │ 15.75 GB/s │ +│ PCIe 4.0 │ 16.0 GT/s │ 128b/130b │ 1.97 GB/s │ 31.5 GB/s │ +│ PCIe 5.0 │ 32.0 GT/s │ 128b/130b │ 3.94 GB/s │ 63 GB/s │ +│ PCIe 6.0 │ 64.0 GT/s │ 1b/1b+FEC │ 7.88 GB/s │ 126 GB/s │ +└──────────┴────────────┴───────────┴───────────┴───────────────┘ + +注:GT/s = Giga Transfers per second (每秒十亿次传输) +``` + +### 1.4 Lane(通道)概念 + +``` + PCIe Link 结构 + ┌─────────────────────────────────────────────────────┐ + │ │ + │ Device A Device B │ + │ ┌──────┐ ┌──────┐ │ + │ │ │ ════════ TX ══════════► │ │ │ + │ │ │ │ │ │ x1 Link + │ │ │ ◄════════ RX ══════════ │ │ │ (1 Lane) + │ └──────┘ └──────┘ │ + │ │ + └─────────────────────────────────────────────────────┘ + + ┌─────────────────────────────────────────────────────┐ + │ Device A Device B │ + │ ┌──────┐ ════════ Lane 0 TX ═══► ┌──────┐ │ + │ │ │ ◄═══════ Lane 0 RX ════ │ │ │ + │ │ │ ════════ Lane 1 TX ═══► │ │ │ x4 Link + │ │ │ ◄═══════ Lane 1 RX ════ │ │ │ (4 Lanes) + │ │ │ ════════ Lane 2 TX ═══► │ │ │ + │ │ │ ◄═══════ Lane 2 RX ════ │ │ │ + │ │ │ ════════ Lane 3 TX ═══► │ │ │ + │ │ │ ◄═══════ Lane 3 RX ════ │ │ │ + │ └──────┘ └──────┘ │ + └─────────────────────────────────────────────────────┘ +``` + +### 1.5 PCIe 发展历程 + +PCIe 技术从传统 PCI 总线演化而来,经历了多次重大技术革新,以下是完整的发展脉络: + +#### 1.5.1 前 PCIe 时代 + +| 时间 | 标准 | 说明 | +|------|------|------| +| **1990年** | PCI 草案 | Intel 主导制定 PCI (Peripheral Component Interconnect) 规范草案 | +| **1992年** | PCI 1.0 | 正式发布,32位/33MHz 并行总线,带宽 133MB/s | +| **1993年** | PCI 2.0 | 改进规范,完善电气特性和协议细节 | +| **1995年** | PCI 2.1 | 支持 66MHz,带宽提升至 266MB/s (32位) 或 533MB/s (64位) | +| **1998年** | PCI-X 1.0 | 面向服务器市场,64位/133MHz,带宽达 1066MB/s | +| **2002年** | PCI-X 2.0 | 支持 266MHz/533MHz,但共享总线架构的瓶颈愈发明显 | + +``` + PCI 总线 (共享并行架构的局限) + ┌──────────────────────────────────────────────────────────────┐ + │ │ + │ CPU ◄══════════════════════════════════════════════► │ + │ ║ ║ ║ ║ │ + │ 设备A 设备B 设备C 设备D │ + │ │ + │ 问题:所有设备共享同一总线带宽,存在竞争和冲突 │ + │ 并行信号高频下的串扰和时序问题严重 │ + └──────────────────────────────────────────────────────────────┘ +``` + +#### 1.5.2 PCIe 各代际发展 + +**PCIe 1.0 (2003年)** +- 由 Intel、Dell、HP、IBM 等联合推出 +- 采用**点对点串行**架构,彻底取代 PCI 共享并行总线 +- 每通道 (Lane) 传输速率 2.5 GT/s,采用 8b/10b 编码 +- 单通道 (x1) 有效带宽 250 MB/s(双向 500 MB/s) +- 支持 x1, x2, x4, x8, x12, x16, x32 通道配置 +- 定义了完整的分层协议架构(事务层、数据链路层、物理层) + +**PCIe 1.1 (2005年)** +- 对 1.0 的小幅改进 +- 完善了信号完整性要求和测试规范 + +**PCIe 2.0 (2007年)** +- 传输速率翻倍至 **5.0 GT/s**,仍使用 8b/10b 编码 +- 单通道有效带宽 500 MB/s(双向 1 GB/s) +- 保持与 PCIe 1.x 完全向后兼容 +- 引入链路速度自动协商机制 + +**PCIe 3.0 (2010年)** +- 传输速率提升至 **8.0 GT/s** +- 编码方式从 8b/10b 改为 **128b/130b**,编码效率从 80% 提升至 ~98.5% +- 单通道有效带宽约 984 MB/s(双向约 1.97 GB/s) +- 成为最长寿、应用最广泛的 PCIe 版本之一 +- 此后很长一段时间,大量显卡、SSD 和网卡基于此标准 + +**PCIe 4.0 (2017年)** +- 传输速率再翻倍至 **16.0 GT/s**,编码仍为 128b/130b +- 单通道有效带宽约 1.97 GB/s(双向约 3.94 GB/s) +- AMD Zen 2 架构(如 Ryzen 3000 系列)率先支持 +- NVMe SSD(如 Samsung 980 PRO)开始大量采用 PCIe 4.0 x4 + +**PCIe 5.0 (2019年)** +- 传输速率提升至 **32.0 GT/s**,编码仍为 128b/130b +- 单通道有效带宽约 3.94 GB/s(双向约 7.88 GB/s) +- Intel 第 12 代 Alder Lake 和 AMD Zen 4 架构开始支持 +- 主要面向高端 SSD、AI 加速卡、高速网卡(100G/200G 以上) + +**PCIe 6.0 (2022年)** +- 传输速率再次翻倍至 **64.0 GT/s** +- 编码方式革命性改变:采用 **PAM4 (4级脉冲幅度调制)** + **1b/1b FLIT 编码** + **FEC (前向纠错)** +- 引入 FLIT (Flow Control Unit) 固定大小包机制,取代传统的可变长 TLP +- 单通道有效带宽约 7.88 GB/s(双向约 15.75 GB/s) +- x16 配置下双向带宽高达 **252 GB/s** + +**PCIe 7.0 (预计 2025年)** +- 目标传输速率 **128.0 GT/s** +- 继续使用 PAM4 调制 +- x16 双向带宽预计将达 **512 GB/s** +- 主要面向数据中心、AI/ML 训练、高性能计算 (HPC) + +``` + PCIe 各版本发展时间线与带宽演进 + ═══════════════════════════════════════════════════════════════ + + 2003 2007 2010 2017 2019 2022 2025(预计) + │ │ │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ ▼ ▼ + 1.0 2.0 3.0 4.0 5.0 6.0 7.0 + 2.5GT/s 5GT/s 8GT/s 16GT/s 32GT/s 64GT/s 128GT/s + 8b/10b 8b/10b 128b/130b 128b/130b PAM4 PAM4 + + x16 双向带宽: + 8GB/s → 16GB/s → 32GB/s → 64GB/s → 128GB/s → 252GB/s → 512GB/s + ×2 ×2 ×2 ×2 ×2 ×2 + + 关键技术节点: + ├─ 2003: 串行取代并行,点对点取代共享总线 + ├─ 2010: 128b/130b 编码取代 8b/10b,编码效率质的飞跃 + ├─ 2022: PAM4 调制 + FLIT 机制,信号层面革命性变化 + └─ 2025: 继续倍增,面向 AI/HPC 超大带宽需求 +``` + +#### 1.5.3 PCIe 标准管理组织 + +PCIe 规范由 **PCI-SIG (PCI Special Interest Group)** 制定和维护: + +- **成立时间**: 1992 年 +- **成员**: 超过 900 家企业(含 Intel、AMD、NVIDIA、Broadcom、Samsung 等) +- **职责**: 制定 PCI/PCIe 规范、合规性测试、互操作性认证 +- **官网**: https://pcisig.com + +--- + +## 2. PCIe 拓扑结构 + +### 2.1 整体架构 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ CPU / Memory │ +└────────────────────────────────┬────────────────────────────────────────┘ + │ + ┌────────────▼────────────┐ + │ Root Complex (RC) │ + │ (PCIe 根复合体) │ + └──┬─────────┬─────────┬──┘ + │ │ │ + ┌───────────▼─┐ ┌────▼────┐ ┌─▼───────────┐ + │ Root Port 0 │ │ Root │ │ Root Port 2 │ + │ (RP) │ │ Port 1 │ │ (RP) │ + └──────┬──────┘ └────┬────┘ └──────┬──────┘ + │ │ │ + ┌──────▼──────┐ │ ┌──────▼──────┐ + │ Endpoint │ │ │ Switch │ + │ (显卡) │ │ │ (交换机) │ + └─────────────┘ │ └──┬─────┬────┘ + │ │ │ + ┌──────▼────┐ ┌───▼─┐ ┌─▼───┐ + │ Endpoint │ │ EP │ │ EP │ + │ (NVMe) │ │ │ │ │ + └───────────┘ └─────┘ └─────┘ +``` + +### 2.2 核心组件说明 + +| 组件 | 英文名称 | 功能描述 | +|------|----------|----------| +| **Root Complex** | RC | 连接 CPU/Memory 与 PCIe 总线的桥梁 | +| **Root Port** | RP | RC 上的 PCIe 端口,连接下游设备 | +| **Switch** | Switch | PCIe 交换机,扩展端口数量 | +| **Endpoint** | EP | 终端设备(显卡、网卡、NVMe等) | +| **Bridge** | Bridge | PCI-PCIe 桥接器 | + +### 2.3 PCIe Switch 内部结构 + +``` + ┌─────────────────────────────────┐ + │ PCIe Switch │ + │ ┌──────────────────────────┐ │ + │ │ Internal Bus (虚拟) │ │ + ┌───────────────┤ └──────────────────────────┘ │ + │ │ │ │ │ │ + │ Upstream │ ┌──▼──┐ ┌───▼──┐ ┌──▼──┐│ + │ Port │ │ DP0 │ │ DP1 │ │ DP2 ││ + │ │ └──┬──┘ └───┬──┘ └──┬──┘│ + └───────────────┤ │ │ │ │ + └─────┼───────────┼───────────┼───┘ + │ │ │ + ┌─────▼───┐ ┌─────▼───┐ ┌─────▼───┐ + │ EP │ │ EP │ │ EP │ + └─────────┘ └─────────┘ └─────────┘ +``` + +--- + +## 3. PCIe 配置空间 + +### 3.1 配置空间结构 + +PCIe 设备有 4KB 配置空间(PCI 只有 256B): + +``` + 地址偏移 + ┌────────────────────────────────────────┐ 0x000 + │ │ + │ PCI 兼容配置空间 │ + │ (256 Bytes) │ + │ │ + │ ┌──────────────────────────────────┐ │ 0x000 + │ │ Vendor ID (2B) │ Device ID (2B) │ │ + │ ├──────────────────────────────────┤ │ 0x004 + │ │ Command (2B) │ Status (2B) │ │ + │ ├──────────────────────────────────┤ │ 0x008 + │ │ Class Code (3B)│ Revision (1B) │ │ + │ ├──────────────────────────────────┤ │ 0x00C + │ │ BIST │ Header │ Latency │ Cache │ │ + │ ├──────────────────────────────────┤ │ 0x010 + │ │ BAR0 (Base Address) │ │ + │ ├──────────────────────────────────┤ │ 0x014 + │ │ BAR1 │ │ + │ ├──────────────────────────────────┤ │ ... + │ │ BAR2 ~ BAR5 │ │ + │ ├──────────────────────────────────┤ │ 0x034 + │ │ Capabilities Pointer │ │ + │ ├──────────────────────────────────┤ │ 0x03C + │ │ IRQ Line │ IRQ Pin │ ... │ │ + │ └──────────────────────────────────┘ │ + │ │ + ├────────────────────────────────────────┤ 0x100 + │ │ + │ PCIe 扩展配置空间 │ + │ (Extended Configuration) │ + │ (3840 Bytes) │ + │ │ + │ - PCIe Capability │ + │ - MSI/MSI-X Capability │ + │ - Power Management │ + │ - AER (Advanced Error Reporting) │ + │ - SR-IOV │ + │ - ... │ + │ │ + └────────────────────────────────────────┘ 0xFFF +``` + +### 3.2 重要寄存器说明 + +```c +/* 配置空间偏移定义 (include/uapi/linux/pci_regs.h) */ + +#define PCI_VENDOR_ID 0x00 /* 厂商ID */ +#define PCI_DEVICE_ID 0x02 /* 设备ID */ +#define PCI_COMMAND 0x04 /* 命令寄存器 */ +#define PCI_STATUS 0x06 /* 状态寄存器 */ +#define PCI_CLASS_REVISION 0x08 /* 类代码和版本 */ +#define PCI_BASE_ADDRESS_0 0x10 /* BAR0 */ +#define PCI_INTERRUPT_LINE 0x3c /* 中断线 */ +#define PCI_INTERRUPT_PIN 0x3d /* 中断引脚 */ +``` + +### 3.3 BAR (Base Address Register) 解析 + +``` + BAR 寄存器格式 (Memory BAR) + ┌─────────────────────────────────────────────────────────┐ + │ 31 4 3 2 1 0 │ + │ ├──────────────────────────────────┼───┼───┼───┼───┤ │ + │ │ Base Address │Pre│Type │ 0 │ │ + │ │ (对齐后的基地址) │fch│ │ │ │ + │ └──────────────────────────────────┴───┴───┴───┴───┘ │ + │ │ + │ Bit 0: 0 = Memory Space, 1 = I/O Space │ + │ Bit 2-1: Type (00=32位, 10=64位) │ + │ Bit 3: Prefetchable │ + └─────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. PCIe 事务层协议 + +### 4.1 PCIe 分层架构 + +``` + ┌─────────────────────────────────────────────────────────┐ + │ Software Layer │ + │ (驱动程序 / 应用程序) │ + └─────────────────────────────────────────────────────────┘ + ↕ + ╔═════════════════════════════════════════════════════════╗ + ║ Transaction Layer (事务层) ║ + ║ - TLP (Transaction Layer Packet) 生成/解析 ║ + ║ - 流控制 (Flow Control) ║ + ║ - 虚拟通道 (Virtual Channels) ║ + ╚═════════════════════════════════════════════════════════╝ + ↕ + ╔═════════════════════════════════════════════════════════╗ + ║ Data Link Layer (数据链路层) ║ + ║ - DLLP (Data Link Layer Packet) ║ + ║ - ACK/NAK 协议 ║ + ║ - CRC 校验 ║ + ║ - 重传机制 ║ + ╚═════════════════════════════════════════════════════════╝ + ↕ + ╔═════════════════════════════════════════════════════════╗ + ║ Physical Layer (物理层) ║ + ║ - 编码 (8b/10b, 128b/130b) ║ + ║ - 串行化/解串行化 ║ + ║ - 链路训练 (Link Training) ║ + ╚═════════════════════════════════════════════════════════╝ +``` + +### 4.2 TLP (Transaction Layer Packet) 类型 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ TLP 类型分类 │ +├─────────────────┬───────────────────────────────────────────────┤ +│ 类型 │ 描述 │ +├─────────────────┼───────────────────────────────────────────────┤ +│ Memory Read │ 内存读请求 │ +│ Memory Write │ 内存写请求 │ +│ I/O Read │ I/O 端口读 │ +│ I/O Write │ I/O 端口写 │ +│ Config Read │ 配置空间读 (Type 0/1) │ +│ Config Write │ 配置空间写 (Type 0/1) │ +│ Message │ 消息传递 (中断、电源管理等) │ +│ Completion │ 完成包 (对读请求的响应) │ +└─────────────────┴───────────────────────────────────────────────┘ +``` + +### 4.3 TLP 包结构 + +``` + ┌────────────────────────────────────────────────────────────┐ + │ TLP Packet Structure │ + ├────────────────────────────────────────────────────────────┤ + │ ┌──────────────────────────────────────────────────────┐ │ + │ │ Header (3-4 DW) │ │ + │ │ ┌────────┬────────┬────────┬────────┐ │ │ + │ │ │ Fmt/ │ TC │ │ Length │ Req ID │ ... │ │ + │ │ │ Type │ │ │ │ │ │ │ + │ │ └────────┴────────┴────────┴────────┘ │ │ + │ └──────────────────────────────────────────────────────┘ │ + │ ┌──────────────────────────────────────────────────────┐ │ + │ │ Data Payload (0-1024 DW) │ │ + │ │ (可选) │ │ + │ └──────────────────────────────────────────────────────┘ │ + │ ┌──────────────────────────────────────────────────────┐ │ + │ │ ECRC (可选, 1 DW) │ │ + │ └──────────────────────────────────────────────────────┘ │ + └────────────────────────────────────────────────────────────┘ + + 注: DW = Double Word = 4 Bytes +``` + +--- + +## 5. Linux 内核 PCIe 子系统架构 + +### 5.1 内核 PCIe 目录结构 + +``` +drivers/pci/ +├── access.c # 配置空间访问 +├── bus.c # 总线管理 +├── probe.c # 设备枚举探测 ← 核心文件 +├── pci-driver.c # PCI 驱动框架 ← 核心文件 +├── pci.c # PCI 核心功能 +├── quirks.c # 硬件兼容处理 +├── msi/ # MSI/MSI-X 中断 +│ ├── api.c +│ ├── irqdomain.c +│ └── msi.c +├── pcie/ # PCIe 特定功能 +│ ├── portdrv.c # PCIe Port 驱动 +│ ├── aer.c # 高级错误报告 +│ ├── aspm.c # 活动状态电源管理 +│ └── dpc.c # 下游端口遏制 +├── hotplug/ # 热插拔支持 +└── controller/ # Host Controller 驱动 +``` + +### 5.2 核心数据结构关系 + +``` + ┌───────────────────┐ + │ pci_host_bridge │ + │ (Host 控制器) │ + └─────────┬─────────┘ + │ + ▼ + ┌───────────────────┐ + ┌─────────│ pci_bus │─────────┐ + │ │ (PCI 总线) │ │ + │ └─────────┬─────────┘ │ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ pci_dev │ │ pci_dev │ │ pci_dev │ +│ (PCI 设备) │ │ (Bridge) │ │ (PCI 设备) │ +└────────┬────────┘ └────────┬────────┘ └─────────────────┘ + │ │ + ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ +│ pci_driver │ │ pci_bus │ +│ (设备驱动) │ │ (子总线) │ +└─────────────────┘ └────────┬────────┘ + │ + ▼ + ┌─────────────────┐ + │ pci_dev │ + │ (下游设备) │ + └─────────────────┘ +``` + +--- + +## 6. PCIe 设备枚举流程 + +### 6.1 枚举总体流程 + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ PCIe 设备枚举流程图 │ +└──────────────────────────────────────────────────────────────────────┘ + + 内核启动 + │ + ▼ +┌───────────────────┐ +│ pci_host_probe() │ ← Host Controller 驱动调用 +└─────────┬─────────┘ + │ + ▼ +┌─────────────────────────┐ +│ pci_scan_root_bus_ │ +│ bridge() │ ← 扫描根总线 +└─────────┬───────────────┘ + │ + ▼ +┌─────────────────────────┐ +│ pci_scan_child_bus() │ ← 扫描子总线 +└─────────┬───────────────┘ + │ + ├──────────────────────────────────────┐ + │ │ + ▼ ▼ +┌──────────────────────┐ ┌──────────────────────┐ +│ pci_scan_slot() │ │ (递归扫描) │ +│ 对每个 slot 扫描 │ │ 如果是 Bridge, │ +└─────────┬────────────┘ │ 继续扫描下游总线 │ + │ └──────────────────────┘ + ▼ +┌──────────────────────┐ +│ pci_scan_single_ │ +│ device() │ +└─────────┬────────────┘ + │ + ▼ +┌──────────────────────┐ ┌────────────────────────────────┐ +│ pci_scan_device() │────►│ 读取 Vendor ID / Device ID │ +└─────────┬────────────┘ │ 判断设备是否存在 │ + │ └────────────────────────────────┘ + ▼ +┌──────────────────────┐ +│ pci_setup_device() │ ← 解析配置空间,填充 pci_dev 结构 +└─────────┬────────────┘ + │ + ▼ +┌──────────────────────┐ +│ pci_device_add() │ ← 添加到设备模型 +└─────────┬────────────┘ + │ + ▼ +┌──────────────────────┐ +│ pci_bus_add_devices()│ ← 触发驱动匹配 +└──────────────────────┘ +``` + +### 6.2 关键代码路径 + +```c +/* drivers/pci/probe.c - 设备扫描核心函数 */ + +static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +{ + struct pci_dev *dev; + u32 l; + + /* 1. 读取 Vendor ID 和 Device ID */ + if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) + return NULL; /* 设备不存在 */ + + /* 2. 分配 pci_dev 结构 */ + dev = pci_alloc_dev(bus); + if (!dev) + return NULL; + + dev->devfn = devfn; + dev->vendor = l & 0xffff; + dev->device = (l >> 16) & 0xffff; + + /* 3. 读取完整配置空间,初始化设备 */ + if (pci_setup_device(dev)) { + kfree(dev); + return NULL; + } + + return dev; +} +``` + +### 6.3 BDF 地址编码 + +``` + ┌─────────────────────────────────────────────────────────┐ + │ BDF (Bus:Device.Function) 地址 │ + ├─────────────────────────────────────────────────────────┤ + │ │ + │ Example: 0000:03:00.0 │ + │ │ + │ ┌────────┬────────┬────────┬──────────┐ │ + │ │ Domain │ Bus │ Device │ Function │ │ + │ │ (16位) │ (8位) │ (5位) │ (3位) │ │ + │ ├────────┼────────┼────────┼──────────┤ │ + │ │ 0000 │ 03 │ 00 │ 0 │ │ + │ └────────┴────────┴────────┴──────────┘ │ + │ │ + │ 配置空间地址计算: │ + │ Address = Base + (Bus << 20) + (Dev << 15) + │ + │ (Func << 12) + Register │ + │ │ + └─────────────────────────────────────────────────────────┘ +``` + +--- + +## 7. PCIe 驱动加载流程 + +### 7.1 驱动注册与匹配流程 + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ PCI 驱动加载流程 │ +└──────────────────────────────────────────────────────────────────────┘ + + 驱动模块 设备枚举 + │ │ + ▼ ▼ +┌───────────────────┐ ┌───────────────────┐ +│ module_init() │ │ pci_device_add() │ +│ 模块初始化 │ │ │ +└─────────┬─────────┘ └─────────┬─────────┘ + │ │ + ▼ ▼ +┌───────────────────┐ ┌───────────────────┐ +│pci_register_ │ │ device_add() │ +│ driver() │ │ │ +└─────────┬─────────┘ └─────────┬─────────┘ + │ │ + │ ┌─────────────────┐ │ + └────────►│ PCI Core │◄───────────┘ + │ │ + │ pci_bus_match() │ + │ 匹配 device_id │ + └────────┬────────┘ + │ + 匹配成功? │ + ┌────────┴────────┐ + │ │ + ▼ ▼ + 匹配成功 匹配失败 + │ │ + ▼ ▼ + ┌─────────────────┐ 设备等待 + │ pci_device_ │ 驱动加载 + │ probe() │ + └────────┬────────┘ + │ + ▼ + ┌─────────────────┐ + │ drv->probe() │ ← 调用驱动的 probe 函数 + │ 驱动初始化设备 │ + └─────────────────┘ +``` + +### 7.2 驱动结构定义 + +```c +/* 典型的 PCI 驱动结构 */ + +/* 1. 设备 ID 表 - 声明驱动支持的设备 */ +static const struct pci_device_id my_pci_ids[] = { + { PCI_DEVICE(0x8086, 0x1234) }, /* Intel 某设备 */ + { PCI_DEVICE(0x10de, 0x5678) }, /* NVIDIA 某设备 */ + { PCI_DEVICE_CLASS(PCI_CLASS_NETWORK_ETHERNET << 8, ~0) }, + { 0, } /* 结束标志 */ +}; +MODULE_DEVICE_TABLE(pci, my_pci_ids); + +/* 2. 驱动结构体 */ +static struct pci_driver my_pci_driver = { + .name = "my_pci_driver", + .id_table = my_pci_ids, + .probe = my_probe, /* 设备探测 */ + .remove = my_remove, /* 设备移除 */ + .suspend = my_suspend, /* 电源管理 */ + .resume = my_resume, + .shutdown = my_shutdown, + .err_handler = &my_err_handler, /* 错误处理 */ +}; + +/* 3. 注册驱动 */ +module_pci_driver(my_pci_driver); +``` + +### 7.3 Probe 函数典型实现 + +```c +static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret; + void __iomem *mmio_base; + + /* Step 1: 启用 PCI 设备 */ + ret = pci_enable_device(pdev); + if (ret) + return ret; + + /* Step 2: 请求 MMIO 区域 */ + ret = pci_request_regions(pdev, "my_driver"); + if (ret) + goto err_disable; + + /* Step 3: 映射 BAR 空间 */ + mmio_base = pci_iomap(pdev, 0, 0); /* 映射 BAR0 */ + if (!mmio_base) { + ret = -ENOMEM; + goto err_release; + } + + /* Step 4: 设置 DMA 掩码 */ + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + goto err_unmap; + } + + /* Step 5: 启用 Bus Master */ + pci_set_master(pdev); + + /* Step 6: 请求中断 (MSI/MSI-X) */ + ret = pci_alloc_irq_vectors(pdev, 1, 16, PCI_IRQ_MSIX | PCI_IRQ_MSI); + if (ret < 0) + goto err_unmap; + + /* Step 7: 注册中断处理函数 */ + ret = request_irq(pci_irq_vector(pdev, 0), my_irq_handler, + 0, "my_driver", pdev); + if (ret) + goto err_free_irq_vectors; + + /* Step 8: 设备特定初始化 */ + /* ... */ + + dev_info(&pdev->dev, "Device initialized successfully\n"); + return 0; + +err_free_irq_vectors: + pci_free_irq_vectors(pdev); +err_unmap: + pci_iounmap(pdev, mmio_base); +err_release: + pci_release_regions(pdev); +err_disable: + pci_disable_device(pdev); + return ret; +} +``` + +### 7.4 设备初始化步骤图 + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ PCI 设备初始化步骤 │ +└─────────────────────────────────────────────────────────────────────┘ + + ┌─────────────────┐ + │ 1. Enable Device │ pci_enable_device() + │ 唤醒设备 │ - 将设备从 D3 唤醒到 D0 + └────────┬────────┘ - 分配 I/O 和 Memory 资源 + │ + ▼ + ┌─────────────────┐ + │ 2. Request │ pci_request_regions() + │ Regions │ - 独占访问 BAR 区域 + └────────┬────────┘ - 防止资源冲突 + │ + ▼ + ┌─────────────────┐ + │ 3. Map BAR │ pci_iomap() / ioremap() + │ 映射 BAR │ - 将物理地址映射到虚拟地址 + └────────┬────────┘ - 获得 void __iomem * 指针 + │ + ▼ + ┌─────────────────┐ + │ 4. Set DMA Mask │ dma_set_mask_and_coherent() + │ 设置DMA掩码 │ - 指定设备 DMA 地址能力 + └────────┬────────┘ - 32位 or 64位 + │ + ▼ + ┌─────────────────┐ + │ 5. Enable │ pci_set_master() + │ Bus Master │ - 允许设备发起 DMA 传输 + └────────┬────────┘ + │ + ▼ + ┌─────────────────┐ + │ 6. Setup IRQ │ pci_alloc_irq_vectors() + │ 配置中断 │ request_irq() + └────────┬────────┘ - 配置 MSI/MSI-X 或 Legacy INTx + │ + ▼ + ┌─────────────────┐ + │ 7. Device │ 设备特定初始化 + │ Specific │ - 初始化硬件寄存器 + └─────────────────┘ - 注册网络/块设备等 +``` + +--- + +## 8. 关键数据结构 + +### 8.1 struct pci_dev + +```c +/* include/linux/pci.h */ +struct pci_dev { + struct list_head bus_list; /* 总线设备链表节点 */ + struct pci_bus *bus; /* 所属总线 */ + struct pci_bus *subordinate; /* 桥接的下级总线 */ + + unsigned int devfn; /* 编码的 device & function */ + unsigned short vendor; /* 厂商 ID */ + unsigned short device; /* 设备 ID */ + unsigned short subsystem_vendor; + unsigned short subsystem_device; + unsigned int class; /* 设备类别 */ + u8 revision; /* 修订版本 */ + u8 hdr_type; /* 头类型 */ + + u8 pcie_cap; /* PCIe capability 偏移 */ + u8 msi_cap; /* MSI capability 偏移 */ + u8 msix_cap; /* MSI-X capability 偏移 */ + + struct pci_driver *driver; /* 绑定的驱动 */ + u64 dma_mask; /* DMA 地址掩码 */ + + pci_power_t current_state; /* 当前电源状态 D0-D3 */ + + struct resource resource[DEVICE_COUNT_RESOURCE]; /* BAR 资源 */ + + /* ... 更多字段 ... */ +}; +``` + +### 8.2 struct pci_driver + +```c +/* include/linux/pci.h */ +struct pci_driver { + const char *name; /* 驱动名称 */ + const struct pci_device_id *id_table; /* 支持的设备 ID 表 */ + + int (*probe)(struct pci_dev *dev, + const struct pci_device_id *id); /* 探测函数 */ + void (*remove)(struct pci_dev *dev); /* 移除函数 */ + int (*suspend)(struct pci_dev *dev, pm_message_t state); /* 挂起 */ + int (*resume)(struct pci_dev *dev); /* 恢复 */ + void (*shutdown)(struct pci_dev *dev); /* 关机 */ + + struct pci_error_handlers *err_handler; /* 错误处理 */ + + struct device_driver driver; /* 通用驱动结构 */ + struct pci_dynids dynids; /* 动态 ID 列表 */ +}; +``` + +### 8.3 struct pci_bus + +```c +/* include/linux/pci.h */ +struct pci_bus { + struct list_head node; /* 总线链表节点 */ + struct pci_bus *parent; /* 父总线 */ + struct list_head children; /* 子总线列表 */ + struct list_head devices; /* 设备列表 */ + + struct pci_dev *self; /* 代表此总线的桥设备 */ + struct list_head slots; /* 插槽列表 */ + struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; + + struct pci_ops *ops; /* 配置空间访问操作 */ + void *sysdata; /* 系统特定数据 */ + + unsigned char number; /* 总线号 */ + unsigned char primary; /* 主总线号 */ + unsigned char max_bus_speed; /* 最大总线速度 */ + unsigned char cur_bus_speed; /* 当前总线速度 */ + + /* ... */ +}; +``` + +--- + +## 9. 实践示例 + +### 9.1 查看系统 PCIe 设备 + +```bash +# 列出所有 PCI 设备 +$ lspci +00:00.0 Host bridge: Intel Corporation Device 9a14 (rev 01) +00:02.0 VGA compatible controller: Intel Corporation Device 9a49 (rev 03) +00:1f.0 ISA bridge: Intel Corporation Device a082 (rev 20) +01:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd ... + +# 详细信息 +$ lspci -vvv -s 01:00.0 + +# 树形显示 +$ lspci -tv +-[0000:00]-+-00.0 Intel Corporation Device 9a14 + +-02.0 Intel Corporation Device 9a49 + +-1c.0-[01]----00.0 Samsung Electronics Co Ltd ... + \-1f.0 Intel Corporation Device a082 +``` + +### 9.2 内核配置空间访问 + +```c +/* 读取配置空间 */ +u16 vendor, device; +u32 class; + +pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor); +pci_read_config_word(pdev, PCI_DEVICE_ID, &device); +pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class); + +/* 写入配置空间 */ +u16 cmd; +pci_read_config_word(pdev, PCI_COMMAND, &cmd); +cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; +pci_write_config_word(pdev, PCI_COMMAND, cmd); + +/* 访问 PCIe 扩展能力 */ +u16 devctl; +pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &devctl); +``` + +### 9.3 sysfs 接口 + +```bash +# PCI 设备 sysfs 路径 +/sys/bus/pci/devices/0000:01:00.0/ +├── class # 设备类别 +├── config # 配置空间 (二进制) +├── device # 设备 ID +├── vendor # 厂商 ID +├── subsystem_device +├── subsystem_vendor +├── resource # 资源描述 +├── resource0 # BAR0 映射 +├── resource1 # BAR1 映射 +├── irq # IRQ 号 +├── enable # 设备使能 +├── driver -> ../../../bus/pci/drivers/nvme +├── msi_irqs/ # MSI 中断 +└── power/ # 电源管理 +``` + +--- + +## 10. 常见问题与调试 + +### 10.1 调试命令 + +```bash +# 查看内核 PCI 日志 +$ dmesg | grep -i pci + +# 查看设备详情 +$ lspci -vvvxxx -s 01:00.0 + +# 查看 BAR 资源 +$ cat /proc/iomem | grep -i pci + +# 查看中断分配 +$ cat /proc/interrupts | grep -i pci + +# PCIe 链路状态 +$ lspci -vvv -s 01:00.0 | grep -i "lnk" +``` + +### 10.2 常见问题 + +| 问题 | 可能原因 | 解决方案 | +|------|----------|----------| +| 设备未识别 | 硬件问题/BIOS设置 | 检查物理连接,更新BIOS | +| 驱动不加载 | ID 不匹配 | 检查 device_id 表 | +| DMA 失败 | DMA mask 设置错误 | 正确设置 dma_set_mask | +| 中断不工作 | MSI 配置问题 | 检查 MSI/MSI-X 设置 | +| 性能差 | PCIe 降速 | 检查 link speed/width | + +--- + +## 11. pci-utils 基本用法 + +**pciutils** (也写作 pci-utils) 是 Linux 下用于查看和操作 PCI/PCIe 设备的标准用户态工具集,由 Martin Mares 维护,是每一位内核开发和系统管理员的必备工具。 + +### 11.1 安装 + +```bash +# Debian / Ubuntu +$ sudo apt install pciutils + +# RHEL / CentOS / Fedora +$ sudo dnf install pciutils +# 或 +$ sudo yum install pciutils + +# Arch Linux +$ sudo pacman -S pciutils + +# 源码编译 +$ git clone https://github.com/pciutils/pciutils.git +$ cd pciutils +$ make && sudo make install +``` + +### 11.2 lspci — 列出 PCI 设备 + +`lspci` 是 pciutils 中最常用的命令,用于列出系统中所有 PCI/PCIe 设备信息。 + +#### 基本用法 + +```bash +# 列出所有 PCI 设备(简洁模式) +$ lspci +00:00.0 Host bridge: Intel Corporation 12th Gen Core Processor Host Bridge (rev 02) +00:02.0 VGA compatible controller: Intel Corporation Alder Lake-P GT2 (rev 0c) +00:1f.0 ISA bridge: Intel Corporation Alder Lake PCH eSPI Controller (rev 01) +01:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd NVMe SSD ... +02:00.0 Network controller: Intel Corporation Wi-Fi 6 AX201 (rev 20) +``` + +#### 常用选项 + +```bash +# -v / -vv / -vvv 逐步增加详细程度 +$ lspci -v # 简要详细信息 +$ lspci -vv # 更详细信息 +$ lspci -vvv # 最详细信息(包括所有 Capability 解析) + +# -s 指定设备(BDF 地址过滤) +$ lspci -s 01:00.0 # 查看特定设备 +$ lspci -s 01: # 查看总线 01 上的所有设备 +$ lspci -s :00.0 # 查看所有总线上 device=00, function=0 的设备 + +# -d 按 Vendor:Device ID 过滤 +$ lspci -d 8086: # 列出所有 Intel (0x8086) 设备 +$ lspci -d :1234 # 列出 Device ID 为 0x1234 的设备 +$ lspci -d 10de:* # 列出所有 NVIDIA (0x10de) 设备 + +# -t 树形拓扑显示 +$ lspci -tv +-[0000:00]-+-00.0 Intel Corporation 12th Gen Core Processor Host Bridge + +-02.0 Intel Corporation Alder Lake-P GT2 + +-1c.0-[01]----00.0 Samsung Electronics Co Ltd NVMe SSD + +-1c.4-[02]----00.0 Intel Corporation Wi-Fi 6 AX201 + \-1f.0 Intel Corporation Alder Lake PCH eSPI Controller + +# -n 显示数字格式的 Vendor/Device ID(不解析名称) +$ lspci -n +00:00.0 0600: 8086:4621 (rev 02) +01:00.0 0108: 144d:a80a + +# -nn 同时显示名称和数字 ID(最实用) +$ lspci -nn +00:00.0 Host bridge [0600]: Intel Corporation 12th Gen ... [8086:4621] (rev 02) +01:00.0 NVMe controller [0108]: Samsung Electronics [144d:a80a] + +# -k 显示设备使用的内核驱动和模块 +$ lspci -k +01:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd ... + Subsystem: Samsung Electronics Co Ltd ... + Kernel driver in use: nvme + Kernel modules: nvme + +# -x / -xx / -xxx / -xxxx 以十六进制 dump 配置空间 +$ lspci -x -s 01:00.0 # 标准配置空间 (前64字节) +$ lspci -xx -s 01:00.0 # 标准配置空间 (前256字节) +$ lspci -xxx -s 01:00.0 # 扩展配置空间 (需 root) +$ lspci -xxxx -s 01:00.0 # 完整 4KB 扩展配置空间 (需 root) + +# -D 始终显示 Domain 号 +$ lspci -D +0000:00:00.0 Host bridge: Intel Corporation ... +``` + +#### 组合使用示例 + +```bash +# 查看 NVMe SSD 完整信息 + PCIe 链路状态 +$ sudo lspci -vvv -s 01:00.0 + +# 输出中重点关注: +# LnkCap: Speed 8GT/s, Width x4 ← 设备支持的最大链路能力 +# LnkSta: Speed 8GT/s, Width x4 ← 当前实际链路状态 +# LnkCtl: ASPM L1 Enabled ← 电源管理状态 + +# 查看所有网络设备及其驱动 +$ lspci -nn -k -d ::0200 +02:00.0 Ethernet controller [0200]: Intel Corporation I210 [8086:1533] (rev 03) + Kernel driver in use: igb + +# 以机器可读格式输出(适合脚本解析) +$ lspci -mm +$ lspci -vmm # 每个字段独立一行 +$ lspci -vmm -s 01:00.0 +Slot: 01:00.0 +Class: Non-Volatile memory controller +Vendor: Samsung Electronics Co Ltd +Device: NVMe SSD Controller ... +``` + +### 11.3 setpci — 读写 PCI 配置空间 + +`setpci` 用于直接读取或写入 PCI 设备的配置空间寄存器,是底层调试和硬件配置的利器。 + +> **警告**: 错误使用 `setpci` 可能导致系统不稳定甚至硬件损坏,请谨慎操作。 + +#### 基本语法 + +```bash +setpci [选项] -s <寄存器>[.<宽度>][=<值>] + +# 宽度说明: +# .B = Byte (1字节) +# .W = Word (2字节) +# .L = Long (4字节, 默认) +``` + +#### 常用示例 + +```bash +# 读取 Vendor ID (偏移 0x00, 2字节) +$ sudo setpci -s 01:00.0 0x00.W +144d + +# 读取 Device ID (偏移 0x02, 2字节) +$ sudo setpci -s 01:00.0 0x02.W +a80a + +# 读取 Command 寄存器 (偏移 0x04, 2字节) +$ sudo setpci -s 01:00.0 COMMAND.W +0407 + +# 读取 Status 寄存器 +$ sudo setpci -s 01:00.0 STATUS.W +0010 + +# 读取 Class Code (偏移 0x08, 4字节,包含 revision) +$ sudo setpci -s 01:00.0 0x08.L +01080200 + +# 读取 BAR0 +$ sudo setpci -s 01:00.0 BASE_ADDRESS_0.L +a1200004 + +# 使用寄存器名称(更易读) +$ sudo setpci -s 01:00.0 VENDOR_ID.W +$ sudo setpci -s 01:00.0 DEVICE_ID.W +$ sudo setpci -s 01:00.0 CLASS_DEVICE.W + +# 写入 Command 寄存器(启用 Bus Master) +$ sudo setpci -s 01:00.0 COMMAND.W=0x0407 + +# 使用位掩码进行部分写入 +# 格式: 寄存器.宽度=值:掩码 +# 仅修改掩码为1的位 +$ sudo setpci -s 01:00.0 COMMAND.W=0004:0004 # 仅设置 Bus Master 位 + +# 读取 PCIe Capability 中的 Link Status +# 首先找到 PCIe Cap 偏移 (通常在 Capability List 中) +$ sudo setpci -s 01:00.0 CAP_EXP+12.W # Link Status Register +``` + +#### 寄存器名称速查 + +```bash +# setpci 支持的常用寄存器名称: +VENDOR_ID # 0x00 厂商 ID +DEVICE_ID # 0x02 设备 ID +COMMAND # 0x04 命令寄存器 +STATUS # 0x06 状态寄存器 +REVISION # 0x08 修订号 +CLASS_PROG # 0x09 编程接口 +CLASS_DEVICE # 0x0a 设备类别 +CACHE_LINE_SIZE # 0x0c 缓存行大小 +LATENCY_TIMER # 0x0d 延迟计时器 +HEADER_TYPE # 0x0e 头类型 +BASE_ADDRESS_0 # 0x10 BAR0 +BASE_ADDRESS_1 # 0x14 BAR1 +BASE_ADDRESS_2 # 0x18 BAR2 +BASE_ADDRESS_3 # 0x1c BAR3 +BASE_ADDRESS_4 # 0x20 BAR4 +BASE_ADDRESS_5 # 0x24 BAR5 +INTERRUPT_LINE # 0x3c 中断线 +INTERRUPT_PIN # 0x3d 中断引脚 + +# PCIe Capability 相关 (通过 CAP_EXP 前缀访问): +CAP_EXP+02.W # PCIe Capabilities Register +CAP_EXP+08.L # Device Capabilities +CAP_EXP+0c.W # Link Capabilities (低16位) +CAP_EXP+10.W # Link Control +CAP_EXP+12.W # Link Status +``` + +### 11.4 update-pciids — 更新 PCI ID 数据库 + +`lspci` 依赖 PCI ID 数据库来将数字 ID 解析为可读的厂商和设备名称。 + +```bash +# 更新本地 PCI ID 数据库(需要网络) +$ sudo update-pciids +Downloaded daily snapshot dated 2026-02-05 ... + +# 数据库文件位置 +$ ls -la /usr/share/misc/pci.ids* +# 或 +$ ls -la /usr/share/hwdata/pci.ids + +# 数据库来源: https://pci-ids.ucw.cz/ +``` + +### 11.5 实用调试场景 + +#### 场景一:排查 PCIe 链路降速 + +```bash +# 1. 查看设备的链路能力和当前状态 +$ sudo lspci -vvv -s 01:00.0 | grep -E "LnkCap|LnkSta" + LnkCap: Port #0, Speed 16GT/s, Width x4, ... + LnkSta: Speed 8GT/s (downgraded), Width x4 (ok), ... + +# 解读: 设备支持 PCIe 4.0 x4,但当前运行在 PCIe 3.0 x4 (降速) +# 可能原因: 主板限制、BIOS 设置、信号完整性问题 + +# 2. 同时检查上游端口 (Root Port 或 Switch Downstream Port) +$ sudo lspci -vvv -s 00:1c.0 | grep -E "LnkCap|LnkSta" +``` + +#### 场景二:检查 MSI/MSI-X 中断配置 + +```bash +# 查看设备的 MSI-X 信息 +$ sudo lspci -vvv -s 01:00.0 | grep -A5 "MSI-X" + Capabilities: [b0] MSI-X: Enable+ Count=128 Masked- + Vector table: BAR=0 offset=00002000 + PBA: BAR=0 offset=00003000 +``` + +#### 场景三:识别未知设备 + +```bash +# 1. 获取设备的 Vendor:Device ID +$ lspci -nn -s 03:00.0 +03:00.0 Unclassified device [00ff]: Unknown device [1234:5678] + +# 2. 在线查询 +# 访问 https://pci-ids.ucw.cz/ 输入 Vendor ID 和 Device ID 查询 + +# 3. 查看 subsystem ID 获取更多线索 +$ lspci -vvv -s 03:00.0 | grep -i subsystem +``` + +#### 场景四:导出设备信息用于 Bug 报告 + +```bash +# 完整导出所有 PCI 信息(适合附在 Bug 报告中) +$ sudo lspci -vvvnn > pci_info.txt + +# 导出完整配置空间十六进制 dump +$ sudo lspci -xxxx > pci_config_dump.txt + +# 导出机器可读格式 +$ lspci -vmm > pci_machine_readable.txt +``` + +### 11.6 pciutils 库 (libpci) 编程接口 + +pciutils 还提供了 C 语言库 `libpci`,可以在用户态程序中直接使用: + +```c +/* 使用 libpci 读取设备信息的示例 */ +#include +#include + +int main(void) +{ + struct pci_access *pacc; + struct pci_dev *dev; + char namebuf[1024]; + + /* 初始化 libpci */ + pacc = pci_alloc(); + pci_init(pacc); + pci_scan_bus(pacc); + + /* 遍历所有设备 */ + for (dev = pacc->devices; dev; dev = dev->next) { + pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | + PCI_FILL_CLASS | PCI_FILL_CAPS); + + printf("%04x:%02x:%02x.%d vendor=%04x device=%04x class=%04x\n", + dev->domain, dev->bus, dev->dev, dev->func, + dev->vendor_id, dev->device_id, dev->device_class); + + /* 获取可读名称 */ + printf(" %s\n", + pci_lookup_name(pacc, namebuf, sizeof(namebuf), + PCI_LOOKUP_DEVICE, + dev->vendor_id, dev->device_id)); + } + + pci_cleanup(pacc); + return 0; +} +``` + +编译方式: + +```bash +$ gcc -o pci_scan pci_scan.c -lpci +$ sudo ./pci_scan +``` + +--- + +## 参考资料 + +1. **PCI Express Base Specification** - PCI-SIG +2. **Linux Kernel Documentation** - `Documentation/PCI/` +3. **Linux Device Drivers, 3rd Edition** - O'Reilly +4. **Understanding Linux Network Internals** - O'Reilly +5. **pciutils 项目主页** - https://github.com/pciutils/pciutils +6. **PCI ID 数据库** - https://pci-ids.ucw.cz/ + +--- + +> **文档版本**: 1.1 +> **最后更新**: 2026年2月 diff --git a/Schedutil_Governor_Flow.md b/Schedutil_Governor_Flow.md new file mode 100644 index 00000000000000..46df0afddd175d --- /dev/null +++ b/Schedutil_Governor_Flow.md @@ -0,0 +1,1149 @@ +# Schedutil Governor 调频策略下发流程详解 + +> 基于 Linux 内核源码分析 +> 核心文件:`kernel/sched/cpufreq_schedutil.c` + +--- + +## 目录 + +1. [Schedutil 概述](#1-schedutil-概述) +2. [整体架构图](#2-整体架构图) +3. [触发调频的入口点](#3-触发调频的入口点) +4. [Schedutil 核心处理流程](#4-schedutil-核心处理流程) +5. [频率下发的两条路径](#5-频率下发的两条路径) +6. [关键函数接口详解](#6-关键函数接口详解) +7. [数据结构](#7-数据结构) +8. [频率计算公式](#8-频率计算公式) +9. [时序图](#9-时序图) +10. [调试与追踪](#10-调试与追踪) + +--- + +## 1. Schedutil 概述 + +**Schedutil** 是 Linux 内核中基于调度器提供的 CPU 利用率数据的 CPUFreq Governor。它与调度器紧密集成,能够快速响应负载变化。 + +### 1.1 核心特点 + +| 特性 | 说明 | +|------|------| +| **调度器集成** | 直接从 CFS/RT/DL 调度类获取利用率 | +| **PELT 驱动** | 使用 Per-Entity Load Tracking 数据 | +| **快速响应** | 支持 Fast Switch 模式,无需睡眠 | +| **频率不变性** | 支持 Frequency Invariant 计算 | +| **IO 等待加速** | 针对 IO 等待任务的频率提升 | + +### 1.2 代码位置 + +``` +kernel/sched/ +├── cpufreq_schedutil.c # Schedutil Governor 核心实现 +├── cpufreq.c # 调度器与 cpufreq 的接口 +├── fair.c # CFS 调度类 (调用 cpufreq_update_util) +├── rt.c # RT 调度类 +├── deadline.c # DL 调度类 +└── sched.h # cpufreq_update_util() 定义 +``` + +--- + +## 2. 整体架构图 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 调度器层 (Scheduler) │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ +│ │ CFS │ │ RT │ │ DL │ │ SCX │ │ +│ │ fair.c │ │ rt.c │ │deadline.c │ │ ext.c │ │ +│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ +│ │ │ │ │ │ +│ └────────────┬────┴─────────────────┴─────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────────────┐ │ +│ │ cpufreq_update_util() │ ← 调频触发入口 │ +│ │ (sched.h) │ │ +│ └────────────┬───────────────┘ │ +│ │ │ +└──────────────────────┼──────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Schedutil Governor 层 │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌────────────────────────────┐ │ +│ │ update_util callback │ │ +│ │ ┌─────────────────────┐ │ │ +│ │ │sugov_update_single_ │ │ ← 单 CPU Policy │ +│ │ │ freq/perf() │ │ │ +│ │ └─────────────────────┘ │ │ +│ │ ┌─────────────────────┐ │ │ +│ │ │sugov_update_shared()│ │ ← 多 CPU 共享 Policy │ +│ │ └─────────────────────┘ │ │ +│ └────────────┬───────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────────────┐ │ +│ │ get_next_freq() │ ← 计算目标频率 │ +│ │ - effective_cpu_util() │ │ +│ │ - map_util_freq() │ │ +│ └────────────┬───────────────┘ │ +│ │ │ +│ ┌───────────┴───────────┐ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ Fast Path │ │ Slow Path │ │ +│ │ (fast_switch) │ │ (kthread) │ │ +│ └────────┬────────┘ └────────┬────────┘ │ +│ │ │ │ +└────────────┼──────────────────────┼─────────────────────────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ CPUFreq Core 层 │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────────────┐ ┌────────────────────────────────┐ │ +│ │cpufreq_driver_fast_ │ │ __cpufreq_driver_target() │ │ +│ │ switch() │ │ │ │ +│ └────────────┬─────────────┘ └──────────────┬─────────────────┘ │ +│ │ │ │ +└────────────────┼──────────────────────────────────┼─────────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ CPUFreq Driver 层 │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ +│ │ intel_pstate │ │ amd_pstate │ │ acpi-cpufreq │ ... │ +│ │ .fast_switch │ │ .fast_switch │ │ .fast_switch │ │ +│ │ .target │ │ .target │ │ .target_index │ │ +│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │ +│ │ │ │ │ +└───────────┼──────────────────┼──────────────────┼───────────────────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Hardware (CPU) │ +│ P-State / OPP / DVFS Controller │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 3. 触发调频的入口点 + +调度器在以下场景会调用 `cpufreq_update_util()` 触发调频: + +### 3.1 触发场景 + +``` +┌───────────────────────────────────────────────────────────────────────────┐ +│ cpufreq_update_util() 触发场景 │ +├───────────────┬───────────────────────────────────────────────────────────┤ +│ 调度类 │ 触发点 │ +├───────────────┼───────────────────────────────────────────────────────────┤ +│ │ • enqueue_task_fair() - 任务入队 │ +│ CFS │ • dequeue_task_fair() - 任务出队 │ +│ (fair.c) │ • update_load_avg() - 负载更新 (PELT) │ +│ │ • task wakeup with SCHED_CPUFREQ_IOWAIT │ +├───────────────┼───────────────────────────────────────────────────────────┤ +│ RT │ • inc_rt_prio() / dec_rt_prio() │ +│ (rt.c) │ • RT 任务入队/出队时定期触发 │ +├───────────────┼───────────────────────────────────────────────────────────┤ +│ DL │ • __add_running_bw() / __sub_running_bw() │ +│ (deadline.c) │ • DL 任务带宽变化时触发 │ +├───────────────┼───────────────────────────────────────────────────────────┤ +│ SCX │ • scx_set_cpuperf() │ +│ (ext.c) │ • BPF 调度器设置性能目标 │ +└───────────────┴───────────────────────────────────────────────────────────┘ +``` + +### 3.2 cpufreq_update_util() 实现 + +```c +/* kernel/sched/sched.h */ +static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) +{ + struct update_util_data *data; + + /* 获取当前 CPU 注册的回调数据 */ + data = rcu_dereference_sched(*per_cpu_ptr(&cpufreq_update_util_data, + cpu_of(rq))); + if (data) + /* 调用 schedutil 注册的回调函数 */ + data->func(data, rq_clock(rq), flags); +} +``` + +### 3.3 Flags 参数 + +```c +/* include/linux/sched/cpufreq.h */ +#define SCHED_CPUFREQ_IOWAIT (1U << 0) /* IO 等待唤醒,需要 boost */ +``` + +--- + +## 4. Schedutil 核心处理流程 + +### 4.1 Governor 初始化流程 + +``` +sugov_init() # Governor 初始化 + │ + ├── cpufreq_enable_fast_switch() # 尝试启用快速切换 + │ + ├── sugov_policy_alloc() # 分配 policy 数据结构 + │ + ├── sugov_kthread_create() # 创建工作线程 (slow path) + │ │ + │ └── kthread_create("sugov:%d") + │ sched_setattr(SCHED_DEADLINE) # 设为 DL 调度类 + │ + └── sugov_tunables_alloc() # 分配可调参数 + │ + └── rate_limit_us # 调频速率限制 + + +sugov_start() # Governor 启动 + │ + ├── 选择 update 回调函数: + │ ├── policy_is_shared() → sugov_update_shared() + │ ├── fast_switch + perf → sugov_update_single_perf() + │ └── 其他 → sugov_update_single_freq() + │ + └── cpufreq_add_update_util_hook() # 注册回调到调度器 + │ + └── 设置 per_cpu(cpufreq_update_util_data, cpu) +``` + +### 4.2 主处理流程 (单 CPU Policy) + +``` +sugov_update_single_freq() # 单 CPU Policy 的更新入口 + │ + ├── sugov_update_single_common() + │ │ + │ ├── sugov_iowait_boost() # 处理 IO 等待 boost + │ │ + │ ├── sugov_should_update_freq() # 检查是否需要更新 + │ │ │ + │ │ ├── 检查 rate_limit + │ │ └── 检查 limits_changed + │ │ + │ ├── sugov_iowait_apply() # 应用 IO boost + │ │ + │ └── sugov_get_util() # 获取 CPU 利用率 + │ │ + │ ├── cpu_util_cfs_boost() # CFS 利用率 + │ ├── effective_cpu_util() # 综合利用率 + │ │ │ + │ │ ├── cpu_util_rt() # RT 利用率 + │ │ ├── cpu_util_dl() # DL 利用率 + │ │ └── cpu_util_irq() # IRQ 利用率 + │ │ + │ └── sugov_effective_cpu_perf() # 添加 headroom + │ + ├── get_next_freq() # 计算目标频率 + │ │ + │ ├── get_capacity_ref_freq() # 获取参考频率 + │ ├── map_util_freq() # 利用率映射到频率 + │ └── cpufreq_driver_resolve_freq() # 解析到实际频率 + │ + ├── sugov_hold_freq() # 检查是否保持频率 + │ + ├── sugov_update_next_freq() # 更新下一个频率 + │ + └── 频率下发: + │ + ├── [Fast Path] cpufreq_driver_fast_switch() + │ + └── [Slow Path] sugov_deferred_update() + └── irq_work_queue() +``` + +--- + +## 5. 频率下发的两条路径 + +### 5.1 Fast Path (快速路径) + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Fast Path 流程 │ +│ (适用于支持 fast_switch 的驱动) │ +└─────────────────────────────────────────────────────────────────────────┘ + + sugov_update_single_freq() / sugov_update_shared() + │ + │ policy->fast_switch_enabled == true + │ + ▼ + ┌─────────────────────────────────────┐ + │ cpufreq_driver_fast_switch() │ ← CPUFreq Core + │ (drivers/cpufreq/cpufreq.c) │ + └──────────────┬──────────────────────┘ + │ + │ target_freq = clamp(freq, min, max) + │ + ▼ + ┌─────────────────────────────────────┐ + │ cpufreq_driver->fast_switch() │ ← Driver Callback + │ │ + │ 例如: │ + │ - intel_cpufreq_fast_switch() │ + │ - amd_pstate_fast_switch() │ + │ - acpi_cpufreq_fast_switch() │ + └──────────────┬──────────────────────┘ + │ + │ 直接写入硬件寄存器 (无睡眠) + │ + ▼ + ┌─────────────────────────────────────┐ + │ arch_set_freq_scale() │ ← 更新频率缩放因子 + │ cpufreq_stats_record_transition() │ ← 记录统计信息 + │ trace_cpu_frequency() │ ← 触发 trace event + └─────────────────────────────────────┘ + + 优点: 延迟低 (<1us), 在调度器上下文直接执行 + 缺点: 需要硬件/驱动支持 +``` + +### 5.2 Slow Path (慢速路径) + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Slow Path 流程 │ +│ (适用于不支持 fast_switch 的驱动) │ +└─────────────────────────────────────────────────────────────────────────┘ + + sugov_update_single_freq() / sugov_update_shared() + │ + │ policy->fast_switch_enabled == false + │ + ▼ + ┌─────────────────────────────────────┐ + │ sugov_deferred_update() │ ← 延迟更新 + │ │ + │ if (!work_in_progress) { │ + │ work_in_progress = true; │ + │ irq_work_queue(&irq_work); │ + │ } │ + └──────────────┬──────────────────────┘ + │ + │ IRQ Work 机制 (在中断上下文安全执行) + │ + ▼ + ┌─────────────────────────────────────┐ + │ sugov_irq_work() │ ← IRQ Work Handler + │ │ + │ kthread_queue_work(&worker, │ + │ &work); │ + └──────────────┬──────────────────────┘ + │ + │ 唤醒 kthread (SCHED_DEADLINE) + │ + ▼ + ┌─────────────────────────────────────┐ + │ sugov_work() │ ← Kthread Worker + │ │ + │ mutex_lock(&work_lock); │ + │ __cpufreq_driver_target(policy, │ + │ freq, │ + │ RELATION);│ + │ mutex_unlock(&work_lock); │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ __cpufreq_driver_target() │ ← CPUFreq Core + │ (drivers/cpufreq/cpufreq.c) │ + │ │ + │ → cpufreq_driver->target() 或 │ + │ → cpufreq_driver->target_index() │ + └──────────────┬──────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ Driver 具体实现 │ + │ (可能涉及 ACPI/固件调用, 可睡眠) │ + └─────────────────────────────────────┘ + + 优点: 兼容所有驱动 + 缺点: 延迟较高 (需要上下文切换) +``` + +### 5.3 路径选择逻辑 + +```c +/* kernel/sched/cpufreq_schedutil.c: sugov_start() */ +if (policy_is_shared(policy)) + uu = sugov_update_shared; /* 多 CPU 共享 policy */ +else if (policy->fast_switch_enabled && cpufreq_driver_has_adjust_perf()) + uu = sugov_update_single_perf; /* 性能级别直接调整 */ +else + uu = sugov_update_single_freq; /* 标准频率调整 */ + +/* 在 sugov_update_single_freq() 中选择路径 */ +if (sg_policy->policy->fast_switch_enabled) { + cpufreq_driver_fast_switch(policy, next_f); /* Fast Path */ +} else { + sugov_deferred_update(sg_policy); /* Slow Path */ +} +``` + +--- + +## 6. 关键函数接口详解 + +### 6.1 调度器层接口 + +| 函数 | 文件 | 说明 | +|------|------|------| +| `cpufreq_update_util()` | sched.h | 调频触发入口,由调度器调用 | +| `cpufreq_add_update_util_hook()` | cpufreq.c | 注册 governor 回调 | +| `cpufreq_remove_update_util_hook()` | cpufreq.c | 移除 governor 回调 | +| `cpufreq_this_cpu_can_update()` | cpufreq.c | 检查当前 CPU 能否更新频率 | + +### 6.2 Schedutil Governor 接口 + +| 函数 | 说明 | +|------|------| +| **回调函数** | | +| `sugov_update_single_freq()` | 单 CPU Policy,基于频率的更新 | +| `sugov_update_single_perf()` | 单 CPU Policy,基于性能级别的更新 | +| `sugov_update_shared()` | 多 CPU 共享 Policy 的更新 | +| **利用率计算** | | +| `sugov_get_util()` | 获取 CPU 综合利用率 | +| `effective_cpu_util()` | 计算有效 CPU 利用率 (CFS+RT+DL+IRQ) | +| `sugov_effective_cpu_perf()` | 添加 DVFS headroom | +| **频率计算** | | +| `get_next_freq()` | 根据利用率计算目标频率 | +| `get_capacity_ref_freq()` | 获取参考频率 | +| `map_util_freq()` | 利用率到频率的映射 | +| **更新控制** | | +| `sugov_should_update_freq()` | 检查是否应该更新频率 | +| `sugov_update_next_freq()` | 确认并更新下一个频率 | +| `sugov_deferred_update()` | 延迟更新 (slow path) | +| **IO Boost** | | +| `sugov_iowait_boost()` | 处理 IO 等待 boost 请求 | +| `sugov_iowait_apply()` | 应用 IO boost | +| `sugov_iowait_reset()` | 重置 IO boost 状态 | +| **Governor 生命周期** | | +| `sugov_init()` | Governor 初始化 | +| `sugov_exit()` | Governor 退出 | +| `sugov_start()` | Governor 启动 | +| `sugov_stop()` | Governor 停止 | +| `sugov_limits()` | 频率限制变更处理 | + +### 6.3 CPUFreq Core 接口 + +| 函数 | 文件 | 说明 | +|------|------|------| +| `cpufreq_driver_fast_switch()` | cpufreq.c | 快速频率切换 | +| `cpufreq_driver_adjust_perf()` | cpufreq.c | 直接性能级别调整 | +| `__cpufreq_driver_target()` | cpufreq.c | 慢速频率切换 | +| `cpufreq_driver_resolve_freq()` | cpufreq.c | 解析到实际可用频率 | + +### 6.4 Driver 层回调接口 + +```c +struct cpufreq_driver { + /* Fast Path - 不可睡眠 */ + unsigned int (*fast_switch)(struct cpufreq_policy *policy, + unsigned int target_freq); + + /* 性能级别调整 - 不可睡眠 */ + void (*adjust_perf)(unsigned int cpu, + unsigned long min_perf, + unsigned long target_perf, + unsigned long capacity); + + /* Slow Path - 可睡眠 */ + int (*target)(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); + + int (*target_index)(struct cpufreq_policy *policy, + unsigned int index); + + /* ... */ +}; +``` + +--- + +## 7. 数据结构 + +### 7.1 sugov_policy + +```c +/* kernel/sched/cpufreq_schedutil.c */ +struct sugov_policy { + struct cpufreq_policy *policy; /* 关联的 cpufreq policy */ + + struct sugov_tunables *tunables; /* 可调参数 */ + struct list_head tunables_hook; + + raw_spinlock_t update_lock; /* 更新锁 */ + u64 last_freq_update_time; /* 上次更新时间 */ + s64 freq_update_delay_ns; /* rate_limit 转换值 */ + unsigned int next_freq; /* 下一个目标频率 */ + unsigned int cached_raw_freq; /* 缓存的原始频率 */ + + /* Slow Path 相关 */ + struct irq_work irq_work; /* IRQ work */ + struct kthread_work work; /* kthread work */ + struct mutex work_lock; + struct kthread_worker worker; + struct task_struct *thread; /* sugov kthread */ + bool work_in_progress; /* 工作进行中标志 */ + + bool limits_changed; /* 限制变更标志 */ + bool need_freq_update; /* 需要强制更新 */ +}; +``` + +### 7.2 sugov_cpu + +```c +struct sugov_cpu { + struct update_util_data update_util; /* 回调数据 */ + struct sugov_policy *sg_policy; /* 所属 policy */ + unsigned int cpu; /* CPU 编号 */ + + /* IO Boost */ + bool iowait_boost_pending; /* IO boost 待处理 */ + unsigned int iowait_boost; /* 当前 IO boost 值 */ + u64 last_update; /* 上次更新时间戳 */ + + /* 利用率 */ + unsigned long util; /* 计算后的利用率 */ + unsigned long bw_min; /* 最小带宽需求 (DL) */ + +#ifdef CONFIG_NO_HZ_COMMON + unsigned long saved_idle_calls; /* 保存的 idle 调用计数 */ +#endif +}; +``` + +### 7.3 sugov_tunables + +```c +struct sugov_tunables { + struct gov_attr_set attr_set; + unsigned int rate_limit_us; /* 调频速率限制 (微秒) */ +}; +``` + +### 7.4 update_util_data + +```c +/* include/linux/sched/cpufreq.h */ +struct update_util_data { + void (*func)(struct update_util_data *data, u64 time, unsigned int flags); +}; +``` + +--- + +## 8. 频率计算公式 + +### 8.1 利用率到频率的映射 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 频率计算公式 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 基本公式 (频率不变性): │ +│ │ +│ util │ +│ next_freq = 1.25 × ────── × max_freq │ +│ max │ +│ │ +│ 其中: │ +│ - util: 当前 CPU 利用率 (0 ~ SCHED_CAPACITY_SCALE) │ +│ - max: CPU 最大算力 (arch_scale_cpu_capacity) │ +│ - max_freq: CPU 最大频率 │ +│ - 1.25: headroom 系数,使 80% 利用率时就达到最高频 │ +│ │ +│ 代码实现: │ +│ ```c │ +│ /* kernel/sched/cpufreq_schedutil.c */ │ +│ freq = get_capacity_ref_freq(policy); /* max_freq */ │ +│ freq = map_util_freq(util, freq, max); /* 1.25 * util/max * freq */│ +│ ``` │ +│ │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 综合利用率计算: │ +│ │ +│ util_cfs = cpu_util_cfs() + util_est │ +│ │ +│ util = clamp(util_cfs + util_rt, uclamp_min, uclamp_max) + irq + dl │ +│ │ +│ effective_util = util * (1 + headroom) │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 8.2 map_util_freq 实现 + +```c +/* include/linux/sched/cpufreq.h */ +static inline unsigned long map_util_freq(unsigned long util, + unsigned long freq, + unsigned long cap) +{ + return freq * util / cap; /* 基本映射 */ +} + +/* kernel/sched/cpufreq_schedutil.c */ +/* 在 sugov_effective_cpu_perf() 中添加 headroom */ +actual = map_util_perf(actual); /* actual + (actual >> 2) = 1.25 * actual */ +``` + +### 8.3 IO Wait Boost + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ IO Wait Boost 机制 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ IO 等待唤醒时的频率提升策略: │ +│ │ +│ ┌────────────────┐ │ +│ │ 首次 IO 唤醒 │ → iowait_boost = IOWAIT_BOOST_MIN (12.5%) │ +│ └───────┬────────┘ │ +│ │ 在 1 tick 内再次 IO 唤醒 │ +│ ▼ │ +│ ┌────────────────┐ │ +│ │ boost 翻倍 │ → iowait_boost = min(boost * 2, 100%) │ +│ └───────┬────────┘ │ +│ │ 超过 1 tick 无 IO 唤醒 │ +│ ▼ │ +│ ┌────────────────┐ │ +│ │ boost 衰减 │ → iowait_boost = boost / 2 │ +│ └───────┬────────┘ │ +│ │ boost < IOWAIT_BOOST_MIN │ +│ ▼ │ +│ ┌────────────────┐ │ +│ │ boost 清零 │ → iowait_boost = 0 │ +│ └────────────────┘ │ +│ │ +│ IOWAIT_BOOST_MIN = SCHED_CAPACITY_SCALE / 8 = 128 (12.5%) │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 9. 时序图 + +### 9.1 完整调频时序 (Fast Path) + +``` + Scheduler Schedutil CPUFreq Core Driver + │ │ │ │ + │ cpufreq_update_util() │ │ + │─────────────────────►│ │ │ + │ │ │ │ + │ │ sugov_update_single_freq() │ + │ │───────┐ │ │ + │ │ │ │ │ + │ │◄──────┘ │ │ + │ │ get_next_freq() │ │ + │ │───────┐ │ │ + │ │ │ │ │ + │ │◄──────┘ │ │ + │ │ │ │ + │ │ cpufreq_driver_fast_switch() │ + │ │──────────────────────►│ │ + │ │ │ │ + │ │ │ ->fast_switch() │ + │ │ │───────────────────►│ + │ │ │ │ + │ │ │ │ 写硬件寄存器 + │ │ │ │───────┐ + │ │ │ │ │ + │ │ │ │◄──────┘ + │ │ │ new_freq │ + │ │ │◄───────────────────│ + │ │ │ │ + │ │ │ arch_set_freq_scale() + │ │ │───────┐ │ + │ │ │ │ │ + │ │ │◄──────┘ │ + │ │ freq │ │ + │◄─────────────────────│◄──────────────────────│ │ + │ │ │ │ + + 延迟: < 1 微秒 (典型值) +``` + +### 9.2 完整调频时序 (Slow Path) + +``` + Scheduler Schedutil IRQ Context Kthread Driver + │ │ │ │ │ + │ cpufreq_ │ │ │ │ + │ update_util() │ │ │ │ + │───────────────►│ │ │ │ + │ │ │ │ │ + │ │ sugov_deferred_ │ │ │ + │ │ update() │ │ │ + │ │────────┐ │ │ │ + │ │ │ │ │ │ + │ │◄───────┘ │ │ │ + │ │ │ │ │ + │ │ irq_work_queue() │ │ │ + │ │───────────────────►│ │ │ + │ │ │ │ │ + │◄───────────────│ │ │ │ + │ 返回调度器 │ │ │ │ + │ │ │ │ │ + │ │ │ sugov_irq_ │ │ + │ │ │ work() │ │ + │ │ │─────────────►│ │ + │ │ │ │ │ + │ │ │ │ sugov_work() │ + │ │ │ │───────┐ │ + │ │ │ │ │ │ + │ │ │ │◄──────┘ │ + │ │ │ │ │ + │ │ │ │ __cpufreq_ │ + │ │ │ │ driver_target │ + │ │ │ │──────────────►│ + │ │ │ │ │ + │ │ │ │ │ 设置频率 + │ │ │ │ │─────┐ + │ │ │ │ │ │ + │ │ │ │ │◄────┘ + │ │ │ │◄──────────────│ + │ │ │ │ │ + + 延迟: 数十到数百微秒 (取决于调度延迟) +``` + +--- + +## 10. 调试与追踪 + +### 10.1 sysfs 接口 + +```bash +# 查看/设置 governor +cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor +echo schedutil > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + +# schedutil 可调参数 +cat /sys/devices/system/cpu/cpufreq/policy0/schedutil/rate_limit_us +echo 1000 > /sys/devices/system/cpu/cpufreq/policy0/schedutil/rate_limit_us + +# 当前频率 +cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq + +# 频率范围 +cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq +cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq +``` + +### 10.2 Trace Events + +```bash +# 启用 cpu_frequency trace +echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable + +# 查看 trace +cat /sys/kernel/debug/tracing/trace + +# 或使用 trace-cmd +trace-cmd record -e power:cpu_frequency -e sched:sched_switch sleep 1 +trace-cmd report +``` + +### 10.3 关键 Trace Points + +```c +/* 频率变更 trace */ +trace_cpu_frequency(freq, cpu); + +/* 可以添加自定义调试 */ +pr_debug("schedutil: cpu %d, util %lu, freq %u\n", cpu, util, next_freq); +``` + +### 10.4 调试技巧 + +```bash +# 检查 fast_switch 是否启用 +cat /sys/devices/system/cpu/cpufreq/policy0/fast_switch_enabled + +# 查看 schedutil kthread +ps aux | grep sugov + +# 查看 kthread 调度属性 (应该是 SCHED_DEADLINE) +chrt -p $(pgrep -f "sugov:0") + +# 监控频率变化 +watch -n 0.1 "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq" +``` + +--- + +## 11. CPPC 调频流程 + +### 11.1 CPPC 概述 + +**CPPC (Collaborative Processor Performance Control)** 是 ACPI 规范定义的 CPU 性能控制接口,主要用于 ARM64 服务器和部分 x86 平台。CPPC 调频 **完全复用 schedutil 的框架**,只是在驱动层有不同的实现。 + +### 11.2 CPPC 与 Schedutil 的关系 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ CPPC 调频完整流程 │ +└─────────────────────────────────────────────────────────────────────────┘ + + 与其他驱动完全相同 + │ + ┌──────────────────────────┼──────────────────────────┐ + │ │ │ + │ 调度器层 │ │ + │ cpufreq_update_util() │ │ + │ │ │ │ + │ ▼ │ │ + │ Schedutil Governor │ ← 相同的 Governor │ + │ sugov_update_single_*() │ │ + │ │ │ │ + │ ▼ │ │ + │ get_next_freq() │ ← 相同的频率计算 │ + │ │ │ │ + └─────────────┼────────────┘ │ + │ │ + ┌─────────────┼────────────┐ │ + │ ▼ │ │ + │ CPUFreq Core │ │ + │ cpufreq_driver_fast_ │ │ + │ switch() │ │ + │ │ │ │ + └─────────────┼────────────┘ │ + │ │ + ╔═════════════╪════════════╗ │ + ║ ▼ ║ ← CPPC 特有部分 │ + ║ CPPC Driver 层 ║ │ + ║ cppc_cpufreq_fast_ ║ │ + ║ switch() ║ │ + ║ │ ║ │ + ║ ▼ ║ │ + ║ cppc_set_perf() ║ ← ACPI CPPC 接口 │ + ║ │ ║ │ + ║ ▼ ║ │ + ║ 写入 CPPC 寄存器 ║ ← 系统内存/PCC │ + ╚══════════════════════════╝ │ + │ +``` + +### 11.3 CPPC Driver 关键接口 + +```c +/* drivers/cpufreq/cppc_cpufreq.c */ + +static struct cpufreq_driver cppc_cpufreq_driver = { + .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS, + .verify = cppc_verify_policy, + .target = cppc_cpufreq_set_target, /* Slow Path */ + .get = cppc_cpufreq_get_rate, + .fast_switch = cppc_cpufreq_fast_switch, /* Fast Path */ + .init = cppc_cpufreq_cpu_init, + .exit = cppc_cpufreq_cpu_exit, + .set_boost = cppc_cpufreq_set_boost, + .name = "cppc_cpufreq", +}; +``` + +### 11.4 Fast Switch 条件 + +CPPC 是否支持 fast_switch 取决于 **DESIRED_PERF 寄存器的位置**: + +```c +/* drivers/acpi/cppc_acpi.c */ +bool cppc_allow_fast_switch(void) +{ + struct cpc_register_resource *desired_reg; + struct cpc_desc *cpc_ptr; + int cpu; + + for_each_online_cpu(cpu) { + cpc_ptr = per_cpu(cpc_desc_ptr, cpu); + desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF]; + + /* 只有寄存器在系统内存或系统 I/O 时才支持 fast_switch */ + if (!CPC_IN_SYSTEM_MEMORY(desired_reg) && + !CPC_IN_SYSTEM_IO(desired_reg)) + return false; + } + return true; +} +``` + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ CPPC 寄存器位置与路径选择 │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ DESIRED_PERF 寄存器位置 → 调频路径 │ +│ ───────────────────────────────────────────────────────── │ +│ System Memory (MMIO) → Fast Path ✓ │ +│ System I/O (Port I/O) → Fast Path ✓ │ +│ PCC (Platform Communication → Slow Path ✗ │ +│ Channel) (需要固件交互) │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 11.5 CPPC Fast Path 实现 + +```c +/* drivers/cpufreq/cppc_cpufreq.c */ +static unsigned int cppc_cpufreq_fast_switch(struct cpufreq_policy *policy, + unsigned int target_freq) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + unsigned int cpu = policy->cpu; + u32 desired_perf; + int ret; + + /* 1. 频率转换为 CPPC 性能级别 */ + desired_perf = cppc_khz_to_perf(&cpu_data->perf_caps, target_freq); + cpu_data->perf_ctrls.desired_perf = desired_perf; + + /* 2. 调用 ACPI CPPC 接口设置性能 */ + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); + + if (ret) { + pr_debug("Failed to set target on CPU:%d. ret:%d\n", cpu, ret); + return 0; + } + return target_freq; +} +``` + +### 11.6 CPPC Slow Path 实现 + +```c +/* drivers/cpufreq/cppc_cpufreq.c */ +static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + unsigned int cpu = policy->cpu; + struct cpufreq_freqs freqs; + int ret = 0; + + /* 1. 频率转换为性能级别 */ + cpu_data->perf_ctrls.desired_perf = + cppc_khz_to_perf(&cpu_data->perf_caps, target_freq); + + freqs.old = policy->cur; + freqs.new = target_freq; + + /* 2. 频率转换通知 (开始) */ + cpufreq_freq_transition_begin(policy, &freqs); + + /* 3. 调用 ACPI CPPC 接口 */ + ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); + + /* 4. 频率转换通知 (结束) */ + cpufreq_freq_transition_end(policy, &freqs, ret != 0); + + return ret; +} +``` + +### 11.7 cppc_set_perf() 底层实现 + +```c +/* drivers/acpi/cppc_acpi.c */ +int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *desired_reg, *min_perf_reg, *max_perf_reg; + + /* 获取 CPPC 寄存器描述 */ + desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; + min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF]; + max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF]; + + /* 如果寄存器在 PCC 中,需要获取锁 */ + if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || + CPC_IN_PCC(max_perf_reg)) { + down_read(&pcc_ss_data->pcc_lock); /* Phase-I */ + /* ... 检查 PCC 通道归属 ... */ + } + + /* 写入性能寄存器 */ + cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); + if (perf_ctrls->min_perf) + cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf); + if (perf_ctrls->max_perf) + cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf); + + /* 如果在 PCC 中,需要 doorbell 通知平台 */ + if (CPC_IN_PCC(desired_reg)) { + /* Phase-II: 批量发送 PCC 命令 */ + send_pcc_cmd(pcc_ss_id, CMD_WRITE); + } + + return ret; +} +``` + +### 11.8 CPPC 调频完整调用链 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ CPPC Fast Path 完整调用链 │ +└─────────────────────────────────────────────────────────────────────────┘ + +[调度器上下文] +update_load_avg() / enqueue_task_fair() / ... + └── cpufreq_update_util(rq, flags) + └── sugov_update_single_freq() ← Schedutil + ├── sugov_get_util() + │ └── effective_cpu_util() + ├── get_next_freq() + │ └── map_util_freq() + └── cpufreq_driver_fast_switch() ← CPUFreq Core + └── cppc_cpufreq_fast_switch() ← CPPC Driver + ├── cppc_khz_to_perf() # 频率→性能转换 + └── cppc_set_perf() ← ACPI CPPC + └── cpc_write() # 写入寄存器 + └── [MMIO/PIO 写操作] + + +┌─────────────────────────────────────────────────────────────────────────┐ +│ CPPC Slow Path (PCC) 完整调用链 │ +└─────────────────────────────────────────────────────────────────────────┘ + +[调度器上下文] +cpufreq_update_util() + └── sugov_update_single_freq() + └── sugov_deferred_update() + └── irq_work_queue() + +[IRQ 上下文] +sugov_irq_work() + └── kthread_queue_work() + +[Kthread 上下文] +sugov_work() + └── __cpufreq_driver_target() + └── cppc_cpufreq_set_target() ← CPPC Driver + ├── cppc_khz_to_perf() + ├── cpufreq_freq_transition_begin() + ├── cppc_set_perf() ← ACPI CPPC + │ ├── down_read(&pcc_lock) # PCC Phase-I + │ ├── cpc_write() # 写入 PCC 缓冲区 + │ ├── up_read(&pcc_lock) + │ ├── down_write(&pcc_lock) # PCC Phase-II + │ ├── send_pcc_cmd(CMD_WRITE) # 发送 Doorbell + │ └── up_write(&pcc_lock) + └── cpufreq_freq_transition_end() +``` + +### 11.9 CPPC vs 其他驱动对比 + +| 特性 | CPPC | Intel P-State | ACPI CPUFreq | +|------|------|---------------|--------------| +| **标准** | ACPI CPPC | Intel HWP | ACPI P-States | +| **平台** | ARM64/x86 | Intel | x86 (AMD/Intel) | +| **fast_switch** | 条件支持 | 支持 | 支持 | +| **性能表示** | 性能级别 | 性能百分比 | 频率 | +| **硬件接口** | 系统内存/PCC | MSR | MSR/MMIO | +| **固件交互** | 可能 (PCC) | 无 | 无 | + +### 11.10 调试 CPPC + +```bash +# 检查 CPPC 驱动是否加载 +lsmod | grep cppc + +# 查看 CPPC 相关的 sysfs +ls /sys/devices/system/cpu/cpu0/cpufreq/ + +# 查看 CPPC 特有属性 +cat /sys/devices/system/cpu/cpu0/cpufreq/freqdomain_cpus +cat /sys/devices/system/cpu/cpu0/cpufreq/auto_select +cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference_val + +# 检查 fast_switch 是否启用 +cat /sys/devices/system/cpu/cpufreq/policy0/fast_switch_enabled + +# 查看 ACPI CPPC 信息 +cat /sys/firmware/acpi/tables/CPPC # 如果存在 + +# 内核日志 +dmesg | grep -i cppc +``` + +--- + +## 附录:调用链总结 + +### Fast Path 完整调用链 + +``` +[调度器上下文] +update_load_avg() / enqueue_task_fair() / ... + └── cpufreq_update_util(rq, flags) + └── data->func() [sugov_update_single_freq] + ├── sugov_update_single_common() + │ ├── sugov_iowait_boost() + │ ├── sugov_should_update_freq() + │ ├── sugov_iowait_apply() + │ └── sugov_get_util() + │ └── effective_cpu_util() + ├── get_next_freq() + │ ├── get_capacity_ref_freq() + │ ├── map_util_freq() + │ └── cpufreq_driver_resolve_freq() + ├── sugov_update_next_freq() + └── cpufreq_driver_fast_switch() + └── cpufreq_driver->fast_switch() + └── [写入硬件寄存器] +``` + +### Slow Path 完整调用链 + +``` +[调度器上下文] +cpufreq_update_util() + └── sugov_update_single_freq() + └── sugov_deferred_update() + └── irq_work_queue(&sg_policy->irq_work) + +[IRQ 上下文] +sugov_irq_work() + └── kthread_queue_work(&sg_policy->worker, &sg_policy->work) + +[Kthread 上下文 - SCHED_DEADLINE] +sugov_work() + └── __cpufreq_driver_target() + ├── cpufreq_driver->target() + └── cpufreq_driver->target_index() + └── [调用 ACPI/固件设置频率] +``` + +--- + +> **文档版本**: 1.0 +> **最后更新**: 2026年1月 +> **基于内核版本**: Linux 6.19-rc7