mirror of https://github.com/F-Stack/f-stack.git
247 lines
6.6 KiB
C
247 lines
6.6 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2022 Ericsson AB
|
|
*/
|
|
|
|
#ifndef _RTE_SEQCOUNT_H_
|
|
#define _RTE_SEQCOUNT_H_
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @file
|
|
* RTE Seqcount
|
|
*
|
|
* The sequence counter synchronizes a single writer with multiple,
|
|
* parallel readers. It is used as the basis for the RTE sequence
|
|
* lock.
|
|
*
|
|
* @see rte_seqlock.h
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include <rte_atomic.h>
|
|
#include <rte_branch_prediction.h>
|
|
#include <rte_compat.h>
|
|
|
|
/**
|
|
* The RTE seqcount type.
|
|
*/
|
|
typedef struct {
|
|
uint32_t sn; /**< A sequence number for the protected data. */
|
|
} rte_seqcount_t;
|
|
|
|
/**
|
|
* A static seqcount initializer.
|
|
*/
|
|
#define RTE_SEQCOUNT_INITIALIZER { .sn = 0 }
|
|
|
|
/**
|
|
* @warning
|
|
* @b EXPERIMENTAL: this API may change without prior notice.
|
|
*
|
|
* Initialize the sequence counter.
|
|
*
|
|
* @param seqcount
|
|
* A pointer to the sequence counter.
|
|
*/
|
|
__rte_experimental
|
|
static inline void
|
|
rte_seqcount_init(rte_seqcount_t *seqcount)
|
|
{
|
|
seqcount->sn = 0;
|
|
}
|
|
|
|
/**
|
|
* @warning
|
|
* @b EXPERIMENTAL: this API may change without prior notice.
|
|
*
|
|
* Begin a read-side critical section.
|
|
*
|
|
* A call to this function marks the beginning of a read-side critical
|
|
* section, for @p seqcount.
|
|
*
|
|
* rte_seqcount_read_begin() returns a sequence number, which is later
|
|
* used in rte_seqcount_read_retry() to check if the protected data
|
|
* underwent any modifications during the read transaction.
|
|
*
|
|
* After (in program order) rte_seqcount_read_begin() has been called,
|
|
* the calling thread reads the protected data, for later use. The
|
|
* protected data read *must* be copied (either in pristine form, or
|
|
* in the form of some derivative), since the caller may only read the
|
|
* data from within the read-side critical section (i.e., after
|
|
* rte_seqcount_read_begin() and before rte_seqcount_read_retry()),
|
|
* but must not act upon the retrieved data while in the critical
|
|
* section, since it does not yet know if it is consistent.
|
|
*
|
|
* The protected data may be read using atomic and/or non-atomic
|
|
* operations.
|
|
*
|
|
* After (in program order) all required data loads have been
|
|
* performed, rte_seqcount_read_retry() should be called, marking
|
|
* the end of the read-side critical section.
|
|
*
|
|
* If rte_seqcount_read_retry() returns true, the just-read data is
|
|
* inconsistent and should be discarded. The caller has the option to
|
|
* either restart the whole procedure right away (i.e., calling
|
|
* rte_seqcount_read_begin() again), or do the same at some later time.
|
|
*
|
|
* If rte_seqcount_read_retry() returns false, the data was read
|
|
* atomically and the copied data is consistent.
|
|
*
|
|
* @param seqcount
|
|
* A pointer to the sequence counter.
|
|
* @return
|
|
* The seqcount sequence number for this critical section, to
|
|
* later be passed to rte_seqcount_read_retry().
|
|
*
|
|
* @see rte_seqcount_read_retry()
|
|
*/
|
|
|
|
__rte_experimental
|
|
static inline uint32_t
|
|
rte_seqcount_read_begin(const rte_seqcount_t *seqcount)
|
|
{
|
|
/* __ATOMIC_ACQUIRE to prevent loads after (in program order)
|
|
* from happening before the sn load. Synchronizes-with the
|
|
* store release in rte_seqcount_write_end().
|
|
*/
|
|
return __atomic_load_n(&seqcount->sn, __ATOMIC_ACQUIRE);
|
|
}
|
|
|
|
/**
|
|
* @warning
|
|
* @b EXPERIMENTAL: this API may change without prior notice.
|
|
*
|
|
* End a read-side critical section.
|
|
*
|
|
* A call to this function marks the end of a read-side critical
|
|
* section, for @p seqcount. The application must supply the sequence
|
|
* number produced by the corresponding rte_seqcount_read_begin() call.
|
|
*
|
|
* After this function has been called, the caller should not access
|
|
* the protected data.
|
|
*
|
|
* In case rte_seqcount_read_retry() returns true, the just-read data
|
|
* was modified as it was being read and may be inconsistent, and thus
|
|
* should be discarded.
|
|
*
|
|
* In case this function returns false, the data is consistent and the
|
|
* set of atomic and non-atomic load operations performed between
|
|
* rte_seqcount_read_begin() and rte_seqcount_read_retry() were atomic,
|
|
* as a whole.
|
|
*
|
|
* @param seqcount
|
|
* A pointer to the sequence counter.
|
|
* @param begin_sn
|
|
* The sequence number returned by rte_seqcount_read_begin().
|
|
* @return
|
|
* true or false, if the just-read seqcount-protected data was
|
|
* inconsistent or consistent, respectively, at the time it was
|
|
* read.
|
|
*
|
|
* @see rte_seqcount_read_begin()
|
|
*/
|
|
|
|
__rte_experimental
|
|
static inline bool
|
|
rte_seqcount_read_retry(const rte_seqcount_t *seqcount, uint32_t begin_sn)
|
|
{
|
|
uint32_t end_sn;
|
|
|
|
/* An odd sequence number means the protected data was being
|
|
* modified already at the point of the rte_seqcount_read_begin()
|
|
* call.
|
|
*/
|
|
if (unlikely(begin_sn & 1))
|
|
return true;
|
|
|
|
/* make sure the data loads happens before the sn load */
|
|
rte_atomic_thread_fence(__ATOMIC_ACQUIRE);
|
|
|
|
end_sn = __atomic_load_n(&seqcount->sn, __ATOMIC_RELAXED);
|
|
|
|
/* A writer incremented the sequence number during this read
|
|
* critical section.
|
|
*/
|
|
return begin_sn != end_sn;
|
|
}
|
|
|
|
/**
|
|
* @warning
|
|
* @b EXPERIMENTAL: this API may change without prior notice.
|
|
*
|
|
* Begin a write-side critical section.
|
|
*
|
|
* A call to this function marks the beginning of a write-side
|
|
* critical section, after which the caller may go on to modify (both
|
|
* read and write) the protected data, in an atomic or non-atomic
|
|
* manner.
|
|
*
|
|
* After the necessary updates have been performed, the application
|
|
* calls rte_seqcount_write_end().
|
|
*
|
|
* Multiple, parallel writers must use some external serialization.
|
|
*
|
|
* This function is not preemption-safe in the sense that preemption
|
|
* of the calling thread may block reader progress until the writer
|
|
* thread is rescheduled.
|
|
*
|
|
* @param seqcount
|
|
* A pointer to the sequence counter.
|
|
*
|
|
* @see rte_seqcount_write_end()
|
|
*/
|
|
|
|
__rte_experimental
|
|
static inline void
|
|
rte_seqcount_write_begin(rte_seqcount_t *seqcount)
|
|
{
|
|
uint32_t sn;
|
|
|
|
sn = seqcount->sn + 1;
|
|
|
|
__atomic_store_n(&seqcount->sn, sn, __ATOMIC_RELAXED);
|
|
|
|
/* __ATOMIC_RELEASE to prevent stores after (in program order)
|
|
* from happening before the sn store.
|
|
*/
|
|
rte_atomic_thread_fence(__ATOMIC_RELEASE);
|
|
}
|
|
|
|
/**
|
|
* @warning
|
|
* @b EXPERIMENTAL: this API may change without prior notice.
|
|
*
|
|
* End a write-side critical section.
|
|
*
|
|
* A call to this function marks the end of the write-side critical
|
|
* section, for @p seqcount. After this call has been made, the
|
|
* protected data may no longer be modified.
|
|
*
|
|
* @param seqcount
|
|
* A pointer to the sequence counter.
|
|
*
|
|
* @see rte_seqcount_write_begin()
|
|
*/
|
|
__rte_experimental
|
|
static inline void
|
|
rte_seqcount_write_end(rte_seqcount_t *seqcount)
|
|
{
|
|
uint32_t sn;
|
|
|
|
sn = seqcount->sn + 1;
|
|
|
|
/* Synchronizes-with the load acquire in rte_seqcount_read_begin(). */
|
|
__atomic_store_n(&seqcount->sn, sn, __ATOMIC_RELEASE);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* _RTE_SEQCOUNT_H_ */
|