拓冰建站拓冰建站
首页 / 资讯中心 / 正文

page_alloc pcpu_spin_lock/_trylock/_unlock

通用宏的特化封装再加上两个 NUMA 相关的全局定义。一、PCP 专用宏对通用宏的薄封装/* struct per_cpu_pages specific helpers. */ #define pcp_spin_lock(ptr) \ pcpu_spin_lock(struct per_cpu_pages, lock, ptr) #define pcp_spin_trylock(ptr) \ pcpu_spin_trylock(struct per_cpu_pages, lock, ptr) #define pcp_spin_unlock(ptr) \ pcpu_spin_unlock(lock, ptr)作用把前面通用的pcpu_spin_*宏绑定到struct per_cpu_pages的lock成员调用方不用每次都写struct per_cpu_pages和lock这两个参数。参数固定化通用宏参数特化后固定为typestruct per_cpu_pagesmemberlockptr仍然由调用方传入per-CPU 变量指针展开对比pcp_spin_lock(zone-per_cpu_pageset) // 展开为 pcpu_spin_lock(struct per_cpu_pages, lock, zone-per_cpu_pageset) // 再展开为 ({ struct per_cpu_pages *_ret; pcpu_task_pin(); _ret this_cpu_ptr(zone-per_cpu_pageset); spin_lock(_ret-lock); _ret; })调用示例struct per_cpu_pages *pcp; pcp pcp_spin_lock(zone-per_cpu_pageset); /* 此时已 pin 锁住当前 CPU 的 pcp */ pcp-count--; ... pcp_spin_unlock(pcp);trylock 版本同样pcp pcp_spin_trylock(zone-per_cpu_pageset); if (!pcp) { /* 没拿到锁 */ } ... pcp_spin_unlock(pcp);设计意图减少重复PCP 操作非常频繁每次都写全参数冗长且易错。语义清晰pcp_spin_lock一眼看出是锁 PCP比通用宏更直观。保持灵活性底层还是通用宏若将来 PCP 锁语义变化只需改通用宏或这层薄封装。二、CONFIG_USE_PERCPU_NUMA_NODE_ID下的 NUMA 节点 ID#ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID DEFINE_PER_CPU(int, numa_node); EXPORT_PER_CPU_SYMBOL(numa_node); #endif背景为什么要 per-CPU 的 numa_node通常情况下一个 CPU 属于哪个 NUMA 节点是静态的由 ACPI/SRAT 等固件信息决定可以用一张静态表如cpu_to_node[]查。但在某些平台上CPU 到 NUMA 节点的映射可能动态变化或查询静态表开销较大如跨节点访问表本身。这时就用每个 CPU 一个numa_node变量来缓存我这个 CPU 属于哪个节点。配置项CONFIG_USE_PERCPU_NUMA_NODE_ID当该配置开启时内核用 per-CPU 变量存节点 ID。关闭时节点 ID 通过其他方式如静态表cpu_to_node获取不占 per-CPU 空间。DEFINE_PER_CPU(int, numa_node)定义每个 CPU 一份int类型的numa_node。每个 CPU 访问自己的那份时用this_cpu_read(numa_node)或per_cpu(numa_node, cpu)。在 PCP 分配路径中常用来判断从哪个 NUMA 节点分配保证本地性。EXPORT_PER_CPU_SYMBOL(numa_node)把numa_node导出给可加载模块使用类似EXPORT_SYMBOL但针对 per-CPU 符号。模块里可以用per_cpu(numa_node, cpu)访问。典型使用int node numa_node_id(); // 通常内部就是读 per-CPU 的 numa_node若配置开启PCP 分配时用它决定本地节点、本地 zone减少跨节点访问。三、vm_numa_stat_key静态键DEFINE_STATIC_KEY_TRUE(vm_numa_stat_key);什么是 static key静态键static key是内核的一种运行时分支优化机制include/linux/jump_label.h。本质把几乎总是走某条分支的代码用static_branch_*()包装编译时生成可原地修改的跳转指令NOP / jmp。运行时通过static_branch_enable/disable()改写指令实现零开销的开/关开启时分支变成 NOP直接走启用路径关闭时分支变成 jmp跳到禁用路径。比普通if (flag)好在不启用时几乎没有分支预测和内存访问开销指令被改成 NOP 或跳转。DEFINE_STATIC_KEY_TRUE定义一个静态键初始状态为true启用。对比DEFINE_STATIC_KEY_FALSE初始为禁用。vm_numa_stat_key的用途控制NUMA 统计VM NUMA statistics是否启用。相关代码通常是if (static_branch_likely(vm_numa_stat_key)) __count_numa_event(...); // 或更新 NUMA 统计初始为 true默认启用 NUMA 统计。可通过 sysfs / 内核参数在运行时static_branch_disable(vm_numa_stat_key)关闭关闭后统计代码被优化掉几乎零开销。为什么用 static key 而不是普通变量NUMA 统计在每次页分配/释放的热路径上。如果只是普通if (numa_stat_enabled)每次都要读内存、做分支判断有开销。用 static key禁用时连判断都没有变成 NOP 或直接跳走对性能敏感路径友好。而是否启用 NUMA 统计正是长期不变、适合静态键的场景。四、与 PCP 分配路径的关系把这三块放一起看它们在 PCP/分配路径中的角色定义角色pcp_spin_lock/trylock/unlock操作 PCP 时安全地 pin 加锁numa_nodeper-CPU快速获取当前 CPU 的 NUMA 节点保证本地分配vm_numa_stat_key零开销地开关 NUMA 统计PCP 分配时典型流程示意pcp pcp_spin_lock(zone-per_cpu_pageset); ... if (static_branch_likely(vm_numa_stat_key)) __count_numa_event(zone, NUMA_HIT); ... pcp_spin_unlock(pcp);五、小结代码作用pcp_spin_lock对struct per_cpu_pages加锁封装pcpu_spin_lockpcp_spin_trylock对 PCP 非阻塞加锁失败返回NULLpcp_spin_unlock解 PCP 锁 unpinDEFINE_PER_CPU(int, numa_node)每 CPU 缓存所属 NUMA 节点 ID配置相关EXPORT_PER_CPU_SYMBOL(numa_node)导出给模块用DEFINE_STATIC_KEY_TRUE(vm_numa_stat_key)NUMA 统计开关静态键实现默认开启一句话总结这段代码用pcp_spin_*把通用 per-CPU 锁宏特化到struct per_cpu_pages简化 PCP 操作再定义 per-CPU 的numa_node缓存本 CPU 的 NUMA 节点保证本地分配和静态键vm_numa_stat_key以零开销方式开关 NUMA 统计三者共同服务于 PCP 分配路径的性能与 NUMA 局部性。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门