use_global_asid是 x86 架构中为满足条件的多线程进程分配全局 ASID 的加锁入口函数。它由consider_global_asid调用,负责在并发环境下安全地完成“检查-分配-标记”的完整流程。
核心作用:安全地分配并标记全局 ASID
这个函数的核心目标是:为一个已经满足“活跃 CPU 数”阈值的进程,分配一个全局 ASID,并将其记录到mm->context中,使该进程在所有 CPU 上使用相同的 PCID,从而能够利用 AMD INVLPGB 等硬件辅助的广播 TLB 失效机制。
代码逻辑解析
static void use_global_asid(struct mm_struct *mm) { guard(raw_spinlock_irqsave)(&global_asid_lock); /* This process is already using broadcast TLB invalidation. */ if (READ_ONCE(mm->context.global_asid)) return; /* The last global ASID was consumed while waiting for the lock. */ if (!READ_ONCE(global_asid_available)) { VM_WARN_ONCE(1, "Ran out of global ASIDs\n"); return; } /* * The transition from IPI TLB flushing, with a dynamic ASID, * and broadcast TLB flushing, using a global ASID, uses memory * ordering for synchronization. * * This code sets asid_transition first, before assigning the * global ASID. */ WRITE_ONCE(mm->context.asid_transition, true); WRITE_ONCE(mm->context.global_asid, get_global_asid()); }关键步骤:
获取全局锁:
guard(raw_spinlock_irqsave)(&global_asid_lock)确保同一时刻只有一个 CPU 在修改全局 ASID 分配状态。重复检查:如果
mm->context.global_asid已经非零,说明该进程已经拥有全局 ASID(可能在等锁期间被其他线程分配了),直接返回。可用性检查:如果
global_asid_available为 0,说明全局 ASID 空间已耗尽,发出警告并返回。这是consider_global_asid中检查的“双重保险”。设置转换标志:先将
mm->context.asid_transition设为true。这个顺序至关重要——它确保后续的 TLB 刷新代码在观察到global_asid非零时,也能看到asid_transition为真,从而正确处理从动态 ASID 到全局 ASID 的过渡。分配并记录 ASID:调用
get_global_asid()获取一个全局 ASID,并写入mm->context.global_asid。从此刻起,该进程在所有 CPU 上都会使用这个统一的 PCID。
调用关系与设计意图
consider_global_asid() // 定期检查,条件满足时触发 └── use_global_asid() // ← 你问的函数(加锁分配) └── get_global_asid() // 实际从位图分配
consider_global_asid通过(current->pid & 0x1f) != (jiffies & 0x1f)的采样机制,每隔一段时间才检查一次,避免频繁的锁竞争和 CPU 掩码遍历。一旦条件满足,use_global_asid就负责在锁保护下完成最终的分配。
已知问题与修复
搜索结果记录了一个重要的边界计算 bug:global_asid_available的初始值被设为MAX_ASID_AVAILABLE - TLB_NR_DYN_ASIDS - 1,这少算了一个 ASID。由于位图大小是MAX_ASID_AVAILABLE,有效范围[TLB_NR_DYN_ASIDS, MAX_ASID_AVAILABLE-1]的实际大小应该是MAX_ASID_AVAILABLE - TLB_NR_DYN_ASIDS。这个修复确保了 ASID 空间的完整利用。
此外,READ_ONCE在持有global_asid_lock时被一些开发者认为是不必要的,因为所有修改都在锁保护下进行。但这些标记作为一种防御性编程习惯被保留了下来。