1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _BCACHEFS_TWO_STATE_LOCK_H |
3 | #define _BCACHEFS_TWO_STATE_LOCK_H |
4 | |
5 | #include <linux/atomic.h> |
6 | #include <linux/sched.h> |
7 | #include <linux/wait.h> |
8 | |
9 | #include "util.h" |
10 | |
11 | /* |
12 | * Two-state lock - can be taken for add or block - both states are shared, |
13 | * like read side of rwsem, but conflict with other state: |
14 | */ |
15 | typedef struct { |
16 | atomic_long_t v; |
17 | wait_queue_head_t wait; |
18 | } two_state_lock_t; |
19 | |
20 | static inline void two_state_lock_init(two_state_lock_t *lock) |
21 | { |
22 | atomic_long_set(v: &lock->v, i: 0); |
23 | init_waitqueue_head(&lock->wait); |
24 | } |
25 | |
26 | static inline void bch2_two_state_unlock(two_state_lock_t *lock, int s) |
27 | { |
28 | long i = s ? 1 : -1; |
29 | |
30 | EBUG_ON(atomic_long_read(&lock->v) == 0); |
31 | |
32 | if (atomic_long_sub_return_release(i, v: &lock->v) == 0) |
33 | wake_up_all(&lock->wait); |
34 | } |
35 | |
36 | static inline bool bch2_two_state_trylock(two_state_lock_t *lock, int s) |
37 | { |
38 | long i = s ? 1 : -1; |
39 | long v = atomic_long_read(v: &lock->v), old; |
40 | |
41 | do { |
42 | old = v; |
43 | |
44 | if (i > 0 ? v < 0 : v > 0) |
45 | return false; |
46 | } while ((v = atomic_long_cmpxchg_acquire(v: &lock->v, |
47 | old, new: old + i)) != old); |
48 | return true; |
49 | } |
50 | |
51 | void __bch2_two_state_lock(two_state_lock_t *, int); |
52 | |
53 | static inline void bch2_two_state_lock(two_state_lock_t *lock, int s) |
54 | { |
55 | if (!bch2_two_state_trylock(lock, s)) |
56 | __bch2_two_state_lock(lock, s); |
57 | } |
58 | |
59 | #endif /* _BCACHEFS_TWO_STATE_LOCK_H */ |
60 | |