我发现Linux内核中的自旋锁现在都在使用“ticket-based”自旋锁。然而,在查看它的 ARM 实现后,我感到困惑,因为“加载-添加-存储”操作根本不是原子的。请看下面的代码:
74 static inline void arch_spin_lock(arch_spinlock_t *lock)
75 {
76 unsigned long tmp;
77 u32 newval;
78 arch_spinlock_t lockval;
79
80 __asm__ __volatile__(
81 "1: ldrex %0, [%3]\n" /*Why this load-add-store is not atomic?*/
82 " add %1, %0, %4\n"
83 " strex %2, %1, [%3]\n"
84 " teq %2, #0\n"
85 " bne 1b"
86 : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
87 : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
88 : "cc");
89
90 while (lockval.tickets.next != lockval.tickets.owner) {
91 wfe();
92 lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
93 }
94
95 smp_mb();
96 }
如您所见,在第 81~83 行,它将 lock->slock 加载到“lockval”并将其递增 1,然后将其存储回 lock->slock。
但是我没有看到任何地方确保它是原子的。所以有可能:
不同 cpu 上的两个用户同时读取 lock->slock 到他们自己的变量“lockval”;然后他们分别将“lockval”加一,然后将其存储回去。
这将导致这两个用户手头有相同的“编号”,一旦“所有者”字段变为该编号,他们都将获得锁并对某些共享资源进行操作!
我认为内核在自旋锁中不会有这样的错误。我哪里错了吗?
最佳答案
STREX
is a conditional store , 此代码有 Load Link-Store Conditional语义,即使 ARM 不使用该名称。
操作要么自动完成,要么失败。
汇编程序 block 测试失败(tmp
变量指示失败)并使用新值(由另一个内核更新)重新尝试修改。
关于linux - 基于票证的 ARM 自旋锁的竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19759148/