1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2021 Microsoft Corp.
5 * Derived from Concurrency Kit
6 * Copyright 2011-2015 Samy Al Bahra.
10 #define _RTE_PFLOCK_H_
17 * This file defines an API for phase-fair reader writer locks,
18 * which is a variant of typical reader-writer locks that prevent
19 * starvation. In this type of lock, readers and writers alternate.
20 * This significantly reduces the worst-case blocking for readers and writers.
22 * This is an implementation derived from FreeBSD
23 * based on the work described in:
24 * Brandenburg, B. and Anderson, J. 2010. Spin-Based
25 * Reader-Writer Synchronization for Multiprocessor Real-Time Systems
27 * All locks must be initialised before use, and only initialised once.
34 #include <rte_common.h>
35 #include <rte_pause.h>
38 * The rte_pflock_t type.
46 typedef struct rte_pflock rte_pflock_t;
49 * Allocation of bits to reader
52 * +-------------------+---+-+-+
53 * | rin: reads issued |x|x| | |
54 * +-------------------+---+-+-+
57 * PRES: writer present ----/ |
58 * PHID: writer phase id -----/
61 * +------------------+------+
62 * |rout:read complete|unused|
63 * +------------------+------+
65 * The maximum number of readers is 4095
68 /* Constants used to map the bits in reader counter */
69 #define RTE_PFLOCK_WBITS 0x3 /* Writer bits in reader. */
70 #define RTE_PFLOCK_PRES 0x2 /* Writer present bit. */
71 #define RTE_PFLOCK_PHID 0x1 /* Phase ID bit. */
72 #define RTE_PFLOCK_LSB 0xFFF0 /* reader bits. */
73 #define RTE_PFLOCK_RINC 0x10 /* Reader increment. */
76 * A static pflock initializer.
78 #define RTE_PFLOCK_INITIALIZER { }
82 * @b EXPERIMENTAL: this API may change without prior notice.
84 * Initialize the pflock to an unlocked state.
87 * A pointer to the pflock.
91 rte_pflock_init(struct rte_pflock *pf)
101 * @b EXPERIMENTAL: this API may change without prior notice.
103 * Take a pflock for read.
106 * A pointer to a pflock structure.
110 rte_pflock_read_lock(rte_pflock_t *pf)
115 * If no writer is present, then the operation has completed
118 w = __atomic_fetch_add(&pf->rd.in, RTE_PFLOCK_RINC, __ATOMIC_ACQUIRE)
123 /* Wait for current write phase to complete. */
124 while ((__atomic_load_n(&pf->rd.in, __ATOMIC_ACQUIRE)
125 & RTE_PFLOCK_WBITS) == w)
131 * @b EXPERIMENTAL: this API may change without prior notice.
133 * Release a pflock locked for reading.
136 * A pointer to the pflock structure.
140 rte_pflock_read_unlock(rte_pflock_t *pf)
142 __atomic_fetch_add(&pf->rd.out, RTE_PFLOCK_RINC, __ATOMIC_RELEASE);
147 * @b EXPERIMENTAL: this API may change without prior notice.
149 * Take the pflock for write.
152 * A pointer to the pflock structure.
156 rte_pflock_write_lock(rte_pflock_t *pf)
160 /* Acquire ownership of write-phase.
161 * This is same as rte_tickelock_lock().
163 ticket = __atomic_fetch_add(&pf->wr.in, 1, __ATOMIC_RELAXED);
164 rte_wait_until_equal_16(&pf->wr.out, ticket, __ATOMIC_ACQUIRE);
167 * Acquire ticket on read-side in order to allow them
168 * to flush. Indicates to any incoming reader that a
169 * write-phase is pending.
171 * The load of rd.out in wait loop could be executed
174 w = RTE_PFLOCK_PRES | (ticket & RTE_PFLOCK_PHID);
175 ticket = __atomic_fetch_add(&pf->rd.in, w, __ATOMIC_RELAXED);
177 /* Wait for any pending readers to flush. */
178 rte_wait_until_equal_16(&pf->rd.out, ticket, __ATOMIC_ACQUIRE);
183 * @b EXPERIMENTAL: this API may change without prior notice.
185 * Release a pflock held for writing.
188 * A pointer to a pflock structure.
192 rte_pflock_write_unlock(rte_pflock_t *pf)
194 /* Migrate from write phase to read phase. */
195 __atomic_fetch_and(&pf->rd.in, RTE_PFLOCK_LSB, __ATOMIC_RELEASE);
197 /* Allow other writers to continue. */
198 __atomic_fetch_add(&pf->wr.out, 1, __ATOMIC_RELEASE);
205 #endif /* RTE_PFLOCK_H */