ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

page_alloc pcpu_task_pin/ pcpu_task_unpin

page_alloc pcpu_task_pin/ pcpu_task_unpin 用于在访问PCPPer-CPU Pages时把当前任务临时钉在某个 CPU 上避免迁移导致锁错 PCP。同时它区分了非 RT和RTCONFIG_PREEMPT_RT两种内核。下面逐层拆解。一、要解决的核心问题注释第一句点明背景Locking a pcp requires a PCP lookup followed by a spinlock. To avoid a migration causing the wrong PCP to be locked and remote memory being potentially allocated, pin the task to the CPU for the lookuplock.PCP 操作的标准流程是两步1. PCP lookup根据当前 CPU 找到对应的 per_cpu_pages 结构 通常用 smp_processor_id() / raw_cpu_ptr() 2. spinlock 锁住这个 PCP 或 zone-lock然后操作问题在于这两步之间可能发生任务迁移migrationCPU0 上 步骤1查到 CPU0 的 PCP 结构记下指针 p -- 任务被调度器迁移到 CPU1 步骤2对 p实际是 CPU0 的 PCP加锁、分配页后果有两个锁错了 PCP你以为在锁当前 CPU的 PCP实际锁的是另一个 CPU的 PCP可能与其他 CPU 的操作冲突、破坏并发假设。远程内存分配从别的 CPU 的 PCP 分配页等于跨 NUMA/跨 CPU 拿内存破坏 per-CPU 缓存的局部性性能下降。所以必须在lookup lock这段时间里禁止任务被迁移。这就是 pin the task to the CPU。二、两种实现preempt_disablevsmigrate_disable#ifndef CONFIG_PREEMPT_RT #define pcpu_task_pin() preempt_disable() #define pcpu_task_unpin() preempt_enable() #else #define pcpu_task_pin() migrate_disable() #define pcpu_task_unpin() migrate_enable() #endif非 RT 内核用preempt_disable()preempt_disable()禁止抢占。禁止抢占 ⇒ 当前任务在preempt_enable()之前不会被调度出去⇒ 自然也不会迁移到别的 CPU。注释说preempt_disableis used on !RT because it is faster than migrate_disable。在非 RT 内核里preempt_disable通常只是给preempt_count加一非常轻量。migrate_disable在非 RT 下实现更复杂要处理迁移线程、可能触发 stop_machine 之类更重。所以非 RT 优先用更快的preempt_disable。RT 内核用migrate_disable()RT 内核CONFIG_PREEMPT_RT为什么不能只用preempt_disable注释解释migrate_disable is used on RT because otherwise RT spinlock usage is interfered with and a high priority task cannot preempt the allocator.拆开看RT 内核中自旋锁被替换为可睡眠锁rt_mutex / sleeping spinlock在CONFIG_PREEMPT_RT下spinlock_t不再是忙等自旋而是可睡眠的互斥量允许高优先级任务抢占持锁者。如果这里用preempt_disable()把抢占整个关掉就破坏了 RT 的抢占语义高优先级任务无法抢占正在做分配的低优先级任务持锁者可能被迫等待而高优先级任务反而被阻塞 → 优先级反转RT 最忌讳。注释里的 RT spinlock usage is interfered with 就是指这个preempt_disable会干扰 RT 自旋锁可被抢占的设计。migrate_disable()只禁迁移不禁抢占migrate_disable()的效果是任务不会被迁移到别的 CPU但仍可被抢占在 RT 下。这样既解决了lookuplock 之间迁移的问题又保留了 RT 所需的可抢占性高优先级任务仍能抢占分配器分配器使用的 RT 锁仍能按设计工作。所以 RT 下选择migrate_disable/migrate_enable。三、migrate_disable在非 RT 下也存在但为什么不用migrate_disable在非 RT 内核里也有实现用于需要禁迁移但允许抢占的场景。但它比preempt_disable慢非 RT 下禁迁移需要更复杂的机制如设置cpus_mask、必要时用 migration 线程帮忙成本高。因此非 RT 选择更快的preempt_disable。这也解释了注释里 it is faster than migrate_disable 的比较是在非 RT 语境下说的。四、典型使用模式pcpu_task_pin(); // 禁迁移 /* 1. lookup确定当前 CPU 的 PCP */ struct per_cpu_pages *pcp this_cpu_ptr(zone-per_cpu_pageset); /* 2. lock锁住它或 zone-lock */ spin_lock(pcp-lock); // 或 trylock /* ... 操作 PCP 链表分配/释放页 ... */ spin_unlock(pcp-lock); pcpu_task_unpin(); // 允许迁移注意pin/unpin必须成对且unpin要在锁释放之后或至少保证 lookup 到 lock 的区间被覆盖。五、与前面pcp_trylock_prepare的关系这两个机制解决不同层面的问题常一起出现机制解决什么手段pcpu_task_pin()lookup 与 lock 之间任务迁移导致锁错 PCP禁迁移preempt_disable / migrate_disablepcp_trylock_prepare()UP 下spin_trylock 失效导致中断重入UP 关中断 / SMP 空操作前者管CPU 迁移后者管中断重入二者互补。六、对比小结内核配置用的宏禁止迁移禁止抢占原因非 RT!CONFIG_PREEMPT_RTpreempt_disable()✅✅比 migrate_disable 快RTCONFIG_PREEMPT_RTmigrate_disable()✅❌保留可抢占避免干扰 RT 自旋锁、保证高优先级可抢占一句话总结pcpu_task_pin/unpin在查找 PCP 加锁期间禁止任务迁移避免锁错 PCP 和远程分配非 RT 用更快的preempt_disable禁抢占RT 用migrate_disable只禁迁移、保留可抢占以免破坏 RT 的可抢占语义和自旋锁实现。
返回列表