xref: /dpdk/lib/eal/include/rte_pflock.h (revision 719834a6849e1daf4a70ff7742bbcc3ae7e25607)
1e5e613f0SDavid Marchand /* SPDX-License-Identifier: BSD-3-Clause
2e5e613f0SDavid Marchand  * Copyright(c) 2021 Microsoft Corp.
3e5e613f0SDavid Marchand  * All rights reserved.
4e5e613f0SDavid Marchand  *
5e5e613f0SDavid Marchand  * Derived from Concurrency Kit
6e5e613f0SDavid Marchand  * Copyright 2011-2015 Samy Al Bahra.
7e5e613f0SDavid Marchand  */
8e5e613f0SDavid Marchand 
9e5e613f0SDavid Marchand #ifndef _RTE_PFLOCK_H_
10e5e613f0SDavid Marchand #define _RTE_PFLOCK_H_
11e5e613f0SDavid Marchand 
12e5e613f0SDavid Marchand /**
13e5e613f0SDavid Marchand  * @file
14e5e613f0SDavid Marchand  *
15e5e613f0SDavid Marchand  * Phase-fair locks
16e5e613f0SDavid Marchand  *
17e5e613f0SDavid Marchand  * This file defines an API for phase-fair reader writer locks,
18e5e613f0SDavid Marchand  * which is a variant of typical reader-writer locks that prevent
19e5e613f0SDavid Marchand  * starvation. In this type of lock, readers and writers alternate.
20e5e613f0SDavid Marchand  * This significantly reduces the worst-case blocking for readers and writers.
21e5e613f0SDavid Marchand  *
22e5e613f0SDavid Marchand  * This is an implementation derived from FreeBSD
23e5e613f0SDavid Marchand  * based on the work described in:
24e5e613f0SDavid Marchand  *    Brandenburg, B. and Anderson, J. 2010. Spin-Based
25e5e613f0SDavid Marchand  *    Reader-Writer Synchronization for Multiprocessor Real-Time Systems
26e5e613f0SDavid Marchand  *
27e5e613f0SDavid Marchand  * All locks must be initialised before use, and only initialised once.
28e5e613f0SDavid Marchand  */
29e5e613f0SDavid Marchand 
30e5e613f0SDavid Marchand #include <rte_common.h>
31e5e613f0SDavid Marchand #include <rte_pause.h>
321ec6a845STyler Retzlaff #include <rte_stdatomic.h>
33e5e613f0SDavid Marchand 
34*719834a6SMattias Rönnblom #ifdef __cplusplus
35*719834a6SMattias Rönnblom extern "C" {
36*719834a6SMattias Rönnblom #endif
37*719834a6SMattias Rönnblom 
38e5e613f0SDavid Marchand /**
39e5e613f0SDavid Marchand  * The rte_pflock_t type.
40e5e613f0SDavid Marchand  */
41e5e613f0SDavid Marchand struct rte_pflock {
42e5e613f0SDavid Marchand 	struct {
431ec6a845STyler Retzlaff 		RTE_ATOMIC(uint16_t) in;
441ec6a845STyler Retzlaff 		RTE_ATOMIC(uint16_t) out;
45e5e613f0SDavid Marchand 	} rd, wr;
46e5e613f0SDavid Marchand };
47e5e613f0SDavid Marchand typedef struct rte_pflock rte_pflock_t;
48e5e613f0SDavid Marchand 
49e5e613f0SDavid Marchand /*
50e5e613f0SDavid Marchand  * Allocation of bits to reader
51e5e613f0SDavid Marchand  *
52e5e613f0SDavid Marchand  * 15                 4 3 2 1 0
53e5e613f0SDavid Marchand  * +-------------------+---+-+-+
54e5e613f0SDavid Marchand  * | rin: reads issued |x|x| | |
55e5e613f0SDavid Marchand  * +-------------------+---+-+-+
56e5e613f0SDavid Marchand  *                          ^ ^
57e5e613f0SDavid Marchand  *                          | |
58e5e613f0SDavid Marchand  * PRES: writer present ----/ |
59e5e613f0SDavid Marchand  * PHID: writer phase id -----/
60e5e613f0SDavid Marchand  *
61e5e613f0SDavid Marchand  * 15                4 3 2 1 0
62e5e613f0SDavid Marchand  * +------------------+------+
63e5e613f0SDavid Marchand  * |rout:read complete|unused|
64e5e613f0SDavid Marchand  * +------------------+------+
65e5e613f0SDavid Marchand  *
66e5e613f0SDavid Marchand  * The maximum number of readers is 4095
67e5e613f0SDavid Marchand  */
68e5e613f0SDavid Marchand 
69e5e613f0SDavid Marchand /* Constants used to map the bits in reader counter */
70e5e613f0SDavid Marchand #define RTE_PFLOCK_WBITS 0x3	/* Writer bits in reader. */
71e5e613f0SDavid Marchand #define RTE_PFLOCK_PRES  0x2	/* Writer present bit. */
72e5e613f0SDavid Marchand #define RTE_PFLOCK_PHID  0x1	/* Phase ID bit. */
73e5e613f0SDavid Marchand #define RTE_PFLOCK_LSB   0xFFF0 /* reader bits. */
74e5e613f0SDavid Marchand #define RTE_PFLOCK_RINC  0x10	/* Reader increment. */
75e5e613f0SDavid Marchand 
76e5e613f0SDavid Marchand /**
77e5e613f0SDavid Marchand  * A static pflock initializer.
78e5e613f0SDavid Marchand  */
79e5e613f0SDavid Marchand #define RTE_PFLOCK_INITIALIZER {  }
80e5e613f0SDavid Marchand 
81e5e613f0SDavid Marchand /**
82e5e613f0SDavid Marchand  * Initialize the pflock to an unlocked state.
83e5e613f0SDavid Marchand  *
84e5e613f0SDavid Marchand  * @param pf
85e5e613f0SDavid Marchand  *   A pointer to the pflock.
86e5e613f0SDavid Marchand  */
87e5e613f0SDavid Marchand static inline void
88e5e613f0SDavid Marchand rte_pflock_init(struct rte_pflock *pf)
89e5e613f0SDavid Marchand {
90e5e613f0SDavid Marchand 	pf->rd.in = 0;
91e5e613f0SDavid Marchand 	pf->rd.out = 0;
92e5e613f0SDavid Marchand 	pf->wr.in = 0;
93e5e613f0SDavid Marchand 	pf->wr.out = 0;
94e5e613f0SDavid Marchand }
95e5e613f0SDavid Marchand 
96e5e613f0SDavid Marchand /**
97e5e613f0SDavid Marchand  * Take a pflock for read.
98e5e613f0SDavid Marchand  *
99e5e613f0SDavid Marchand  * @param pf
100e5e613f0SDavid Marchand  *   A pointer to a pflock structure.
101e5e613f0SDavid Marchand  */
102e5e613f0SDavid Marchand static inline void
103e5e613f0SDavid Marchand rte_pflock_read_lock(rte_pflock_t *pf)
104e5e613f0SDavid Marchand {
105e5e613f0SDavid Marchand 	uint16_t w;
106e5e613f0SDavid Marchand 
107e5e613f0SDavid Marchand 	/*
108e5e613f0SDavid Marchand 	 * If no writer is present, then the operation has completed
109e5e613f0SDavid Marchand 	 * successfully.
110e5e613f0SDavid Marchand 	 */
1111ec6a845STyler Retzlaff 	w = rte_atomic_fetch_add_explicit(&pf->rd.in, RTE_PFLOCK_RINC, rte_memory_order_acquire)
112e5e613f0SDavid Marchand 		& RTE_PFLOCK_WBITS;
113e5e613f0SDavid Marchand 	if (w == 0)
114e5e613f0SDavid Marchand 		return;
115e5e613f0SDavid Marchand 
116e5e613f0SDavid Marchand 	/* Wait for current write phase to complete. */
1171ec6a845STyler Retzlaff 	RTE_WAIT_UNTIL_MASKED(&pf->rd.in, RTE_PFLOCK_WBITS, !=, w, rte_memory_order_acquire);
118e5e613f0SDavid Marchand }
119e5e613f0SDavid Marchand 
120e5e613f0SDavid Marchand /**
121e5e613f0SDavid Marchand  * Release a pflock locked for reading.
122e5e613f0SDavid Marchand  *
123e5e613f0SDavid Marchand  * @param pf
124e5e613f0SDavid Marchand  *   A pointer to the pflock structure.
125e5e613f0SDavid Marchand  */
126e5e613f0SDavid Marchand static inline void
127e5e613f0SDavid Marchand rte_pflock_read_unlock(rte_pflock_t *pf)
128e5e613f0SDavid Marchand {
1291ec6a845STyler Retzlaff 	rte_atomic_fetch_add_explicit(&pf->rd.out, RTE_PFLOCK_RINC, rte_memory_order_release);
130e5e613f0SDavid Marchand }
131e5e613f0SDavid Marchand 
132e5e613f0SDavid Marchand /**
133e5e613f0SDavid Marchand  * Take the pflock for write.
134e5e613f0SDavid Marchand  *
135e5e613f0SDavid Marchand  * @param pf
136e5e613f0SDavid Marchand  *   A pointer to the pflock structure.
137e5e613f0SDavid Marchand  */
138e5e613f0SDavid Marchand static inline void
139e5e613f0SDavid Marchand rte_pflock_write_lock(rte_pflock_t *pf)
140e5e613f0SDavid Marchand {
141e5e613f0SDavid Marchand 	uint16_t ticket, w;
142e5e613f0SDavid Marchand 
143e5e613f0SDavid Marchand 	/* Acquire ownership of write-phase.
144e5e613f0SDavid Marchand 	 * This is same as rte_ticketlock_lock().
145e5e613f0SDavid Marchand 	 */
1461ec6a845STyler Retzlaff 	ticket = rte_atomic_fetch_add_explicit(&pf->wr.in, 1, rte_memory_order_relaxed);
1471ec6a845STyler Retzlaff 	rte_wait_until_equal_16((uint16_t *)(uintptr_t)&pf->wr.out, ticket,
1481ec6a845STyler Retzlaff 		rte_memory_order_acquire);
149e5e613f0SDavid Marchand 
150e5e613f0SDavid Marchand 	/*
151e5e613f0SDavid Marchand 	 * Acquire ticket on read-side in order to allow them
152e5e613f0SDavid Marchand 	 * to flush. Indicates to any incoming reader that a
153e5e613f0SDavid Marchand 	 * write-phase is pending.
154e5e613f0SDavid Marchand 	 *
155e5e613f0SDavid Marchand 	 * The load of rd.out in wait loop could be executed
156e5e613f0SDavid Marchand 	 * speculatively.
157e5e613f0SDavid Marchand 	 */
158e5e613f0SDavid Marchand 	w = RTE_PFLOCK_PRES | (ticket & RTE_PFLOCK_PHID);
1591ec6a845STyler Retzlaff 	ticket = rte_atomic_fetch_add_explicit(&pf->rd.in, w, rte_memory_order_relaxed);
160e5e613f0SDavid Marchand 
161e5e613f0SDavid Marchand 	/* Wait for any pending readers to flush. */
1621ec6a845STyler Retzlaff 	rte_wait_until_equal_16((uint16_t *)(uintptr_t)&pf->rd.out, ticket,
1631ec6a845STyler Retzlaff 		rte_memory_order_acquire);
164e5e613f0SDavid Marchand }
165e5e613f0SDavid Marchand 
166e5e613f0SDavid Marchand /**
167e5e613f0SDavid Marchand  * Release a pflock held for writing.
168e5e613f0SDavid Marchand  *
169e5e613f0SDavid Marchand  * @param pf
170e5e613f0SDavid Marchand  *   A pointer to a pflock structure.
171e5e613f0SDavid Marchand  */
172e5e613f0SDavid Marchand static inline void
173e5e613f0SDavid Marchand rte_pflock_write_unlock(rte_pflock_t *pf)
174e5e613f0SDavid Marchand {
175e5e613f0SDavid Marchand 	/* Migrate from write phase to read phase. */
1761ec6a845STyler Retzlaff 	rte_atomic_fetch_and_explicit(&pf->rd.in, RTE_PFLOCK_LSB, rte_memory_order_release);
177e5e613f0SDavid Marchand 
178e5e613f0SDavid Marchand 	/* Allow other writers to continue. */
1791ec6a845STyler Retzlaff 	rte_atomic_fetch_add_explicit(&pf->wr.out, 1, rte_memory_order_release);
180e5e613f0SDavid Marchand }
181e5e613f0SDavid Marchand 
182e5e613f0SDavid Marchand #ifdef __cplusplus
183e5e613f0SDavid Marchand }
184e5e613f0SDavid Marchand #endif
185e5e613f0SDavid Marchand 
186e5e613f0SDavid Marchand #endif /* RTE_PFLOCK_H */
187