Skip to content

Commit c9102a6

Browse files
kkdwivediAlexei Starovoitov
authored andcommitted
rqspinlock: Add a test-and-set fallback
Include a test-and-set fallback when queued spinlock support is not available. Introduce a rqspinlock type to act as a fallback when qspinlock support is absent. Include ifdef guards to ensure the slow path in this file is only compiled when CONFIG_QUEUED_SPINLOCKS=y. Subsequent patches will add further logic to ensure fallback to the test-and-set implementation when queued spinlock support is unavailable on an architecture. Unlike other waiting loops in rqspinlock code, the one for test-and-set has no theoretical upper bound under contention, therefore we need a longer timeout than usual. Bump it up to a second in this case. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20250316040541.108729-14-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 31158ad commit c9102a6

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

include/asm-generic/rqspinlock.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,28 @@
1212
#include <linux/types.h>
1313
#include <vdso/time64.h>
1414
#include <linux/percpu.h>
15+
#ifdef CONFIG_QUEUED_SPINLOCKS
16+
#include <asm/qspinlock.h>
17+
#endif
18+
19+
struct rqspinlock {
20+
union {
21+
atomic_t val;
22+
u32 locked;
23+
};
24+
};
1525

1626
struct qspinlock;
27+
#ifdef CONFIG_QUEUED_SPINLOCKS
1728
typedef struct qspinlock rqspinlock_t;
29+
#else
30+
typedef struct rqspinlock rqspinlock_t;
31+
#endif
1832

33+
extern int resilient_tas_spin_lock(rqspinlock_t *lock);
34+
#ifdef CONFIG_QUEUED_SPINLOCKS
1935
extern int resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val);
36+
#endif
2037

2138
/*
2239
* Default timeout for waiting loops is 0.25 seconds

kernel/bpf/rqspinlock.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@
2121
#include <linux/mutex.h>
2222
#include <linux/prefetch.h>
2323
#include <asm/byteorder.h>
24+
#ifdef CONFIG_QUEUED_SPINLOCKS
2425
#include <asm/qspinlock.h>
26+
#endif
2527
#include <trace/events/lock.h>
2628
#include <asm/rqspinlock.h>
2729
#include <linux/timekeeping.h>
2830

2931
/*
3032
* Include queued spinlock definitions and statistics code
3133
*/
34+
#ifdef CONFIG_QUEUED_SPINLOCKS
3235
#include "../locking/qspinlock.h"
3336
#include "../locking/lock_events.h"
3437
#include "rqspinlock.h"
38+
#include "../locking/mcs_spinlock.h"
39+
#endif
3540

3641
/*
3742
* The basic principle of a queue-based spinlock can best be understood
@@ -70,8 +75,6 @@
7075
*
7176
*/
7277

73-
#include "../locking/mcs_spinlock.h"
74-
7578
struct rqspinlock_timeout {
7679
u64 timeout_end;
7780
u64 duration;
@@ -263,6 +266,43 @@ static noinline int check_timeout(rqspinlock_t *lock, u32 mask,
263266
*/
264267
#define RES_RESET_TIMEOUT(ts, _duration) ({ (ts).timeout_end = 0; (ts).duration = _duration; })
265268

269+
/*
270+
* Provide a test-and-set fallback for cases when queued spin lock support is
271+
* absent from the architecture.
272+
*/
273+
int __lockfunc resilient_tas_spin_lock(rqspinlock_t *lock)
274+
{
275+
struct rqspinlock_timeout ts;
276+
int val, ret = 0;
277+
278+
RES_INIT_TIMEOUT(ts);
279+
grab_held_lock_entry(lock);
280+
281+
/*
282+
* Since the waiting loop's time is dependent on the amount of
283+
* contention, a short timeout unlike rqspinlock waiting loops
284+
* isn't enough. Choose a second as the timeout value.
285+
*/
286+
RES_RESET_TIMEOUT(ts, NSEC_PER_SEC);
287+
retry:
288+
val = atomic_read(&lock->val);
289+
290+
if (val || !atomic_try_cmpxchg(&lock->val, &val, 1)) {
291+
if (RES_CHECK_TIMEOUT(ts, ret, ~0u))
292+
goto out;
293+
cpu_relax();
294+
goto retry;
295+
}
296+
297+
return 0;
298+
out:
299+
release_held_lock_entry();
300+
return ret;
301+
}
302+
EXPORT_SYMBOL_GPL(resilient_tas_spin_lock);
303+
304+
#ifdef CONFIG_QUEUED_SPINLOCKS
305+
266306
/*
267307
* Per-CPU queue node structures; we can never have more than 4 nested
268308
* contexts: task, softirq, hardirq, nmi.
@@ -616,3 +656,5 @@ int __lockfunc resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val)
616656
return ret;
617657
}
618658
EXPORT_SYMBOL_GPL(resilient_queued_spin_lock_slowpath);
659+
660+
#endif /* CONFIG_QUEUED_SPINLOCKS */

0 commit comments

Comments
 (0)