199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation 399a2dd95SBruce Richardson */ 499a2dd95SBruce Richardson 599a2dd95SBruce Richardson #ifndef _RTE_SPINLOCK_H_ 699a2dd95SBruce Richardson #define _RTE_SPINLOCK_H_ 799a2dd95SBruce Richardson 899a2dd95SBruce Richardson /** 999a2dd95SBruce Richardson * @file 1099a2dd95SBruce Richardson * 1199a2dd95SBruce Richardson * RTE Spinlocks 1299a2dd95SBruce Richardson * 1399a2dd95SBruce Richardson * This file defines an API for read-write locks, which are implemented 1499a2dd95SBruce Richardson * in an architecture-specific way. This kind of lock simply waits in 1599a2dd95SBruce Richardson * a loop repeatedly checking until the lock becomes available. 1699a2dd95SBruce Richardson * 1799a2dd95SBruce Richardson * All locks must be initialised before use, and only initialised once. 1899a2dd95SBruce Richardson */ 1999a2dd95SBruce Richardson 2099a2dd95SBruce Richardson #include <rte_lcore.h> 2199a2dd95SBruce Richardson #ifdef RTE_FORCE_INTRINSICS 2299a2dd95SBruce Richardson #include <rte_common.h> 2399a2dd95SBruce Richardson #endif 24657a98f3SDavid Marchand #include <rte_lock_annotations.h> 2599a2dd95SBruce Richardson #include <rte_pause.h> 261ec6a845STyler Retzlaff #include <rte_stdatomic.h> 2799a2dd95SBruce Richardson 28*719834a6SMattias Rönnblom #ifdef __cplusplus 29*719834a6SMattias Rönnblom extern "C" { 30*719834a6SMattias Rönnblom #endif 31*719834a6SMattias Rönnblom 3299a2dd95SBruce Richardson /** 3399a2dd95SBruce Richardson * The rte_spinlock_t type. 3499a2dd95SBruce Richardson */ 35657a98f3SDavid Marchand typedef struct __rte_lockable { 361ec6a845STyler Retzlaff volatile RTE_ATOMIC(int) locked; /**< lock status 0 = unlocked, 1 = locked */ 3799a2dd95SBruce Richardson } rte_spinlock_t; 3899a2dd95SBruce Richardson 3999a2dd95SBruce Richardson /** 4099a2dd95SBruce Richardson * A static spinlock initializer. 4199a2dd95SBruce Richardson */ 4299a2dd95SBruce Richardson #define RTE_SPINLOCK_INITIALIZER { 0 } 4399a2dd95SBruce Richardson 4499a2dd95SBruce Richardson /** 4599a2dd95SBruce Richardson * Initialize the spinlock to an unlocked state. 4699a2dd95SBruce Richardson * 4799a2dd95SBruce Richardson * @param sl 4899a2dd95SBruce Richardson * A pointer to the spinlock. 4999a2dd95SBruce Richardson */ 5099a2dd95SBruce Richardson static inline void 5199a2dd95SBruce Richardson rte_spinlock_init(rte_spinlock_t *sl) 5299a2dd95SBruce Richardson { 5399a2dd95SBruce Richardson sl->locked = 0; 5499a2dd95SBruce Richardson } 5599a2dd95SBruce Richardson 5699a2dd95SBruce Richardson /** 5799a2dd95SBruce Richardson * Take the spinlock. 5899a2dd95SBruce Richardson * 5999a2dd95SBruce Richardson * @param sl 6099a2dd95SBruce Richardson * A pointer to the spinlock. 6199a2dd95SBruce Richardson */ 6299a2dd95SBruce Richardson static inline void 63657a98f3SDavid Marchand rte_spinlock_lock(rte_spinlock_t *sl) 64657a98f3SDavid Marchand __rte_exclusive_lock_function(sl); 6599a2dd95SBruce Richardson 6699a2dd95SBruce Richardson #ifdef RTE_FORCE_INTRINSICS 6799a2dd95SBruce Richardson static inline void 6899a2dd95SBruce Richardson rte_spinlock_lock(rte_spinlock_t *sl) 69657a98f3SDavid Marchand __rte_no_thread_safety_analysis 7099a2dd95SBruce Richardson { 7199a2dd95SBruce Richardson int exp = 0; 7299a2dd95SBruce Richardson 731ec6a845STyler Retzlaff while (!rte_atomic_compare_exchange_strong_explicit(&sl->locked, &exp, 1, 741ec6a845STyler Retzlaff rte_memory_order_acquire, rte_memory_order_relaxed)) { 751ec6a845STyler Retzlaff rte_wait_until_equal_32((volatile uint32_t *)(uintptr_t)&sl->locked, 761ec6a845STyler Retzlaff 0, rte_memory_order_relaxed); 7799a2dd95SBruce Richardson exp = 0; 7899a2dd95SBruce Richardson } 7999a2dd95SBruce Richardson } 8099a2dd95SBruce Richardson #endif 8199a2dd95SBruce Richardson 8299a2dd95SBruce Richardson /** 8399a2dd95SBruce Richardson * Release the spinlock. 8499a2dd95SBruce Richardson * 8599a2dd95SBruce Richardson * @param sl 8699a2dd95SBruce Richardson * A pointer to the spinlock. 8799a2dd95SBruce Richardson */ 8899a2dd95SBruce Richardson static inline void 89657a98f3SDavid Marchand rte_spinlock_unlock(rte_spinlock_t *sl) 90657a98f3SDavid Marchand __rte_unlock_function(sl); 9199a2dd95SBruce Richardson 9299a2dd95SBruce Richardson #ifdef RTE_FORCE_INTRINSICS 9399a2dd95SBruce Richardson static inline void 9499a2dd95SBruce Richardson rte_spinlock_unlock(rte_spinlock_t *sl) 95657a98f3SDavid Marchand __rte_no_thread_safety_analysis 9699a2dd95SBruce Richardson { 971ec6a845STyler Retzlaff rte_atomic_store_explicit(&sl->locked, 0, rte_memory_order_release); 9899a2dd95SBruce Richardson } 9999a2dd95SBruce Richardson #endif 10099a2dd95SBruce Richardson 10199a2dd95SBruce Richardson /** 10299a2dd95SBruce Richardson * Try to take the lock. 10399a2dd95SBruce Richardson * 10499a2dd95SBruce Richardson * @param sl 10599a2dd95SBruce Richardson * A pointer to the spinlock. 10699a2dd95SBruce Richardson * @return 10799a2dd95SBruce Richardson * 1 if the lock is successfully taken; 0 otherwise. 10899a2dd95SBruce Richardson */ 10976076342SMattias Rönnblom __rte_warn_unused_result 11099a2dd95SBruce Richardson static inline int 111657a98f3SDavid Marchand rte_spinlock_trylock(rte_spinlock_t *sl) 112657a98f3SDavid Marchand __rte_exclusive_trylock_function(1, sl); 11399a2dd95SBruce Richardson 11499a2dd95SBruce Richardson #ifdef RTE_FORCE_INTRINSICS 11599a2dd95SBruce Richardson static inline int 11699a2dd95SBruce Richardson rte_spinlock_trylock(rte_spinlock_t *sl) 117657a98f3SDavid Marchand __rte_no_thread_safety_analysis 11899a2dd95SBruce Richardson { 11999a2dd95SBruce Richardson int exp = 0; 1201ec6a845STyler Retzlaff return rte_atomic_compare_exchange_strong_explicit(&sl->locked, &exp, 1, 1211ec6a845STyler Retzlaff rte_memory_order_acquire, rte_memory_order_relaxed); 12299a2dd95SBruce Richardson } 12399a2dd95SBruce Richardson #endif 12499a2dd95SBruce Richardson 12599a2dd95SBruce Richardson /** 12699a2dd95SBruce Richardson * Test if the lock is taken. 12799a2dd95SBruce Richardson * 12899a2dd95SBruce Richardson * @param sl 12999a2dd95SBruce Richardson * A pointer to the spinlock. 13099a2dd95SBruce Richardson * @return 13199a2dd95SBruce Richardson * 1 if the lock is currently taken; 0 otherwise. 13299a2dd95SBruce Richardson */ 13399a2dd95SBruce Richardson static inline int rte_spinlock_is_locked (rte_spinlock_t *sl) 13499a2dd95SBruce Richardson { 1351ec6a845STyler Retzlaff return rte_atomic_load_explicit(&sl->locked, rte_memory_order_acquire); 13699a2dd95SBruce Richardson } 13799a2dd95SBruce Richardson 13899a2dd95SBruce Richardson /** 13999a2dd95SBruce Richardson * Test if hardware transactional memory (lock elision) is supported 14099a2dd95SBruce Richardson * 14199a2dd95SBruce Richardson * @return 14299a2dd95SBruce Richardson * 1 if the hardware transactional memory is supported; 0 otherwise. 14399a2dd95SBruce Richardson */ 14499a2dd95SBruce Richardson static inline int rte_tm_supported(void); 14599a2dd95SBruce Richardson 14699a2dd95SBruce Richardson /** 14799a2dd95SBruce Richardson * Try to execute critical section in a hardware memory transaction, 14899a2dd95SBruce Richardson * if it fails or not available take the spinlock. 14999a2dd95SBruce Richardson * 15099a2dd95SBruce Richardson * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 15199a2dd95SBruce Richardson * transaction always aborts the transaction since the CPU is not able to 15299a2dd95SBruce Richardson * roll-back should the transaction fail. Therefore, hardware transactional 15399a2dd95SBruce Richardson * locks are not advised to be used around rte_eth_rx_burst() and 15499a2dd95SBruce Richardson * rte_eth_tx_burst() calls. 15599a2dd95SBruce Richardson * 15699a2dd95SBruce Richardson * @param sl 15799a2dd95SBruce Richardson * A pointer to the spinlock. 15899a2dd95SBruce Richardson */ 15999a2dd95SBruce Richardson static inline void 160657a98f3SDavid Marchand rte_spinlock_lock_tm(rte_spinlock_t *sl) 161657a98f3SDavid Marchand __rte_exclusive_lock_function(sl); 16299a2dd95SBruce Richardson 16399a2dd95SBruce Richardson /** 16499a2dd95SBruce Richardson * Commit hardware memory transaction or release the spinlock if 16599a2dd95SBruce Richardson * the spinlock is used as a fall-back 16699a2dd95SBruce Richardson * 16799a2dd95SBruce Richardson * @param sl 16899a2dd95SBruce Richardson * A pointer to the spinlock. 16999a2dd95SBruce Richardson */ 17099a2dd95SBruce Richardson static inline void 171657a98f3SDavid Marchand rte_spinlock_unlock_tm(rte_spinlock_t *sl) 172657a98f3SDavid Marchand __rte_unlock_function(sl); 17399a2dd95SBruce Richardson 17499a2dd95SBruce Richardson /** 17599a2dd95SBruce Richardson * Try to execute critical section in a hardware memory transaction, 17699a2dd95SBruce Richardson * if it fails or not available try to take the lock. 17799a2dd95SBruce Richardson * 17899a2dd95SBruce Richardson * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 17999a2dd95SBruce Richardson * transaction always aborts the transaction since the CPU is not able to 18099a2dd95SBruce Richardson * roll-back should the transaction fail. Therefore, hardware transactional 18199a2dd95SBruce Richardson * locks are not advised to be used around rte_eth_rx_burst() and 18299a2dd95SBruce Richardson * rte_eth_tx_burst() calls. 18399a2dd95SBruce Richardson * 18499a2dd95SBruce Richardson * @param sl 18599a2dd95SBruce Richardson * A pointer to the spinlock. 18699a2dd95SBruce Richardson * @return 18799a2dd95SBruce Richardson * 1 if the hardware memory transaction is successfully started 18899a2dd95SBruce Richardson * or lock is successfully taken; 0 otherwise. 18999a2dd95SBruce Richardson */ 19076076342SMattias Rönnblom __rte_warn_unused_result 19199a2dd95SBruce Richardson static inline int 192657a98f3SDavid Marchand rte_spinlock_trylock_tm(rte_spinlock_t *sl) 193657a98f3SDavid Marchand __rte_exclusive_trylock_function(1, sl); 19499a2dd95SBruce Richardson 19599a2dd95SBruce Richardson /** 19699a2dd95SBruce Richardson * The rte_spinlock_recursive_t type. 19799a2dd95SBruce Richardson */ 19899a2dd95SBruce Richardson typedef struct { 19999a2dd95SBruce Richardson rte_spinlock_t sl; /**< the actual spinlock */ 20099a2dd95SBruce Richardson volatile int user; /**< core id using lock, -1 for unused */ 20199a2dd95SBruce Richardson volatile int count; /**< count of time this lock has been called */ 20299a2dd95SBruce Richardson } rte_spinlock_recursive_t; 20399a2dd95SBruce Richardson 20499a2dd95SBruce Richardson /** 20599a2dd95SBruce Richardson * A static recursive spinlock initializer. 20699a2dd95SBruce Richardson */ 20799a2dd95SBruce Richardson #define RTE_SPINLOCK_RECURSIVE_INITIALIZER {RTE_SPINLOCK_INITIALIZER, -1, 0} 20899a2dd95SBruce Richardson 20999a2dd95SBruce Richardson /** 21099a2dd95SBruce Richardson * Initialize the recursive spinlock to an unlocked state. 21199a2dd95SBruce Richardson * 21299a2dd95SBruce Richardson * @param slr 21399a2dd95SBruce Richardson * A pointer to the recursive spinlock. 21499a2dd95SBruce Richardson */ 21599a2dd95SBruce Richardson static inline void rte_spinlock_recursive_init(rte_spinlock_recursive_t *slr) 21699a2dd95SBruce Richardson { 21799a2dd95SBruce Richardson rte_spinlock_init(&slr->sl); 21899a2dd95SBruce Richardson slr->user = -1; 21999a2dd95SBruce Richardson slr->count = 0; 22099a2dd95SBruce Richardson } 22199a2dd95SBruce Richardson 22299a2dd95SBruce Richardson /** 22399a2dd95SBruce Richardson * Take the recursive spinlock. 22499a2dd95SBruce Richardson * 22599a2dd95SBruce Richardson * @param slr 22699a2dd95SBruce Richardson * A pointer to the recursive spinlock. 22799a2dd95SBruce Richardson */ 22899a2dd95SBruce Richardson static inline void rte_spinlock_recursive_lock(rte_spinlock_recursive_t *slr) 229657a98f3SDavid Marchand __rte_no_thread_safety_analysis 23099a2dd95SBruce Richardson { 23199a2dd95SBruce Richardson int id = rte_gettid(); 23299a2dd95SBruce Richardson 23399a2dd95SBruce Richardson if (slr->user != id) { 23499a2dd95SBruce Richardson rte_spinlock_lock(&slr->sl); 23599a2dd95SBruce Richardson slr->user = id; 23699a2dd95SBruce Richardson } 23799a2dd95SBruce Richardson slr->count++; 23899a2dd95SBruce Richardson } 23999a2dd95SBruce Richardson /** 24099a2dd95SBruce Richardson * Release the recursive spinlock. 24199a2dd95SBruce Richardson * 24299a2dd95SBruce Richardson * @param slr 24399a2dd95SBruce Richardson * A pointer to the recursive spinlock. 24499a2dd95SBruce Richardson */ 24599a2dd95SBruce Richardson static inline void rte_spinlock_recursive_unlock(rte_spinlock_recursive_t *slr) 246657a98f3SDavid Marchand __rte_no_thread_safety_analysis 24799a2dd95SBruce Richardson { 24899a2dd95SBruce Richardson if (--(slr->count) == 0) { 24999a2dd95SBruce Richardson slr->user = -1; 25099a2dd95SBruce Richardson rte_spinlock_unlock(&slr->sl); 25199a2dd95SBruce Richardson } 25299a2dd95SBruce Richardson 25399a2dd95SBruce Richardson } 25499a2dd95SBruce Richardson 25599a2dd95SBruce Richardson /** 25699a2dd95SBruce Richardson * Try to take the recursive lock. 25799a2dd95SBruce Richardson * 25899a2dd95SBruce Richardson * @param slr 25999a2dd95SBruce Richardson * A pointer to the recursive spinlock. 26099a2dd95SBruce Richardson * @return 26199a2dd95SBruce Richardson * 1 if the lock is successfully taken; 0 otherwise. 26299a2dd95SBruce Richardson */ 26376076342SMattias Rönnblom __rte_warn_unused_result 26499a2dd95SBruce Richardson static inline int rte_spinlock_recursive_trylock(rte_spinlock_recursive_t *slr) 265657a98f3SDavid Marchand __rte_no_thread_safety_analysis 26699a2dd95SBruce Richardson { 26799a2dd95SBruce Richardson int id = rte_gettid(); 26899a2dd95SBruce Richardson 26999a2dd95SBruce Richardson if (slr->user != id) { 27099a2dd95SBruce Richardson if (rte_spinlock_trylock(&slr->sl) == 0) 27199a2dd95SBruce Richardson return 0; 27299a2dd95SBruce Richardson slr->user = id; 27399a2dd95SBruce Richardson } 27499a2dd95SBruce Richardson slr->count++; 27599a2dd95SBruce Richardson return 1; 27699a2dd95SBruce Richardson } 27799a2dd95SBruce Richardson 27899a2dd95SBruce Richardson 27999a2dd95SBruce Richardson /** 28099a2dd95SBruce Richardson * Try to execute critical section in a hardware memory transaction, 28199a2dd95SBruce Richardson * if it fails or not available take the recursive spinlocks 28299a2dd95SBruce Richardson * 28399a2dd95SBruce Richardson * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 28499a2dd95SBruce Richardson * transaction always aborts the transaction since the CPU is not able to 28599a2dd95SBruce Richardson * roll-back should the transaction fail. Therefore, hardware transactional 28699a2dd95SBruce Richardson * locks are not advised to be used around rte_eth_rx_burst() and 28799a2dd95SBruce Richardson * rte_eth_tx_burst() calls. 28899a2dd95SBruce Richardson * 28999a2dd95SBruce Richardson * @param slr 29099a2dd95SBruce Richardson * A pointer to the recursive spinlock. 29199a2dd95SBruce Richardson */ 29299a2dd95SBruce Richardson static inline void rte_spinlock_recursive_lock_tm( 29399a2dd95SBruce Richardson rte_spinlock_recursive_t *slr); 29499a2dd95SBruce Richardson 29599a2dd95SBruce Richardson /** 29699a2dd95SBruce Richardson * Commit hardware memory transaction or release the recursive spinlock 29799a2dd95SBruce Richardson * if the recursive spinlock is used as a fall-back 29899a2dd95SBruce Richardson * 29999a2dd95SBruce Richardson * @param slr 30099a2dd95SBruce Richardson * A pointer to the recursive spinlock. 30199a2dd95SBruce Richardson */ 30299a2dd95SBruce Richardson static inline void rte_spinlock_recursive_unlock_tm( 30399a2dd95SBruce Richardson rte_spinlock_recursive_t *slr); 30499a2dd95SBruce Richardson 30599a2dd95SBruce Richardson /** 30699a2dd95SBruce Richardson * Try to execute critical section in a hardware memory transaction, 30799a2dd95SBruce Richardson * if it fails or not available try to take the recursive lock 30899a2dd95SBruce Richardson * 30999a2dd95SBruce Richardson * NOTE: An attempt to perform a HW I/O operation inside a hardware memory 31099a2dd95SBruce Richardson * transaction always aborts the transaction since the CPU is not able to 31199a2dd95SBruce Richardson * roll-back should the transaction fail. Therefore, hardware transactional 31299a2dd95SBruce Richardson * locks are not advised to be used around rte_eth_rx_burst() and 31399a2dd95SBruce Richardson * rte_eth_tx_burst() calls. 31499a2dd95SBruce Richardson * 31599a2dd95SBruce Richardson * @param slr 31699a2dd95SBruce Richardson * A pointer to the recursive spinlock. 31799a2dd95SBruce Richardson * @return 31899a2dd95SBruce Richardson * 1 if the hardware memory transaction is successfully started 31999a2dd95SBruce Richardson * or lock is successfully taken; 0 otherwise. 32099a2dd95SBruce Richardson */ 32176076342SMattias Rönnblom __rte_warn_unused_result 32299a2dd95SBruce Richardson static inline int rte_spinlock_recursive_trylock_tm( 32399a2dd95SBruce Richardson rte_spinlock_recursive_t *slr); 32499a2dd95SBruce Richardson 325*719834a6SMattias Rönnblom #ifdef __cplusplus 326*719834a6SMattias Rönnblom } 327*719834a6SMattias Rönnblom #endif 328*719834a6SMattias Rönnblom 32999a2dd95SBruce Richardson #endif /* _RTE_SPINLOCK_H_ */ 330