Skip to content

Commit 36b48be

Browse files
committed
rqspinlock: Enclose lock/unlock within lock entry acquisitions
There is a tiny window between the fast-path cmpxchg and the grabbing of the lock entry where an NMI could land, attempt the same lock that was just acquired, and end up timing out. This is not ideal. Instead, move the lock entry acquisition from the fast path to before the cmpxchg, and remove the grabbing of the lock entry in the slow path, assuming it was already taken by the fast path. Do something similar for the unlock case, where smp_store_release is moved to before the WRITE_ONCE on the lock entry. We could have the case before where if an NMI landed write after the WRITE_ONCE, but before the unlock, it would miss the AA case. [ kkd: The movement of the store release is not trivial, see comments/revisit later. ] Signed-off-by: Kumar Kartikeya Dwivedi <[email protected]>
1 parent 590699d commit 36b48be

File tree

2 files changed

+12
-12
lines changed

2 files changed

+12
-12
lines changed

include/asm-generic/rqspinlock.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,15 @@ static __always_inline int res_spin_lock(rqspinlock_t *lock)
175175
{
176176
int val = 0;
177177

178+
/*
179+
* Grab the deadlock detection entry before doing the cmpxchg, so that
180+
* reentrancy due to NMIs between the succeeding cmpxchg and creation of
181+
* held lock entry can correctly detect an acquisition attempt in the
182+
* interrupted context.
183+
*/
184+
grab_held_lock_entry(lock);
185+
178186
if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL))) {
179-
grab_held_lock_entry(lock);
180187
return 0;
181188
}
182189
return resilient_queued_spin_lock_slowpath(lock, val);
@@ -192,6 +199,7 @@ static __always_inline void res_spin_unlock(rqspinlock_t *lock)
192199
{
193200
struct rqspinlock_held *rqh = this_cpu_ptr(&rqspinlock_held_locks);
194201

202+
smp_store_release(&lock->locked, 0);
195203
if (unlikely(rqh->cnt > RES_NR_HELD))
196204
goto unlock;
197205
WRITE_ONCE(rqh->locks[rqh->cnt - 1], NULL);
@@ -213,7 +221,6 @@ static __always_inline void res_spin_unlock(rqspinlock_t *lock)
213221
* the store below is done, which would ensure the entry is overwritten
214222
* to NULL, etc.
215223
*/
216-
smp_store_release(&lock->locked, 0);
217224
this_cpu_dec(rqspinlock_held_locks.cnt);
218225
}
219226

kernel/bpf/rqspinlock.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ int __lockfunc resilient_tas_spin_lock(rqspinlock_t *lock)
275275
int val, ret = 0;
276276

277277
RES_INIT_TIMEOUT(ts);
278-
grab_held_lock_entry(lock);
278+
/* Deadlock detection entry already held after failing slow path. */
279279

280280
/*
281281
* Since the waiting loop's time is dependent on the amount of
@@ -397,10 +397,7 @@ int __lockfunc resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val)
397397
goto queue;
398398
}
399399

400-
/*
401-
* Grab an entry in the held locks array, to enable deadlock detection.
402-
*/
403-
grab_held_lock_entry(lock);
400+
/* Deadlock detection entry already held after failing slow path. */
404401

405402
/*
406403
* We're pending, wait for the owner to go away.
@@ -448,11 +445,7 @@ int __lockfunc resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val)
448445
*/
449446
queue:
450447
lockevent_inc(lock_slowpath);
451-
/*
452-
* Grab deadlock detection entry for the queue path.
453-
*/
454-
grab_held_lock_entry(lock);
455-
448+
/* Deadlock detection entry already held after failing slow path. */
456449
node = this_cpu_ptr(&rqnodes[0].mcs);
457450
idx = node->count++;
458451
tail = encode_tail(smp_processor_id(), idx);

0 commit comments

Comments
 (0)