rwlock: reimplement with atomic builtins
[dpdk.git] / lib / librte_eal / common / include / generic / rte_rwlock.h
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
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
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 static inline __rte_experimental int
92 rte_rwlock_read_trylock(rte_rwlock_t *rwl)
93 {
94         int32_t x;
95         int success = 0;
96
97         while (success == 0) {
98                 x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
99                 /* write lock is held */
100                 if (x < 0)
101                         return -EBUSY;
102                 success = __atomic_compare_exchange_n(&rwl->cnt, &x, x + 1, 1,
103                                         __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
104         }
105
106         return 0;
107 }
108
109 /**
110  * Release a read lock.
111  *
112  * @param rwl
113  *   A pointer to the rwlock structure.
114  */
115 static inline void
116 rte_rwlock_read_unlock(rte_rwlock_t *rwl)
117 {
118         __atomic_fetch_sub(&rwl->cnt, 1, __ATOMIC_RELEASE);
119 }
120
121 /**
122  * @warning
123  * @b EXPERIMENTAL: this API may change without prior notice.
124  *
125  * try to take a write lock.
126  *
127  * @param rwl
128  *   A pointer to a rwlock structure.
129  * @return
130  *   - zero if the lock is successfully taken
131  *   - -EBUSY if lock could not be acquired for writing because
132  *     it was already locked for reading or writing
133  */
134 static inline __rte_experimental int
135 rte_rwlock_write_trylock(rte_rwlock_t *rwl)
136 {
137         int32_t x;
138
139         x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
140         if (x != 0 || __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,
141                               __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) == 0)
142                 return -EBUSY;
143
144         return 0;
145 }
146
147 /**
148  * Take a write lock. Loop until the lock is held.
149  *
150  * @param rwl
151  *   A pointer to a rwlock structure.
152  */
153 static inline void
154 rte_rwlock_write_lock(rte_rwlock_t *rwl)
155 {
156         int32_t x;
157         int success = 0;
158
159         while (success == 0) {
160                 x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
161                 /* a lock is held */
162                 if (x != 0) {
163                         rte_pause();
164                         continue;
165                 }
166                 success = __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,
167                                         __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
168         }
169 }
170
171 /**
172  * Release a write lock.
173  *
174  * @param rwl
175  *   A pointer to a rwlock structure.
176  */
177 static inline void
178 rte_rwlock_write_unlock(rte_rwlock_t *rwl)
179 {
180         __atomic_store_n(&rwl->cnt, 0, __ATOMIC_RELEASE);
181 }
182
183 /**
184  * Try to execute critical section in a hardware memory transaction, if it
185  * fails or not available take a read lock
186  *
187  * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
188  * transaction always aborts the transaction since the CPU is not able to
189  * roll-back should the transaction fail. Therefore, hardware transactional
190  * locks are not advised to be used around rte_eth_rx_burst() and
191  * rte_eth_tx_burst() calls.
192  *
193  * @param rwl
194  *   A pointer to a rwlock structure.
195  */
196 static inline void
197 rte_rwlock_read_lock_tm(rte_rwlock_t *rwl);
198
199 /**
200  * Commit hardware memory transaction or release the read lock if the lock is used as a fall-back
201  *
202  * @param rwl
203  *   A pointer to the rwlock structure.
204  */
205 static inline void
206 rte_rwlock_read_unlock_tm(rte_rwlock_t *rwl);
207
208 /**
209  * Try to execute critical section in a hardware memory transaction, if it
210  * fails or not available take a write lock
211  *
212  * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
213  * transaction always aborts the transaction since the CPU is not able to
214  * roll-back should the transaction fail. Therefore, hardware transactional
215  * locks are not advised to be used around rte_eth_rx_burst() and
216  * rte_eth_tx_burst() calls.
217  *
218  * @param rwl
219  *   A pointer to a rwlock structure.
220  */
221 static inline void
222 rte_rwlock_write_lock_tm(rte_rwlock_t *rwl);
223
224 /**
225  * Commit hardware memory transaction or release the write lock if the lock is used as a fall-back
226  *
227  * @param rwl
228  *   A pointer to a rwlock structure.
229  */
230 static inline void
231 rte_rwlock_write_unlock_tm(rte_rwlock_t *rwl);
232
233 #ifdef __cplusplus
234 }
235 #endif
236
237 #endif /* _RTE_RWLOCK_H_ */