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_SPINLOCK_H_ 6 : : #define _RTE_SPINLOCK_H_ 7 : : 8 : : /** 9 : : * @file 10 : : * DPDK spinlocks 11 : : * 12 : : * This is an API for spinlocks. 13 : : * This kind of lock simply waits in a loop 14 : : * repeatedly checking until the lock becomes available. 15 : : * 16 : : * Some functions may have an architecture-specific implementation 17 : : * if RTE_FORCE_INTRINSICS is disabled. 18 : : * The hardware transactional memory (lock elision) functions have _tm suffix 19 : : * and are implemented in architecture-specific files. 20 : : * 21 : : * All locks must be initialised before use, and only initialised once. 22 : : */ 23 : : 24 : : #include <rte_lcore.h> 25 : : #ifdef RTE_FORCE_INTRINSICS 26 : : #include <rte_common.h> 27 : : #endif 28 : : #include <rte_debug.h> 29 : : #include <rte_lock_annotations.h> 30 : : #include <rte_pause.h> 31 : : #include <rte_stdatomic.h> 32 : : 33 : : #ifdef __cplusplus 34 : : extern "C" { 35 : : #endif 36 : : 37 : : /** 38 : : * The rte_spinlock_t type. 39 : : */ 40 : : typedef struct __rte_capability("spinlock") { 41 : : volatile RTE_ATOMIC(int) locked; /**< lock status 0 = unlocked, 1 = locked */ 42 : : } rte_spinlock_t; 43 : : 44 : : /** 45 : : * A static spinlock initializer. 46 : : */ 47 : : #define RTE_SPINLOCK_INITIALIZER { 0 } 48 : : 49 : : /** 50 : : * Initialize the spinlock to an unlocked state. 51 : : * 52 : : * @param sl 53 : : * A pointer to the spinlock. 54 : : */ 55 : : static inline void 56 : : rte_spinlock_init(rte_spinlock_t *sl) 57 : : { 58 [ # # # # ]: 1499749 : sl->locked = 0; 59 : 0 : } 60 : : 61 : : /** 62 : : * Take the spinlock. 63 : : * 64 : : * @param sl 65 : : * A pointer to the spinlock. 66 : : */ 67 : : static inline void 68 : : rte_spinlock_lock(rte_spinlock_t *sl) 69 : : __rte_acquire_capability(sl); 70 : : 71 : : #ifdef RTE_FORCE_INTRINSICS 72 : : static inline void 73 : : rte_spinlock_lock(rte_spinlock_t *sl) 74 : : __rte_no_thread_safety_analysis 75 : : { 76 : : int exp = 0; 77 : : 78 : : while (!rte_atomic_compare_exchange_strong_explicit(&sl->locked, &exp, 1, 79 : : rte_memory_order_acquire, rte_memory_order_relaxed)) { 80 : : rte_wait_until_equal_32((volatile uint32_t *)(uintptr_t)&sl->locked, 81 : : 0, rte_memory_order_relaxed); 82 : : exp = 0; 83 : : } 84 : : } 85 : : #endif 86 : : 87 : : /** 88 : : * Release the spinlock. 89 : : * 90 : : * @param sl 91 : : * A pointer to the spinlock. 92 : : */ 93 : : static inline void 94 : : rte_spinlock_unlock(rte_spinlock_t *sl) 95 : : __rte_release_capability(sl); 96 : : 97 : : #ifdef RTE_FORCE_INTRINSICS 98 : : static inline void 99 : : rte_spinlock_unlock(rte_spinlock_t *sl) 100 : : __rte_no_thread_safety_analysis 101 : : { 102 : : rte_atomic_store_explicit(&sl->locked, 0, rte_memory_order_release); 103 : : } 104 : : #endif 105 : : 106 : : /** 107 : : * Try to take the lock. 108 : : * 109 : : * @param sl 110 : : * A pointer to the spinlock. 111 : : * @return 112 : : * 1 if the lock is successfully taken; 0 otherwise. 113 : : */ 114 : : __rte_warn_unused_result 115 : : static inline int 116 : : rte_spinlock_trylock(rte_spinlock_t *sl) 117 : : __rte_try_acquire_capability(true, sl); 118 : : 119 : : #ifdef RTE_FORCE_INTRINSICS 120 : : static inline int 121 : : rte_spinlock_trylock(rte_spinlock_t *sl) 122 : : __rte_no_thread_safety_analysis 123 : : { 124 : : int exp = 0; 125 : : return rte_atomic_compare_exchange_strong_explicit(&sl->locked, &exp, 1, 126 : : rte_memory_order_acquire, rte_memory_order_relaxed); 127 : : } 128 : : #endif 129 : : 130 : : /** 131 : : * Test if the lock is taken. 132 : : * 133 : : * @param sl 134 : : * A pointer to the spinlock. 135 : : * @return 136 : : * 1 if the lock is currently taken; 0 otherwise. 137 : : */ 138 : : static inline int rte_spinlock_is_locked (rte_spinlock_t *sl) 139 : : { 140 [ - + ]: 1 : return rte_atomic_load_explicit(&sl->locked, rte_memory_order_acquire); 141 : : } 142 : : 143 : : /** 144 : : * Test if hardware transactional memory (lock elision) is supported 145 : : * 146 : : * @return 147 : : * 1 if the hardware transactional memory is supported; 0 otherwise. 148 : : */ 149 : : static inline int rte_tm_supported(void); 150 : : 151 : : /** 152 : : * Try to execute critical section in a hardware memory transaction, 153 : : * if it fails or not available take the spinlock. 154 : : * 155 : : * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 156 : : * transaction always aborts the transaction since the CPU is not able to 157 : : * roll-back should the transaction fail. Therefore, hardware transactional 158 : : * locks are not advised to be used around rte_eth_rx_burst() and 159 : : * rte_eth_tx_burst() calls. 160 : : * 161 : : * @param sl 162 : : * A pointer to the spinlock. 163 : : */ 164 : : static inline void 165 : : rte_spinlock_lock_tm(rte_spinlock_t *sl) 166 : : __rte_acquire_capability(sl); 167 : : 168 : : /** 169 : : * Commit hardware memory transaction or release the spinlock if 170 : : * the spinlock is used as a fall-back 171 : : * 172 : : * @param sl 173 : : * A pointer to the spinlock. 174 : : */ 175 : : static inline void 176 : : rte_spinlock_unlock_tm(rte_spinlock_t *sl) 177 : : __rte_release_capability(sl); 178 : : 179 : : /** 180 : : * Try to execute critical section in a hardware memory transaction, 181 : : * if it fails or not available try to take the lock. 182 : : * 183 : : * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 184 : : * transaction always aborts the transaction since the CPU is not able to 185 : : * roll-back should the transaction fail. Therefore, hardware transactional 186 : : * locks are not advised to be used around rte_eth_rx_burst() and 187 : : * rte_eth_tx_burst() calls. 188 : : * 189 : : * @param sl 190 : : * A pointer to the spinlock. 191 : : * @return 192 : : * 1 if the hardware memory transaction is successfully started 193 : : * or lock is successfully taken; 0 otherwise. 194 : : */ 195 : : __rte_warn_unused_result 196 : : static inline int 197 : : rte_spinlock_trylock_tm(rte_spinlock_t *sl) 198 : : __rte_try_acquire_capability(true, sl); 199 : : 200 : : /** 201 : : * The rte_spinlock_recursive_t type. 202 : : */ 203 : : typedef struct { 204 : : rte_spinlock_t sl; /**< the actual spinlock */ 205 : : RTE_ATOMIC(int) owner; /**< thread id owning lock, -1 for unused */ 206 : : int count; /**< count of time this lock has been called */ 207 : : } rte_spinlock_recursive_t; 208 : : 209 : : /** 210 : : * A static recursive spinlock initializer. 211 : : */ 212 : : #define RTE_SPINLOCK_RECURSIVE_INITIALIZER {RTE_SPINLOCK_INITIALIZER, -1, 0} 213 : : 214 : : /** 215 : : * Initialize the recursive spinlock to an unlocked state. 216 : : * 217 : : * @param slr 218 : : * A pointer to the recursive spinlock. 219 : : */ 220 : : static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr) 221 : : { 222 : : rte_spinlock_init(&slr->sl); 223 : 1 : rte_atomic_store_explicit(&slr->owner, -1, rte_memory_order_relaxed); 224 : 1 : slr->count = 0; 225 : : } 226 : : 227 : : /** 228 : : * Take the recursive spinlock. 229 : : * 230 : : * @param slr 231 : : * A pointer to the recursive spinlock. 232 : : */ 233 [ + + ]: 171 : static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr) 234 : : __rte_no_thread_safety_analysis 235 : : { 236 : : int id = rte_gettid(); 237 : : 238 [ + + ]: 171 : if (rte_atomic_load_explicit(&slr->owner, rte_memory_order_relaxed) != id) { 239 : 169 : rte_spinlock_lock(&slr->sl); 240 : 169 : rte_atomic_store_explicit(&slr->owner, id, rte_memory_order_relaxed); 241 : : } 242 : 171 : slr->count++; 243 : 171 : } 244 : : /** 245 : : * Release the recursive spinlock. 246 : : * 247 : : * @param slr 248 : : * A pointer to the recursive spinlock. 249 : : */ 250 : 174 : static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr) 251 : : __rte_no_thread_safety_analysis 252 : : { 253 : : RTE_ASSERT(rte_atomic_load_explicit(&slr->owner, rte_memory_order_relaxed) == rte_gettid()); 254 : : RTE_ASSERT(slr->count > 0); 255 [ + + ]: 174 : if (--(slr->count) == 0) { 256 : 170 : rte_atomic_store_explicit(&slr->owner, -1, rte_memory_order_relaxed); 257 : 170 : rte_spinlock_unlock(&slr->sl); 258 : : } 259 : 174 : } 260 : : 261 : : /** 262 : : * Try to take the recursive lock. 263 : : * 264 : : * @param slr 265 : : * A pointer to the recursive spinlock. 266 : : * @return 267 : : * 1 if the lock is successfully taken; 0 otherwise. 268 : : */ 269 : : __rte_warn_unused_result 270 [ - + ]: 3 : static inline int rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr) 271 : : __rte_no_thread_safety_analysis 272 : : { 273 : : int id = rte_gettid(); 274 : : 275 [ + + ]: 3 : if (rte_atomic_load_explicit(&slr->owner, rte_memory_order_relaxed) != id) { 276 [ + - ]: 1 : if (rte_spinlock_trylock(&slr->sl) == 0) 277 : : return 0; 278 : 1 : rte_atomic_store_explicit(&slr->owner, id, rte_memory_order_relaxed); 279 : : } 280 : 3 : slr->count++; 281 : 3 : return 1; 282 : : } 283 : : 284 : : 285 : : /** 286 : : * Try to execute critical section in a hardware memory transaction, 287 : : * if it fails or not available take the recursive spinlocks 288 : : * 289 : : * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 290 : : * transaction always aborts the transaction since the CPU is not able to 291 : : * roll-back should the transaction fail. Therefore, hardware transactional 292 : : * locks are not advised to be used around rte_eth_rx_burst() and 293 : : * rte_eth_tx_burst() calls. 294 : : * 295 : : * @param slr 296 : : * A pointer to the recursive spinlock. 297 : : */ 298 : : static inline void rte_spinlock_recursive_lock_tm( 299 : : rte_spinlock_recursive_t *slr); 300 : : 301 : : /** 302 : : * Commit hardware memory transaction or release the recursive spinlock 303 : : * if the recursive spinlock is used as a fall-back 304 : : * 305 : : * @param slr 306 : : * A pointer to the recursive spinlock. 307 : : */ 308 : : static inline void rte_spinlock_recursive_unlock_tm( 309 : : rte_spinlock_recursive_t *slr); 310 : : 311 : : /** 312 : : * Try to execute critical section in a hardware memory transaction, 313 : : * if it fails or not available try to take the recursive lock 314 : : * 315 : : * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 316 : : * transaction always aborts the transaction since the CPU is not able to 317 : : * roll-back should the transaction fail. Therefore, hardware transactional 318 : : * locks are not advised to be used around rte_eth_rx_burst() and 319 : : * rte_eth_tx_burst() calls. 320 : : * 321 : : * @param slr 322 : : * A pointer to the recursive spinlock. 323 : : * @return 324 : : * 1 if the hardware memory transaction is successfully started 325 : : * or lock is successfully taken; 0 otherwise. 326 : : */ 327 : : __rte_warn_unused_result 328 : : static inline int rte_spinlock_recursive_trylock_tm( 329 : : rte_spinlock_recursive_t *slr); 330 : : 331 : : #ifdef __cplusplus 332 : : } 333 : : #endif 334 : : 335 : : #endif /* _RTE_SPINLOCK_H_ */