現在、Linux カーネルのスピンロックはすべて「チケットベース」のスピンロックを使用していることがわかりました。しかし、それの ARM 実装を見た後、「load-add-store」操作がまったくアトミックではないため、私は混乱しています。以下のコードを参照してください。
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 を使用する 2 人のユーザーが同時に lock->slock を自分の変数 "lockval" に読み込んでいます。次に、「lockval」をそれぞれ 1 つずつ追加してから、元に戻します。
これにより、これら 2 人のユーザーが同じ「番号」を取得し、「所有者」フィールドがその番号になると、両方ともロックを取得し、いくつかの共有リソースで操作を行います!
カーネルがスピンロックでそのようなバグを持つことはないと思います。私はどこか間違っていますか?