DPDK  24.03.0
Data Structures | Macros | Functions
rte_seqlock.h File Reference
#include <stdbool.h>
#include <stdint.h>
#include <rte_atomic.h>
#include <rte_branch_prediction.h>
#include <rte_seqcount.h>
#include <rte_spinlock.h>

Go to the source code of this file.

Data Structures

struct  rte_seqlock_t
 

Macros

#define RTE_SEQLOCK_INITIALIZER
 

Functions

static void rte_seqlock_init (rte_seqlock_t *seqlock)
 
static uint32_t rte_seqlock_read_begin (const rte_seqlock_t *seqlock)
 
static bool rte_seqlock_read_retry (const rte_seqlock_t *seqlock, uint32_t begin_sn)
 
static void rte_seqlock_write_lock (rte_seqlock_t *seqlock)
 
static void rte_seqlock_write_unlock (rte_seqlock_t *seqlock)
 

Detailed Description

RTE Seqlock

A sequence lock (seqlock) is a synchronization primitive allowing multiple, parallel, readers to efficiently and safely (i.e., in a data-race free manner) access lock-protected data. The RTE seqlock permits multiple writers as well. A spinlock is used for writer-writer synchronization.

A reader never blocks a writer. Very high frequency writes may prevent readers from making progress.

A seqlock is not preemption-safe on the writer side. If a writer is preempted, it may block readers until the writer thread is allowed to continue. Heavy computations should be kept out of the writer-side critical section, to avoid delaying readers.

Seqlocks are useful for data which are read by many cores, at a high frequency, and relatively infrequently written to.

One way to think about seqlocks is that they provide means to perform atomic operations on objects larger than what the native machine instructions allow for.

To avoid resource reclamation issues, the data protected by a seqlock should typically be kept self-contained (e.g., no pointers to mutable, dynamically allocated data).

Example usage:

#define MAX_Y_LEN 16
// Application-defined example data structure, protected by a seqlock.
struct config {
int param_x;
char param_y[MAX_Y_LEN];
};
// Accessor function for reading config fields.
void
config_read(const struct config *config, int *param_x, char *param_y)
{
uint32_t sn;
do {
sn = rte_seqlock_read_begin(&config->lock);
// Loads may be atomic or non-atomic, as in this example.
*param_x = config->param_x;
strcpy(param_y, config->param_y);
// An alternative to an immediate retry is to abort and
// try again at some later time, assuming progress is
// possible without the data.
} while (rte_seqlock_read_retry(&config->lock, sn));
}
// Accessor function for writing config fields.
void
config_update(struct config *config, int param_x, const char *param_y)
{
rte_seqlock_write_lock(&config->lock);
// Stores may be atomic or non-atomic, as in this example.
config->param_x = param_x;
strcpy(config->param_y, param_y);
rte_seqlock_write_unlock(&config->lock);
}

In case there is only a single writer, or writer-writer serialization is provided by other means, the use of sequence lock (i.e., rte_seqlock_t) can be replaced with the use of the "raw" rte_seqcount_t type instead.

See also
https://en.wikipedia.org/wiki/Seqlock.

Definition in file rte_seqlock.h.

Macro Definition Documentation

◆ RTE_SEQLOCK_INITIALIZER

#define RTE_SEQLOCK_INITIALIZER
Value:
{ \
}
#define RTE_SPINLOCK_INITIALIZER
Definition: rte_spinlock.h:38
#define RTE_SEQCOUNT_INITIALIZER
Definition: rte_seqcount.h:40

A static seqlock initializer.

Definition at line 109 of file rte_seqlock.h.

Function Documentation

◆ rte_seqlock_init()

static void rte_seqlock_init ( rte_seqlock_t seqlock)
inlinestatic

Initialize the seqlock.

This function initializes the seqlock, and leaves the writer-side spinlock unlocked.

Parameters
seqlockA pointer to the seqlock.

Definition at line 125 of file rte_seqlock.h.

◆ rte_seqlock_read_begin()

static uint32_t rte_seqlock_read_begin ( const rte_seqlock_t seqlock)
inlinestatic

Begin a read-side critical section.

See rte_seqcount_read_retry() for details.

Parameters
seqlockA pointer to the seqlock.
Returns
The seqlock sequence number for this critical section, to later be passed to rte_seqlock_read_retry().
See also
rte_seqlock_read_retry()
rte_seqcount_read_retry()

Definition at line 146 of file rte_seqlock.h.

◆ rte_seqlock_read_retry()

static bool rte_seqlock_read_retry ( const rte_seqlock_t seqlock,
uint32_t  begin_sn 
)
inlinestatic

End a read-side critical section.

See rte_seqcount_read_retry() for details.

Parameters
seqlockA pointer to the seqlock.
begin_snThe seqlock sequence number returned by rte_seqlock_read_begin().
Returns
true or false, if the just-read seqlock-protected data was inconsistent or consistent, respectively, at the time it was read.
See also
rte_seqlock_read_begin()

Definition at line 168 of file rte_seqlock.h.

◆ rte_seqlock_write_lock()

static void rte_seqlock_write_lock ( rte_seqlock_t seqlock)
inlinestatic

Begin a write-side critical section.

A call to this function acquires the write lock associated seqlock, and marks the beginning of a write-side critical section.

After having called this function, 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_seqlock_write_unlock().

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.

Unlike rte_seqlock_read_begin(), each call made to rte_seqlock_write_lock() must be matched with an unlock call.

Parameters
seqlockA pointer to the seqlock.
See also
rte_seqlock_write_unlock()

Definition at line 199 of file rte_seqlock.h.

◆ rte_seqlock_write_unlock()

static void rte_seqlock_write_unlock ( rte_seqlock_t seqlock)
inlinestatic

End a write-side critical section.

A call to this function marks the end of the write-side critical section, for seqlock. After this call has been made, the protected data may no longer be modified.

Parameters
seqlockA pointer to the seqlock.
See also
rte_seqlock_write_lock()

Definition at line 221 of file rte_seqlock.h.