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