f-stack/dpdk/lib/eal/include/generic/rte_pflock.h

205 lines
4.6 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2021 Microsoft Corp.
* All rights reserved.
*
* Derived from Concurrency Kit
* Copyright 2011-2015 Samy Al Bahra.
*/
#ifndef _RTE_PFLOCK_H_
#define _RTE_PFLOCK_H_
/**
* @file
*
* Phase-fair locks
*
* This file defines an API for phase-fair reader writer locks,
* which is a variant of typical reader-writer locks that prevent
* starvation. In this type of lock, readers and writers alternate.
* This significantly reduces the worst-case blocking for readers and writers.
*
* This is an implementation derived from FreeBSD
* based on the work described in:
* Brandenburg, B. and Anderson, J. 2010. Spin-Based
* Reader-Writer Synchronization for Multiprocessor Real-Time Systems
*
* All locks must be initialised before use, and only initialised once.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <rte_common.h>
#include <rte_pause.h>
/**
* The rte_pflock_t type.
*/
struct rte_pflock {
struct {
uint16_t in;
uint16_t out;
} rd, wr;
};
typedef struct rte_pflock rte_pflock_t;
/*
* Allocation of bits to reader
*
* 15 4 3 2 1 0
* +-------------------+---+-+-+
* | rin: reads issued |x|x| | |
* +-------------------+---+-+-+
* ^ ^
* | |
* PRES: writer present ----/ |
* PHID: writer phase id -----/
*
* 15 4 3 2 1 0
* +------------------+------+
* |rout:read complete|unused|
* +------------------+------+
*
* The maximum number of readers is 4095
*/
/* Constants used to map the bits in reader counter */
#define RTE_PFLOCK_WBITS 0x3 /* Writer bits in reader. */
#define RTE_PFLOCK_PRES 0x2 /* Writer present bit. */
#define RTE_PFLOCK_PHID 0x1 /* Phase ID bit. */
#define RTE_PFLOCK_LSB 0xFFF0 /* reader bits. */
#define RTE_PFLOCK_RINC 0x10 /* Reader increment. */
/**
* A static pflock initializer.
*/
#define RTE_PFLOCK_INITIALIZER { }
/**
* @warning
* @b EXPERIMENTAL: this API may change without prior notice.
*
* Initialize the pflock to an unlocked state.
*
* @param pf
* A pointer to the pflock.
*/
__rte_experimental
static inline void
rte_pflock_init(struct rte_pflock *pf)
{
pf->rd.in = 0;
pf->rd.out = 0;
pf->wr.in = 0;
pf->wr.out = 0;
}
/**
* @warning
* @b EXPERIMENTAL: this API may change without prior notice.
*
* Take a pflock for read.
*
* @param pf
* A pointer to a pflock structure.
*/
__rte_experimental
static inline void
rte_pflock_read_lock(rte_pflock_t *pf)
{
uint16_t w;
/*
* If no writer is present, then the operation has completed
* successfully.
*/
w = __atomic_fetch_add(&pf->rd.in, RTE_PFLOCK_RINC, __ATOMIC_ACQUIRE)
& RTE_PFLOCK_WBITS;
if (w == 0)
return;
/* Wait for current write phase to complete. */
RTE_WAIT_UNTIL_MASKED(&pf->rd.in, RTE_PFLOCK_WBITS, !=, w,
__ATOMIC_ACQUIRE);
}
/**
* @warning
* @b EXPERIMENTAL: this API may change without prior notice.
*
* Release a pflock locked for reading.
*
* @param pf
* A pointer to the pflock structure.
*/
__rte_experimental
static inline void
rte_pflock_read_unlock(rte_pflock_t *pf)
{
__atomic_fetch_add(&pf->rd.out, RTE_PFLOCK_RINC, __ATOMIC_RELEASE);
}
/**
* @warning
* @b EXPERIMENTAL: this API may change without prior notice.
*
* Take the pflock for write.
*
* @param pf
* A pointer to the pflock structure.
*/
__rte_experimental
static inline void
rte_pflock_write_lock(rte_pflock_t *pf)
{
uint16_t ticket, w;
/* Acquire ownership of write-phase.
* This is same as rte_ticketlock_lock().
*/
ticket = __atomic_fetch_add(&pf->wr.in, 1, __ATOMIC_RELAXED);
rte_wait_until_equal_16(&pf->wr.out, ticket, __ATOMIC_ACQUIRE);
/*
* Acquire ticket on read-side in order to allow them
* to flush. Indicates to any incoming reader that a
* write-phase is pending.
*
* The load of rd.out in wait loop could be executed
* speculatively.
*/
w = RTE_PFLOCK_PRES | (ticket & RTE_PFLOCK_PHID);
ticket = __atomic_fetch_add(&pf->rd.in, w, __ATOMIC_RELAXED);
/* Wait for any pending readers to flush. */
rte_wait_until_equal_16(&pf->rd.out, ticket, __ATOMIC_ACQUIRE);
}
/**
* @warning
* @b EXPERIMENTAL: this API may change without prior notice.
*
* Release a pflock held for writing.
*
* @param pf
* A pointer to a pflock structure.
*/
__rte_experimental
static inline void
rte_pflock_write_unlock(rte_pflock_t *pf)
{
/* Migrate from write phase to read phase. */
__atomic_fetch_and(&pf->rd.in, RTE_PFLOCK_LSB, __ATOMIC_RELEASE);
/* Allow other writers to continue. */
__atomic_fetch_add(&pf->wr.out, 1, __ATOMIC_RELEASE);
}
#ifdef __cplusplus
}
#endif
#endif /* RTE_PFLOCK_H */