xref: /dpdk/lib/eal/include/generic/rte_rwlock.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_RWLOCK_H_
699a2dd95SBruce Richardson #define _RTE_RWLOCK_H_
799a2dd95SBruce Richardson 
899a2dd95SBruce Richardson /**
999a2dd95SBruce Richardson  * @file
1099a2dd95SBruce Richardson  *
1199a2dd95SBruce Richardson  * RTE Read-Write Locks
1299a2dd95SBruce Richardson  *
1399a2dd95SBruce Richardson  * This file defines an API for read-write locks. The lock is used to
1499a2dd95SBruce Richardson  * protect data that allows multiple readers in parallel, but only
1599a2dd95SBruce Richardson  * one writer. All readers are blocked until the writer is finished
1699a2dd95SBruce Richardson  * writing.
1799a2dd95SBruce Richardson  *
18832cecc0SStephen Hemminger  * This version does not give preference to readers or writers
19832cecc0SStephen Hemminger  * and does not starve either readers or writers.
20832cecc0SStephen Hemminger  *
21832cecc0SStephen Hemminger  * See also:
22832cecc0SStephen Hemminger  *  https://locklessinc.com/articles/locks/
2399a2dd95SBruce Richardson  */
2499a2dd95SBruce Richardson 
2572b452c5SDmitry Kozlyuk #include <errno.h>
2672b452c5SDmitry Kozlyuk 
27832cecc0SStephen Hemminger #include <rte_branch_prediction.h>
2899a2dd95SBruce Richardson #include <rte_common.h>
29657a98f3SDavid Marchand #include <rte_lock_annotations.h>
3099a2dd95SBruce Richardson #include <rte_pause.h>
311ec6a845STyler Retzlaff #include <rte_stdatomic.h>
3299a2dd95SBruce Richardson 
33*719834a6SMattias Rönnblom #ifdef __cplusplus
34*719834a6SMattias Rönnblom extern "C" {
35*719834a6SMattias Rönnblom #endif
36*719834a6SMattias Rönnblom 
3799a2dd95SBruce Richardson /**
3899a2dd95SBruce Richardson  * The rte_rwlock_t type.
3999a2dd95SBruce Richardson  *
40832cecc0SStephen Hemminger  * Readers increment the counter by RTE_RWLOCK_READ (4)
41832cecc0SStephen Hemminger  * Writers set the RTE_RWLOCK_WRITE bit when lock is held
42832cecc0SStephen Hemminger  *     and set the RTE_RWLOCK_WAIT bit while waiting.
43832cecc0SStephen Hemminger  *
44832cecc0SStephen Hemminger  * 31                 2 1 0
45832cecc0SStephen Hemminger  * +-------------------+-+-+
46832cecc0SStephen Hemminger  * |  readers          | | |
47832cecc0SStephen Hemminger  * +-------------------+-+-+
48832cecc0SStephen Hemminger  *                      ^ ^
49832cecc0SStephen Hemminger  *                      | |
50832cecc0SStephen Hemminger  * WRITE: lock held ----/ |
51832cecc0SStephen Hemminger  * WAIT: writer pending --/
5299a2dd95SBruce Richardson  */
53832cecc0SStephen Hemminger 
54832cecc0SStephen Hemminger #define RTE_RWLOCK_WAIT	 0x1	/* Writer is waiting */
55832cecc0SStephen Hemminger #define RTE_RWLOCK_WRITE 0x2	/* Writer has the lock */
56832cecc0SStephen Hemminger #define RTE_RWLOCK_MASK  (RTE_RWLOCK_WAIT | RTE_RWLOCK_WRITE)
57832cecc0SStephen Hemminger 				/* Writer is waiting or has lock */
58832cecc0SStephen Hemminger #define RTE_RWLOCK_READ	 0x4	/* Reader increment */
59832cecc0SStephen Hemminger 
60657a98f3SDavid Marchand typedef struct __rte_lockable {
611ec6a845STyler Retzlaff 	RTE_ATOMIC(int32_t) cnt;
6299a2dd95SBruce Richardson } rte_rwlock_t;
6399a2dd95SBruce Richardson 
6499a2dd95SBruce Richardson /**
6599a2dd95SBruce Richardson  * A static rwlock initializer.
6699a2dd95SBruce Richardson  */
6799a2dd95SBruce Richardson #define RTE_RWLOCK_INITIALIZER { 0 }
6899a2dd95SBruce Richardson 
6999a2dd95SBruce Richardson /**
7099a2dd95SBruce Richardson  * Initialize the rwlock to an unlocked state.
7199a2dd95SBruce Richardson  *
7299a2dd95SBruce Richardson  * @param rwl
7399a2dd95SBruce Richardson  *   A pointer to the rwlock structure.
7499a2dd95SBruce Richardson  */
7599a2dd95SBruce Richardson static inline void
7699a2dd95SBruce Richardson rte_rwlock_init(rte_rwlock_t *rwl)
7799a2dd95SBruce Richardson {
7899a2dd95SBruce Richardson 	rwl->cnt = 0;
7999a2dd95SBruce Richardson }
8099a2dd95SBruce Richardson 
8199a2dd95SBruce Richardson /**
8299a2dd95SBruce Richardson  * Take a read lock. Loop until the lock is held.
8399a2dd95SBruce Richardson  *
84f82c02d3SArtemy Kovalyov  * @note The RW lock isn't recursive, so calling this function on the same
85f82c02d3SArtemy Kovalyov  * lock twice without releasing it could potentially result in a deadlock
86f82c02d3SArtemy Kovalyov  * scenario when a write lock is involved.
87f82c02d3SArtemy Kovalyov  *
8899a2dd95SBruce Richardson  * @param rwl
8999a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
9099a2dd95SBruce Richardson  */
9199a2dd95SBruce Richardson static inline void
9299a2dd95SBruce Richardson rte_rwlock_read_lock(rte_rwlock_t *rwl)
93657a98f3SDavid Marchand 	__rte_shared_lock_function(rwl)
94657a98f3SDavid Marchand 	__rte_no_thread_safety_analysis
9599a2dd95SBruce Richardson {
9699a2dd95SBruce Richardson 	int32_t x;
9799a2dd95SBruce Richardson 
98832cecc0SStephen Hemminger 	while (1) {
99832cecc0SStephen Hemminger 		/* Wait while writer is present or pending */
1001ec6a845STyler Retzlaff 		while (rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed)
101832cecc0SStephen Hemminger 		       & RTE_RWLOCK_MASK)
10299a2dd95SBruce Richardson 			rte_pause();
103832cecc0SStephen Hemminger 
104832cecc0SStephen Hemminger 		/* Try to get read lock */
1051ec6a845STyler Retzlaff 		x = rte_atomic_fetch_add_explicit(&rwl->cnt, RTE_RWLOCK_READ,
1061ec6a845STyler Retzlaff 				       rte_memory_order_acquire) + RTE_RWLOCK_READ;
107832cecc0SStephen Hemminger 
108832cecc0SStephen Hemminger 		/* If no writer, then acquire was successful */
109832cecc0SStephen Hemminger 		if (likely(!(x & RTE_RWLOCK_MASK)))
110832cecc0SStephen Hemminger 			return;
111832cecc0SStephen Hemminger 
112832cecc0SStephen Hemminger 		/* Lost race with writer, backout the change. */
1131ec6a845STyler Retzlaff 		rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_READ,
1141ec6a845STyler Retzlaff 				   rte_memory_order_relaxed);
11599a2dd95SBruce Richardson 	}
11699a2dd95SBruce Richardson }
11799a2dd95SBruce Richardson 
11899a2dd95SBruce Richardson /**
11904d59ab2SStephen Hemminger  * Try to take a read lock.
12099a2dd95SBruce Richardson  *
12199a2dd95SBruce Richardson  * @param rwl
12299a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
12399a2dd95SBruce Richardson  * @return
12499a2dd95SBruce Richardson  *   - zero if the lock is successfully taken
12599a2dd95SBruce Richardson  *   - -EBUSY if lock could not be acquired for reading because a
12699a2dd95SBruce Richardson  *     writer holds the lock
12799a2dd95SBruce Richardson  */
12899a2dd95SBruce Richardson static inline int
12999a2dd95SBruce Richardson rte_rwlock_read_trylock(rte_rwlock_t *rwl)
130657a98f3SDavid Marchand 	__rte_shared_trylock_function(0, rwl)
131657a98f3SDavid Marchand 	__rte_no_thread_safety_analysis
13299a2dd95SBruce Richardson {
13399a2dd95SBruce Richardson 	int32_t x;
13499a2dd95SBruce Richardson 
1351ec6a845STyler Retzlaff 	x = rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed);
13699a2dd95SBruce Richardson 
137832cecc0SStephen Hemminger 	/* fail if write lock is held or writer is pending */
138832cecc0SStephen Hemminger 	if (x & RTE_RWLOCK_MASK)
139832cecc0SStephen Hemminger 		return -EBUSY;
140832cecc0SStephen Hemminger 
141832cecc0SStephen Hemminger 	/* Try to get read lock */
1421ec6a845STyler Retzlaff 	x = rte_atomic_fetch_add_explicit(&rwl->cnt, RTE_RWLOCK_READ,
1431ec6a845STyler Retzlaff 			       rte_memory_order_acquire) + RTE_RWLOCK_READ;
144832cecc0SStephen Hemminger 
145832cecc0SStephen Hemminger 	/* Back out if writer raced in */
146832cecc0SStephen Hemminger 	if (unlikely(x & RTE_RWLOCK_MASK)) {
1471ec6a845STyler Retzlaff 		rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_READ,
1481ec6a845STyler Retzlaff 				   rte_memory_order_release);
149832cecc0SStephen Hemminger 
150832cecc0SStephen Hemminger 		return -EBUSY;
151832cecc0SStephen Hemminger 	}
15299a2dd95SBruce Richardson 	return 0;
15399a2dd95SBruce Richardson }
15499a2dd95SBruce Richardson 
15599a2dd95SBruce Richardson /**
15699a2dd95SBruce Richardson  * Release a read lock.
15799a2dd95SBruce Richardson  *
15899a2dd95SBruce Richardson  * @param rwl
15999a2dd95SBruce Richardson  *   A pointer to the rwlock structure.
16099a2dd95SBruce Richardson  */
16199a2dd95SBruce Richardson static inline void
16299a2dd95SBruce Richardson rte_rwlock_read_unlock(rte_rwlock_t *rwl)
163657a98f3SDavid Marchand 	__rte_unlock_function(rwl)
164657a98f3SDavid Marchand 	__rte_no_thread_safety_analysis
16599a2dd95SBruce Richardson {
1661ec6a845STyler Retzlaff 	rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_READ, rte_memory_order_release);
16799a2dd95SBruce Richardson }
16899a2dd95SBruce Richardson 
16999a2dd95SBruce Richardson /**
17004d59ab2SStephen Hemminger  * Try to take a write lock.
17199a2dd95SBruce Richardson  *
17299a2dd95SBruce Richardson  * @param rwl
17399a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
17499a2dd95SBruce Richardson  * @return
17599a2dd95SBruce Richardson  *   - zero if the lock is successfully taken
17699a2dd95SBruce Richardson  *   - -EBUSY if lock could not be acquired for writing because
17799a2dd95SBruce Richardson  *     it was already locked for reading or writing
17899a2dd95SBruce Richardson  */
17999a2dd95SBruce Richardson static inline int
18099a2dd95SBruce Richardson rte_rwlock_write_trylock(rte_rwlock_t *rwl)
181657a98f3SDavid Marchand 	__rte_exclusive_trylock_function(0, rwl)
182657a98f3SDavid Marchand 	__rte_no_thread_safety_analysis
18399a2dd95SBruce Richardson {
18499a2dd95SBruce Richardson 	int32_t x;
18599a2dd95SBruce Richardson 
1861ec6a845STyler Retzlaff 	x = rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed);
187832cecc0SStephen Hemminger 	if (x < RTE_RWLOCK_WRITE &&
1881ec6a845STyler Retzlaff 	    rte_atomic_compare_exchange_weak_explicit(&rwl->cnt, &x, x + RTE_RWLOCK_WRITE,
1891ec6a845STyler Retzlaff 					rte_memory_order_acquire, rte_memory_order_relaxed))
19099a2dd95SBruce Richardson 		return 0;
191832cecc0SStephen Hemminger 	else
192832cecc0SStephen Hemminger 		return -EBUSY;
19399a2dd95SBruce Richardson }
19499a2dd95SBruce Richardson 
19599a2dd95SBruce Richardson /**
19699a2dd95SBruce Richardson  * Take a write lock. Loop until the lock is held.
19799a2dd95SBruce Richardson  *
19899a2dd95SBruce Richardson  * @param rwl
19999a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
20099a2dd95SBruce Richardson  */
20199a2dd95SBruce Richardson static inline void
20299a2dd95SBruce Richardson rte_rwlock_write_lock(rte_rwlock_t *rwl)
203657a98f3SDavid Marchand 	__rte_exclusive_lock_function(rwl)
204657a98f3SDavid Marchand 	__rte_no_thread_safety_analysis
20599a2dd95SBruce Richardson {
20699a2dd95SBruce Richardson 	int32_t x;
20799a2dd95SBruce Richardson 
208832cecc0SStephen Hemminger 	while (1) {
2091ec6a845STyler Retzlaff 		x = rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed);
210832cecc0SStephen Hemminger 
211832cecc0SStephen Hemminger 		/* No readers or writers? */
212832cecc0SStephen Hemminger 		if (likely(x < RTE_RWLOCK_WRITE)) {
213832cecc0SStephen Hemminger 			/* Turn off RTE_RWLOCK_WAIT, turn on RTE_RWLOCK_WRITE */
2141ec6a845STyler Retzlaff 			if (rte_atomic_compare_exchange_weak_explicit(&rwl->cnt, &x,
2151ec6a845STyler Retzlaff 					RTE_RWLOCK_WRITE, rte_memory_order_acquire,
2161ec6a845STyler Retzlaff 					rte_memory_order_relaxed))
217832cecc0SStephen Hemminger 				return;
21899a2dd95SBruce Richardson 		}
219832cecc0SStephen Hemminger 
220832cecc0SStephen Hemminger 		/* Turn on writer wait bit */
221832cecc0SStephen Hemminger 		if (!(x & RTE_RWLOCK_WAIT))
2221ec6a845STyler Retzlaff 			rte_atomic_fetch_or_explicit(&rwl->cnt, RTE_RWLOCK_WAIT,
2231ec6a845STyler Retzlaff 				rte_memory_order_relaxed);
224832cecc0SStephen Hemminger 
225832cecc0SStephen Hemminger 		/* Wait until no readers before trying again */
2261ec6a845STyler Retzlaff 		while (rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed)
2271ec6a845STyler Retzlaff 				> RTE_RWLOCK_WAIT)
228832cecc0SStephen Hemminger 			rte_pause();
229832cecc0SStephen Hemminger 
23099a2dd95SBruce Richardson 	}
23199a2dd95SBruce Richardson }
23299a2dd95SBruce Richardson 
23399a2dd95SBruce Richardson /**
23499a2dd95SBruce Richardson  * Release a write lock.
23599a2dd95SBruce Richardson  *
23699a2dd95SBruce Richardson  * @param rwl
23799a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
23899a2dd95SBruce Richardson  */
23999a2dd95SBruce Richardson static inline void
24099a2dd95SBruce Richardson rte_rwlock_write_unlock(rte_rwlock_t *rwl)
241657a98f3SDavid Marchand 	__rte_unlock_function(rwl)
242657a98f3SDavid Marchand 	__rte_no_thread_safety_analysis
24399a2dd95SBruce Richardson {
2441ec6a845STyler Retzlaff 	rte_atomic_fetch_sub_explicit(&rwl->cnt, RTE_RWLOCK_WRITE, rte_memory_order_release);
24599a2dd95SBruce Richardson }
24699a2dd95SBruce Richardson 
24799a2dd95SBruce Richardson /**
24803f77d66SEelco Chaudron  * Test if the write lock is taken.
24903f77d66SEelco Chaudron  *
25003f77d66SEelco Chaudron  * @param rwl
25103f77d66SEelco Chaudron  *   A pointer to a rwlock structure.
25203f77d66SEelco Chaudron  * @return
25303f77d66SEelco Chaudron  *   1 if the write lock is currently taken; 0 otherwise.
25403f77d66SEelco Chaudron  */
25503f77d66SEelco Chaudron static inline int
25603f77d66SEelco Chaudron rte_rwlock_write_is_locked(rte_rwlock_t *rwl)
25703f77d66SEelco Chaudron {
2581ec6a845STyler Retzlaff 	if (rte_atomic_load_explicit(&rwl->cnt, rte_memory_order_relaxed) & RTE_RWLOCK_WRITE)
25903f77d66SEelco Chaudron 		return 1;
26003f77d66SEelco Chaudron 
26103f77d66SEelco Chaudron 	return 0;
26203f77d66SEelco Chaudron }
26303f77d66SEelco Chaudron 
26403f77d66SEelco Chaudron /**
26599a2dd95SBruce Richardson  * Try to execute critical section in a hardware memory transaction, if it
26699a2dd95SBruce Richardson  * fails or not available take a read lock
26799a2dd95SBruce Richardson  *
26899a2dd95SBruce Richardson  * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
26999a2dd95SBruce Richardson  * transaction always aborts the transaction since the CPU is not able to
27099a2dd95SBruce Richardson  * roll-back should the transaction fail. Therefore, hardware transactional
27199a2dd95SBruce Richardson  * locks are not advised to be used around rte_eth_rx_burst() and
27299a2dd95SBruce Richardson  * rte_eth_tx_burst() calls.
27399a2dd95SBruce Richardson  *
27499a2dd95SBruce Richardson  * @param rwl
27599a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
27699a2dd95SBruce Richardson  */
27799a2dd95SBruce Richardson static inline void
278657a98f3SDavid Marchand rte_rwlock_read_lock_tm(rte_rwlock_t *rwl)
279657a98f3SDavid Marchand 	__rte_shared_lock_function(rwl);
28099a2dd95SBruce Richardson 
28199a2dd95SBruce Richardson /**
28299a2dd95SBruce Richardson  * Commit hardware memory transaction or release the read lock if the lock is used as a fall-back
28399a2dd95SBruce Richardson  *
28499a2dd95SBruce Richardson  * @param rwl
28599a2dd95SBruce Richardson  *   A pointer to the rwlock structure.
28699a2dd95SBruce Richardson  */
28799a2dd95SBruce Richardson static inline void
288657a98f3SDavid Marchand rte_rwlock_read_unlock_tm(rte_rwlock_t *rwl)
289657a98f3SDavid Marchand 	__rte_unlock_function(rwl);
29099a2dd95SBruce Richardson 
29199a2dd95SBruce Richardson /**
29299a2dd95SBruce Richardson  * Try to execute critical section in a hardware memory transaction, if it
29399a2dd95SBruce Richardson  * fails or not available take a write lock
29499a2dd95SBruce Richardson  *
29599a2dd95SBruce Richardson  * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
29699a2dd95SBruce Richardson  * transaction always aborts the transaction since the CPU is not able to
29799a2dd95SBruce Richardson  * roll-back should the transaction fail. Therefore, hardware transactional
29899a2dd95SBruce Richardson  * locks are not advised to be used around rte_eth_rx_burst() and
29999a2dd95SBruce Richardson  * rte_eth_tx_burst() calls.
30099a2dd95SBruce Richardson  *
30199a2dd95SBruce Richardson  * @param rwl
30299a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
30399a2dd95SBruce Richardson  */
30499a2dd95SBruce Richardson static inline void
305657a98f3SDavid Marchand rte_rwlock_write_lock_tm(rte_rwlock_t *rwl)
306657a98f3SDavid Marchand 	__rte_exclusive_lock_function(rwl);
30799a2dd95SBruce Richardson 
30899a2dd95SBruce Richardson /**
30999a2dd95SBruce Richardson  * Commit hardware memory transaction or release the write lock if the lock is used as a fall-back
31099a2dd95SBruce Richardson  *
31199a2dd95SBruce Richardson  * @param rwl
31299a2dd95SBruce Richardson  *   A pointer to a rwlock structure.
31399a2dd95SBruce Richardson  */
31499a2dd95SBruce Richardson static inline void
315657a98f3SDavid Marchand rte_rwlock_write_unlock_tm(rte_rwlock_t *rwl)
316657a98f3SDavid Marchand 	__rte_unlock_function(rwl);
31799a2dd95SBruce Richardson 
31899a2dd95SBruce Richardson #ifdef __cplusplus
31999a2dd95SBruce Richardson }
32099a2dd95SBruce Richardson #endif
32199a2dd95SBruce Richardson 
32299a2dd95SBruce Richardson #endif /* _RTE_RWLOCK_H_ */
323