ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

page_alloc pcp_trylock_prepare/pcp_trylock_finish

page_alloc pcp_trylock_prepare/pcp_trylock_finish PCPPer-CPU Pagestrylock 路径的配套宏用来处理一个微妙的问题单处理器UP下spin_trylock永远成功因此必须靠关中断来防止重入。一、宏定义本身/* UP spin_trylock always succeeds so disable IRQs to prevent re-entrancy. */ #define pcp_trylock_prepare(flags) local_irq_save(flags) #define pcp_trylock_finish(flags) local_irq_restore(flags)pcp_trylock_prepare(flags)进入 trylock 保护的临界区前调用保存当前中断状态并关中断。pcp_trylock_finish(flags)离开临界区后调用恢复之前的中断状态。flags是unsigned long由local_irq_save填入交给local_irq_restore还原。这两个宏是成对使用的unsigned long flags; pcp_trylock_prepare(flags); /* ... 临界区trylock 操作 PCP ... */ pcp_trylock_finish(flags);二、为什么叫 pcp_trylock见过FPI_TRYLOCK标志意思是释放页时只用 trylock不阻塞等锁。这条路径就是它的落地实现之一。在 PCP 相关操作如free_unref_page、rmqueue_pcplist等中内核会用spin_trylock尝试拿zone-lock或 PCP 相关锁拿到 → 继续操作拿不到 → 放弃走别的路径或稍后重试。目的是避免在热路径上阻塞等锁降低延迟抖动。三、核心问题UP 下spin_trylock永远成功关键在于spin_trylock在单处理器UP,!CONFIG_SMP下的实现在 SMP 下spin_trylock会真的去尝试获取自旋锁失败返回 0。在UP 下没有真正的并发 CPU所以spin_lock/spin_trylock通常被编译成几乎空操作——它总是返回成功因为不存在另一个 CPU 持锁。那么问题来了既然锁总是拿到为什么还需要防重入答案是中断以及软中断/抢占。场景中断打断临界区考虑 UP 上的执行流进程上下文 进入 PCP 临界区以为拿到了锁 操作 PCP 链表到一半 -- 中断来了 中断处理程序也去操作同一个 PCP 链表 以为也拿到了锁因为 spin_trylock 在 UP 下总是成功 破坏了正在被修改的链表 / 数据结构 -- 中断返回 继续操作但数据已被破坏 离开临界区因为 UP 下锁不提供任何互斥中断处理程序会重入同一段临界区导致数据竞争和损坏。解决办法关中断在 UP 下关中断就能阻止中断处理程序重入临界区内关中断 → 中断被延迟 → 不会有中断处理程序并发进入同时也能防住软中断local_irq_save也关软中断相关路径取决于配置和抢占关中断隐含禁止抢占。这就是注释的含义UP spin_trylock always succeeds so disable IRQs to prevent re-entrancy.即UP 下 trylock 形同虚设必须靠关中断来达到临界区互斥的效果。四、SMP 下这两个宏做什么在 SMP 配置下同样的宏名通常被定义为空操作或只做很轻的处理因为SMP 下spin_trylock是真锁能真正互斥中断处理程序若也去拿同一把锁会自旋等待或 trylock 失败放弃不会重入破坏数据因此不需要靠关中断来防重入而且关中断在 SMP 下代价更大、影响其他 CPU 的中断响应。所以典型实现是#ifdef CONFIG_SMP #define pcp_trylock_prepare(flags) do { } while (0) #define pcp_trylock_finish(flags) do { } while (0) #else #define pcp_trylock_prepare(flags) local_irq_save(flags) #define pcp_trylock_finish(flags) local_irq_restore(flags) #endif五、为什么不用普通spin_lock_irqsave因为这条路径的语义是trylock只想尝试拿锁失败就放弃绝不等待用spin_lock_irqsave会自旋等待违背 trylock 的初衷在内存压力/高并发下可能长时间自旋。于是组合成SMPspin_trylock提供互斥拿不到就退UPspin_trylock无效用local_irq_save提供互斥防中断重入且关中断本身也拿不到就退的语义不冲突关中断总能成功。六、完整调用示意unsigned long flags; pcp_trylock_prepare(flags); // UP: 关中断SMP: 空 if (spin_trylock(zone-lock)) { // UP: 总成功SMP: 可能失败 /* 操作 PCP 链表 / 伙伴系统 */ spin_unlock(zone-lock); /* 成功路径 */ } else { /* SMP: 拿不到锁走 fallback */ } pcp_trylock_finish(flags); // UP: 恢复中断SMP: 空七、小结对比配置spin_trylock行为互斥靠什么pcp_trylock_prepare/finishSMP真锁可能失败锁本身空操作UP总成功空操作关中断防重入local_irq_save/local_irq_restore一句话总结在 UP 下spin_trylock永远成功、不提供任何互斥中断处理程序会重入 PCP 临界区破坏数据因此pcp_trylock_prepare/finish在 UP 下用local_irq_save/local_irq_restore关中断来防重入而在 SMP 下退化为空操作由真正的spin_trylock保证互斥。
返回列表