Line data Source code
1 : /*
2 : * Queue read/write lock
3 : *
4 : * This program is free software; you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation; either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
15 : *
16 : * Authors: Waiman Long <waiman.long@hp.com>
17 : */
18 : #ifndef __ASM_GENERIC_QRWLOCK_H
19 : #define __ASM_GENERIC_QRWLOCK_H
20 :
21 : #include <linux/atomic.h>
22 : #include <asm/barrier.h>
23 : #include <asm/processor.h>
24 :
25 : #include <asm-generic/qrwlock_types.h>
26 :
27 : /*
28 : * Writer states & reader shift and bias
29 : */
30 : #define _QW_WAITING 1 /* A writer is waiting */
31 : #define _QW_LOCKED 0xff /* A writer holds the lock */
32 : #define _QW_WMASK 0xff /* Writer mask */
33 : #define _QR_SHIFT 8 /* Reader count shift */
34 : #define _QR_BIAS (1U << _QR_SHIFT)
35 :
36 : /*
37 : * External function declarations
38 : */
39 : extern void queue_read_lock_slowpath(struct qrwlock *lock);
40 : extern void queue_write_lock_slowpath(struct qrwlock *lock);
41 :
42 : /**
43 : * queue_read_can_lock- would read_trylock() succeed?
44 : * @lock: Pointer to queue rwlock structure
45 : */
46 : static inline int queue_read_can_lock(struct qrwlock *lock)
47 : {
48 : return !(atomic_read(&lock->cnts) & _QW_WMASK);
49 : }
50 :
51 : /**
52 : * queue_write_can_lock- would write_trylock() succeed?
53 : * @lock: Pointer to queue rwlock structure
54 : */
55 : static inline int queue_write_can_lock(struct qrwlock *lock)
56 : {
57 : return !atomic_read(&lock->cnts);
58 : }
59 :
60 : /**
61 : * queue_read_trylock - try to acquire read lock of a queue rwlock
62 : * @lock : Pointer to queue rwlock structure
63 : * Return: 1 if lock acquired, 0 if failed
64 : */
65 : static inline int queue_read_trylock(struct qrwlock *lock)
66 : {
67 : u32 cnts;
68 :
69 : cnts = atomic_read(&lock->cnts);
70 : if (likely(!(cnts & _QW_WMASK))) {
71 : cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts);
72 : if (likely(!(cnts & _QW_WMASK)))
73 : return 1;
74 : atomic_sub(_QR_BIAS, &lock->cnts);
75 : }
76 : return 0;
77 : }
78 :
79 : /**
80 : * queue_write_trylock - try to acquire write lock of a queue rwlock
81 : * @lock : Pointer to queue rwlock structure
82 : * Return: 1 if lock acquired, 0 if failed
83 : */
84 : static inline int queue_write_trylock(struct qrwlock *lock)
85 : {
86 : u32 cnts;
87 :
88 : cnts = atomic_read(&lock->cnts);
89 : if (unlikely(cnts))
90 : return 0;
91 :
92 : return likely(atomic_cmpxchg(&lock->cnts,
93 : cnts, cnts | _QW_LOCKED) == cnts);
94 : }
95 : /**
96 : * queue_read_lock - acquire read lock of a queue rwlock
97 : * @lock: Pointer to queue rwlock structure
98 : */
99 : static inline void queue_read_lock(struct qrwlock *lock)
100 : {
101 : u32 cnts;
102 :
103 : cnts = atomic_add_return(_QR_BIAS, &lock->cnts);
104 : if (likely(!(cnts & _QW_WMASK)))
105 : return;
106 :
107 : /* The slowpath will decrement the reader count, if necessary. */
108 : queue_read_lock_slowpath(lock);
109 : }
110 :
111 : /**
112 : * queue_write_lock - acquire write lock of a queue rwlock
113 : * @lock : Pointer to queue rwlock structure
114 : */
115 : static inline void queue_write_lock(struct qrwlock *lock)
116 : {
117 : /* Optimize for the unfair lock case where the fair flag is 0. */
118 : if (atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0)
119 : return;
120 :
121 : queue_write_lock_slowpath(lock);
122 : }
123 :
124 : /**
125 : * queue_read_unlock - release read lock of a queue rwlock
126 : * @lock : Pointer to queue rwlock structure
127 : */
128 : static inline void queue_read_unlock(struct qrwlock *lock)
129 : {
130 : /*
131 : * Atomically decrement the reader count
132 : */
133 6317893 : smp_mb__before_atomic();
134 6317887 : atomic_sub(_QR_BIAS, &lock->cnts);
135 : }
136 :
137 : #ifndef queue_write_unlock
138 : /**
139 : * queue_write_unlock - release write lock of a queue rwlock
140 : * @lock : Pointer to queue rwlock structure
141 : */
142 : static inline void queue_write_unlock(struct qrwlock *lock)
143 : {
144 : /*
145 : * If the writer field is atomic, it can be cleared directly.
146 : * Otherwise, an atomic subtraction will be used to clear it.
147 : */
148 : smp_mb__before_atomic();
149 : atomic_sub(_QW_LOCKED, &lock->cnts);
150 : }
151 : #endif
152 :
153 : /*
154 : * Remapping rwlock architecture specific functions to the corresponding
155 : * queue rwlock functions.
156 : */
157 : #define arch_read_can_lock(l) queue_read_can_lock(l)
158 : #define arch_write_can_lock(l) queue_write_can_lock(l)
159 : #define arch_read_lock(l) queue_read_lock(l)
160 : #define arch_write_lock(l) queue_write_lock(l)
161 : #define arch_read_trylock(l) queue_read_trylock(l)
162 : #define arch_write_trylock(l) queue_write_trylock(l)
163 : #define arch_read_unlock(l) queue_read_unlock(l)
164 : #define arch_write_unlock(l) queue_write_unlock(l)
165 :
166 : #endif /* __ASM_GENERIC_QRWLOCK_H */
|