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 RTE_WAIT_UNTIL_MASKED(&pf->rd.in, RTE_PFLOCK_WBITS, !=, w,
130 * @b EXPERIMENTAL: this API may change without prior notice.
132 * Release a pflock locked for reading.
135 * A pointer to the pflock structure.
139 rte_pflock_read_unlock(rte_pflock_t *pf)
141 __atomic_fetch_add(&pf->rd.out, RTE_PFLOCK_RINC, __ATOMIC_RELEASE);
146 * @b EXPERIMENTAL: this API may change without prior notice.
148 * Take the pflock for write.
151 * A pointer to the pflock structure.
155 rte_pflock_write_lock(rte_pflock_t *pf)
159 /* Acquire ownership of write-phase.
160 * This is same as rte_ticketlock_lock().
162 ticket = __atomic_fetch_add(&pf->wr.in, 1, __ATOMIC_RELAXED);
163 rte_wait_until_equal_16(&pf->wr.out, ticket, __ATOMIC_ACQUIRE);
166 * Acquire ticket on read-side in order to allow them
167 * to flush. Indicates to any incoming reader that a
168 * write-phase is pending.
170 * The load of rd.out in wait loop could be executed
173 w = RTE_PFLOCK_PRES | (ticket & RTE_PFLOCK_PHID);
174 ticket = __atomic_fetch_add(&pf->rd.in, w, __ATOMIC_RELAXED);
176 /* Wait for any pending readers to flush. */
177 rte_wait_until_equal_16(&pf->rd.out, ticket, __ATOMIC_ACQUIRE);
182 * @b EXPERIMENTAL: this API may change without prior notice.
184 * Release a pflock held for writing.
187 * A pointer to a pflock structure.
191 rte_pflock_write_unlock(rte_pflock_t *pf)
193 /* Migrate from write phase to read phase. */
194 __atomic_fetch_and(&pf->rd.in, RTE_PFLOCK_LSB, __ATOMIC_RELEASE);
196 /* Allow other writers to continue. */
197 __atomic_fetch_add(&pf->wr.out, 1, __ATOMIC_RELEASE);
204 #endif /* RTE_PFLOCK_H */