xref: /dpdk/lib/eal/include/generic/rte_spinlock.h (revision 719834a6849e1daf4a70ff7742bbcc3ae7e25607)
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