Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #ifndef _RTE_RWLOCK_H_
6 : : #define _RTE_RWLOCK_H_
7 : :
8 : : /**
9 : : * @file
10 : : *
11 : : * RTE Read-Write Locks
12 : : *
13 : : * This file defines an API for read-write locks. The lock is used to
14 : : * protect data that allows multiple readers in parallel, but only
15 : : * one writer. All readers are blocked until the writer is finished
16 : : * writing.
17 : : *
18 : : * This version does not give preference to readers or writers
19 : : * and does not starve either readers or writers.
20 : : *
21 : : * See also:
22 : : * https://locklessinc.com/articles/locks/
23 : : */
24 : :
25 : : #include <errno.h>
26 : : #include <stdbool.h>
27 : :
28 : : #include <rte_branch_prediction.h>
29 : : #include <rte_common.h>
30 : : #include <rte_lock_annotations.h>
31 : : #include <rte_pause.h>
32 : : #include <rte_stdatomic.h>
33 : :
34 : : #ifdef __cplusplus
35 : : extern "C" {
36 : : #endif
37 : :
38 : : /**
39 : : * The rte_rwlock_t type.
40 : : *
41 : : * Readers increment the counter by RTE_RWLOCK_READ (4)
42 : : * Writers set the RTE_RWLOCK_WRITE bit when lock is held
43 : : * and set the RTE_RWLOCK_WAIT bit while waiting.
44 : : *
45 : : * 31 2 1 0
46 : : * +-------------------+-+-+
47 : : * | readers | | |
48 : : * +-------------------+-+-+
49 : : * ^ ^
50 : : * | |
51 : : * WRITE: lock held ----/ |
52 : : * WAIT: writer pending --/
53 : : */
54 : :
55 : : #define RTE_RWLOCK_WAIT 0x1 /* Writer is waiting */
56 : : #define RTE_RWLOCK_WRITE 0x2 /* Writer has the lock */
57 : : #define RTE_RWLOCK_MASK (RTE_RWLOCK_WAIT | RTE_RWLOCK_WRITE)
58 : : /* Writer is waiting or has lock */
59 : : #define RTE_RWLOCK_READ 0x4 /* Reader increment */
60 : :
61 : : typedef struct __rte_capability("rwlock") {
62 : : RTE_ATOMIC(int32_t) cnt;
63 : : } rte_rwlock_t;
64 : :
65 : : /**
66 : : * A static rwlock initializer.
67 : : */
68 : : #define RTE_RWLOCK_INITIALIZER { 0 }
69 : :
70 : : /**
71 : : * Initialize the rwlock to an unlocked state.
72 : : *
73 : : * @param rwl
74 : : * A pointer to the rwlock structure.
75 : : */
76 : : static inline void
77 : : rte_rwlock_init(rte_rwlock_t *rwl)
78 : : {
79 [ # # # # ]: 1067 : rwl->cnt = 0;
80 : 2 : }
81 : :
82 : : /**
83 : : * Take a read lock. Loop until the lock is held.
84 : : *
85 : : * @note The RW lock isn't recursive, so calling this function on the same
86 : : * lock twice without releasing it could potentially result in a deadlock
87 : : * scenario when a write lock is involved.
88 : : *
89 : : * @param rwl
90 : : * A pointer to a rwlock structure.
91 : : */
92 : : static inline void
93 : 47969868 : rte_rwlock_read_lock(rte_rwlock_t *rwl)
94 : : __rte_acquire_shared_capability(rwl)
95 : : __rte_no_thread_safety_analysis
96 : : {
97 : : int32_t x;
98 : :
99 : : while (1) {
100 : : /* Wait while writer is present or pending */
101 : 47970403 : while (rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed)
102 [ + + ]: 56909942 : & RTE_RWLOCK_MASK)
103 : : rte_pause();
104 : :
105 : : /* Try to get read lock */
106 : 47966980 : x = rte_atomic_fetch_add_explicit(&rwl->cnt, RTE_RWLOCK_READ,
107 : : rte_memory_order_acquire) + RTE_RWLOCK_READ;
108 : :
109 : : /* If no writer, then acquire was successful */
110 [ + + ]: 47966980 : if (likely(!(x & RTE_RWLOCK_MASK)))
111 : 47966445 : return;
112 : :
113 : : /* Lost race with writer, backout the change. */
114 : 535 : rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_READ,
115 : : rte_memory_order_relaxed);
116 : : }
117 : : }
118 : :
119 : : /**
120 : : * Try to take a read lock.
121 : : *
122 : : * @param rwl
123 : : * A pointer to a rwlock structure.
124 : : * @return
125 : : * - zero if the lock is successfully taken
126 : : * - -EBUSY if lock could not be acquired for reading because a
127 : : * writer holds the lock
128 : : */
129 : : static inline int
130 : 1193125628 : rte_rwlock_read_trylock(rte_rwlock_t *rwl)
131 : : __rte_try_acquire_shared_capability(false, rwl)
132 : : __rte_no_thread_safety_analysis
133 : : {
134 : : int32_t x;
135 : :
136 : 1193125628 : x = rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed);
137 : :
138 : : /* fail if write lock is held or writer is pending */
139 [ + + ]: 1193125628 : if (x & RTE_RWLOCK_MASK)
140 : : return -EBUSY;
141 : :
142 : : /* Try to get read lock */
143 : 40643796 : x = rte_atomic_fetch_add_explicit(&rwl->cnt, RTE_RWLOCK_READ,
144 : : rte_memory_order_acquire) + RTE_RWLOCK_READ;
145 : :
146 : : /* Back out if writer raced in */
147 [ + + ]: 40643796 : if (unlikely(x & RTE_RWLOCK_MASK)) {
148 : 9777110 : rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_READ,
149 : : rte_memory_order_release);
150 : :
151 : 9777110 : return -EBUSY;
152 : : }
153 : : return 0;
154 : : }
155 : :
156 : : /**
157 : : * Release a read lock.
158 : : *
159 : : * @param rwl
160 : : * A pointer to the rwlock structure.
161 : : */
162 : : static inline void
163 : 0 : rte_rwlock_read_unlock(rte_rwlock_t *rwl)
164 : : __rte_release_shared_capability(rwl)
165 : : __rte_no_thread_safety_analysis
166 : : {
167 [ + + # # : 80241883 : rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_READ, rte_memory_order_release);
# # ]
168 : 0 : }
169 : :
170 : : /**
171 : : * Try to take a write lock.
172 : : *
173 : : * @param rwl
174 : : * A pointer to a rwlock structure.
175 : : * @return
176 : : * - zero if the lock is successfully taken
177 : : * - -EBUSY if lock could not be acquired for writing because
178 : : * it was already locked for reading or writing
179 : : */
180 : : static inline int
181 : : rte_rwlock_write_trylock(rte_rwlock_t *rwl)
182 : : __rte_try_acquire_capability(false, rwl)
183 : : __rte_no_thread_safety_analysis
184 : : {
185 : : int32_t x;
186 : :
187 : 44779136 : x = rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed);
188 [ + + + + : 64295836 : if (x < RTE_RWLOCK_WRITE &&
# # # # ]
189 : 19516700 : rte_atomic_compare_exchange_weak_explicit(&rwl->cnt, &x, x + RTE_RWLOCK_WRITE,
190 : : rte_memory_order_acquire, rte_memory_order_relaxed))
191 : : return 0;
192 : : else
193 : : return -EBUSY;
194 : : }
195 : :
196 : : /**
197 : : * Take a write lock. Loop until the lock is held.
198 : : *
199 : : * @param rwl
200 : : * A pointer to a rwlock structure.
201 : : */
202 : : static inline void
203 : 47394291 : rte_rwlock_write_lock(rte_rwlock_t *rwl)
204 : : __rte_acquire_capability(rwl)
205 : : __rte_no_thread_safety_analysis
206 : : {
207 : : int32_t x;
208 : :
209 : : while (1) {
210 : 47404005 : x = rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed);
211 : :
212 : : /* No readers or writers? */
213 [ + + ]: 47404005 : if (likely(x < RTE_RWLOCK_WRITE)) {
214 : : /* Turn off RTE_RWLOCK_WAIT, turn on RTE_RWLOCK_WRITE */
215 [ + + ]: 47399871 : if (rte_atomic_compare_exchange_weak_explicit(&rwl->cnt, &x,
216 : : RTE_RWLOCK_WRITE, rte_memory_order_acquire,
217 : : rte_memory_order_relaxed))
218 : 47396335 : return;
219 : : }
220 : :
221 : : /* Turn on writer wait bit */
222 [ + + ]: 9700 : if (!(x & RTE_RWLOCK_WAIT))
223 : 9621 : rte_atomic_fetch_or_explicit(&rwl->cnt, RTE_RWLOCK_WAIT,
224 : : rte_memory_order_relaxed);
225 : :
226 : : /* Wait until no readers before trying again */
227 : 8348138 : while (rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed)
228 : 8348138 : > RTE_RWLOCK_WAIT)
229 : : rte_pause();
230 : :
231 : : }
232 : : }
233 : :
234 : : /**
235 : : * Release a write lock.
236 : : *
237 : : * @param rwl
238 : : * A pointer to a rwlock structure.
239 : : */
240 : : static inline void
241 : : rte_rwlock_write_unlock(rte_rwlock_t *rwl)
242 : : __rte_release_capability(rwl)
243 : : __rte_no_thread_safety_analysis
244 : : {
245 [ + + # # : 66693521 : rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_WRITE, rte_memory_order_release);
# # ]
246 : 24198 : }
247 : :
248 : : /**
249 : : * Test if the write lock is taken.
250 : : *
251 : : * @param rwl
252 : : * A pointer to a rwlock structure.
253 : : * @return
254 : : * 1 if the write lock is currently taken; 0 otherwise.
255 : : */
256 : : static inline int
257 : : rte_rwlock_write_is_locked(rte_rwlock_t *rwl)
258 : : {
259 [ # # ]: 0 : if (rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed) & RTE_RWLOCK_WRITE)
260 : 0 : return 1;
261 : :
262 : : return 0;
263 : : }
264 : :
265 : : /**
266 : : * Try to execute critical section in a hardware memory transaction, if it
267 : : * fails or not available take a read lock
268 : : *
269 : : * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
270 : : * transaction always aborts the transaction since the CPU is not able to
271 : : * roll-back should the transaction fail. Therefore, hardware transactional
272 : : * locks are not advised to be used around rte_eth_rx_burst() and
273 : : * rte_eth_tx_burst() calls.
274 : : *
275 : : * @param rwl
276 : : * A pointer to a rwlock structure.
277 : : */
278 : : static inline void
279 : : rte_rwlock_read_lock_tm(rte_rwlock_t *rwl)
280 : : __rte_acquire_shared_capability(rwl);
281 : :
282 : : /**
283 : : * Commit hardware memory transaction or release the read lock if the lock is used as a fall-back
284 : : *
285 : : * @param rwl
286 : : * A pointer to the rwlock structure.
287 : : */
288 : : static inline void
289 : : rte_rwlock_read_unlock_tm(rte_rwlock_t *rwl)
290 : : __rte_release_shared_capability(rwl);
291 : :
292 : : /**
293 : : * Try to execute critical section in a hardware memory transaction, if it
294 : : * fails or not available take a write lock
295 : : *
296 : : * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
297 : : * transaction always aborts the transaction since the CPU is not able to
298 : : * roll-back should the transaction fail. Therefore, hardware transactional
299 : : * locks are not advised to be used around rte_eth_rx_burst() and
300 : : * rte_eth_tx_burst() calls.
301 : : *
302 : : * @param rwl
303 : : * A pointer to a rwlock structure.
304 : : */
305 : : static inline void
306 : : rte_rwlock_write_lock_tm(rte_rwlock_t *rwl)
307 : : __rte_acquire_capability(rwl);
308 : :
309 : : /**
310 : : * Commit hardware memory transaction or release the write lock if the lock is used as a fall-back
311 : : *
312 : : * @param rwl
313 : : * A pointer to a rwlock structure.
314 : : */
315 : : static inline void
316 : : rte_rwlock_write_unlock_tm(rte_rwlock_t *rwl)
317 : : __rte_release_capability(rwl);
318 : :
319 : : #ifdef __cplusplus
320 : : }
321 : : #endif
322 : :
323 : : #endif /* _RTE_RWLOCK_H_ */
|