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 */
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 #include <rte_common.h>
25 #include <rte_atomic.h>
26 #include <rte_pause.h>
27
28 /**
29 * The rte_rwlock_t type.
30 *
31 * cnt is -1 when write lock is held, and > 0 when read locks are held.
32 */
33 typedef struct {
34 volatile int32_t cnt; /**< -1 when W lock held, > 0 when R locks held. */
35 } rte_rwlock_t;
36
37 /**
38 * A static rwlock initializer.
39 */
40 #define RTE_RWLOCK_INITIALIZER { 0 }
41
42 /**
43 * Initialize the rwlock to an unlocked state.
44 *
45 * @param rwl
46 * A pointer to the rwlock structure.
47 */
48 static inline void
rte_rwlock_init(rte_rwlock_t * rwl)49 rte_rwlock_init(rte_rwlock_t *rwl)
50 {
51 rwl->cnt = 0;
52 }
53
54 /**
55 * Take a read lock. Loop until the lock is held.
56 *
57 * @param rwl
58 * A pointer to a rwlock structure.
59 */
60 static inline void
rte_rwlock_read_lock(rte_rwlock_t * rwl)61 rte_rwlock_read_lock(rte_rwlock_t *rwl)
62 {
63 int32_t x;
64 int success = 0;
65
66 while (success == 0) {
67 x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
68 /* write lock is held */
69 if (x < 0) {
70 rte_pause();
71 continue;
72 }
73 success = __atomic_compare_exchange_n(&rwl->cnt, &x, x + 1, 1,
74 __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
75 }
76 }
77
78 /**
79 * @warning
80 * @b EXPERIMENTAL: this API may change without prior notice.
81 *
82 * try to take a read lock.
83 *
84 * @param rwl
85 * A pointer to a rwlock structure.
86 * @return
87 * - zero if the lock is successfully taken
88 * - -EBUSY if lock could not be acquired for reading because a
89 * writer holds the lock
90 */
91 __rte_experimental
92 static inline int
rte_rwlock_read_trylock(rte_rwlock_t * rwl)93 rte_rwlock_read_trylock(rte_rwlock_t *rwl)
94 {
95 int32_t x;
96 int success = 0;
97
98 while (success == 0) {
99 x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
100 /* write lock is held */
101 if (x < 0)
102 return -EBUSY;
103 success = __atomic_compare_exchange_n(&rwl->cnt, &x, x + 1, 1,
104 __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
105 }
106
107 return 0;
108 }
109
110 /**
111 * Release a read lock.
112 *
113 * @param rwl
114 * A pointer to the rwlock structure.
115 */
116 static inline void
rte_rwlock_read_unlock(rte_rwlock_t * rwl)117 rte_rwlock_read_unlock(rte_rwlock_t *rwl)
118 {
119 __atomic_fetch_sub(&rwl->cnt, 1, __ATOMIC_RELEASE);
120 }
121
122 /**
123 * @warning
124 * @b EXPERIMENTAL: this API may change without prior notice.
125 *
126 * try to take a write lock.
127 *
128 * @param rwl
129 * A pointer to a rwlock structure.
130 * @return
131 * - zero if the lock is successfully taken
132 * - -EBUSY if lock could not be acquired for writing because
133 * it was already locked for reading or writing
134 */
135 __rte_experimental
136 static inline int
rte_rwlock_write_trylock(rte_rwlock_t * rwl)137 rte_rwlock_write_trylock(rte_rwlock_t *rwl)
138 {
139 int32_t x;
140
141 x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
142 if (x != 0 || __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,
143 __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) == 0)
144 return -EBUSY;
145
146 return 0;
147 }
148
149 /**
150 * Take a write lock. Loop until the lock is held.
151 *
152 * @param rwl
153 * A pointer to a rwlock structure.
154 */
155 static inline void
rte_rwlock_write_lock(rte_rwlock_t * rwl)156 rte_rwlock_write_lock(rte_rwlock_t *rwl)
157 {
158 int32_t x;
159 int success = 0;
160
161 while (success == 0) {
162 x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
163 /* a lock is held */
164 if (x != 0) {
165 rte_pause();
166 continue;
167 }
168 success = __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,
169 __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
170 }
171 }
172
173 /**
174 * Release a write lock.
175 *
176 * @param rwl
177 * A pointer to a rwlock structure.
178 */
179 static inline void
rte_rwlock_write_unlock(rte_rwlock_t * rwl)180 rte_rwlock_write_unlock(rte_rwlock_t *rwl)
181 {
182 __atomic_store_n(&rwl->cnt, 0, __ATOMIC_RELEASE);
183 }
184
185 /**
186 * Try to execute critical section in a hardware memory transaction, if it
187 * fails or not available take a read lock
188 *
189 * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
190 * transaction always aborts the transaction since the CPU is not able to
191 * roll-back should the transaction fail. Therefore, hardware transactional
192 * locks are not advised to be used around rte_eth_rx_burst() and
193 * rte_eth_tx_burst() calls.
194 *
195 * @param rwl
196 * A pointer to a rwlock structure.
197 */
198 static inline void
199 rte_rwlock_read_lock_tm(rte_rwlock_t *rwl);
200
201 /**
202 * Commit hardware memory transaction or release the read lock if the lock is used as a fall-back
203 *
204 * @param rwl
205 * A pointer to the rwlock structure.
206 */
207 static inline void
208 rte_rwlock_read_unlock_tm(rte_rwlock_t *rwl);
209
210 /**
211 * Try to execute critical section in a hardware memory transaction, if it
212 * fails or not available take a write lock
213 *
214 * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
215 * transaction always aborts the transaction since the CPU is not able to
216 * roll-back should the transaction fail. Therefore, hardware transactional
217 * locks are not advised to be used around rte_eth_rx_burst() and
218 * rte_eth_tx_burst() calls.
219 *
220 * @param rwl
221 * A pointer to a rwlock structure.
222 */
223 static inline void
224 rte_rwlock_write_lock_tm(rte_rwlock_t *rwl);
225
226 /**
227 * Commit hardware memory transaction or release the write lock if the lock is used as a fall-back
228 *
229 * @param rwl
230 * A pointer to a rwlock structure.
231 */
232 static inline void
233 rte_rwlock_write_unlock_tm(rte_rwlock_t *rwl);
234
235 #ifdef __cplusplus
236 }
237 #endif
238
239 #endif /* _RTE_RWLOCK_H_ */
240