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