序列计数器和顺序锁

简介

序列计数器是一种读写一致性机制,具有无锁读者(只读重试循环),且没有写者饥饿。它们用于很少写入的数据(例如系统时间),其中读者希望获取一致的信息集,并愿意在该信息发生变化时重试。

当读取端临界区开始时的序列计数为偶数,并且在临界区结束时再次读取到相同的序列计数值时,数据集是一致的。数据集中的数据必须在读取端临界区内复制出来。如果序列计数在临界区的开始和结束之间发生了变化,读者必须重试。

写者在其临界区的开始和结束时增加序列计数。在开始临界区后,序列计数为奇数,表示正在进行更新。在写端临界区结束时,序列计数再次变为偶数,从而让读者能够继续进行。

序列计数器的写端临界区绝不能被读取端区段抢占或中断。否则,由于奇数序列计数值和被中断的写者,读者将自旋整个调度器时钟周期。如果该读者属于实时调度类,它可能会永远自旋,导致内核活锁。

如果受保护的数据包含指针,则不能使用此机制,因为写者可能会使读者正在跟踪的指针失效。

序列计数器(seqcount_t

这是原始的计数机制,它不提供对多个写者的保护。因此,写端临界区必须通过外部锁进行序列化。

如果写入序列化原语不隐式禁用抢占,则在进入写端区段之前必须显式禁用抢占。如果读取区段可以从硬中断或软中断上下文调用,则在进入写入区段之前也必须分别禁用中断或下半部。

如果希望自动处理写者序列化和不可抢占性的序列计数器要求,请改用顺序锁(seqlock_t)

初始化

/* dynamic */
seqcount_t foo_seqcount;
seqcount_init(&foo_seqcount);

/* static */
static seqcount_t foo_seqcount = SEQCNT_ZERO(foo_seqcount);

/* C99 struct init */
struct {
        .seq   = SEQCNT_ZERO(foo.seq),
} foo;

写路径

/* Serialized context with disabled preemption */

write_seqcount_begin(&foo_seqcount);

/* ... [[write-side critical section]] ... */

write_seqcount_end(&foo_seqcount);

读路径

do {
        seq = read_seqcount_begin(&foo_seqcount);

        /* ... [[read-side critical section]] ... */

} while (read_seqcount_retry(&foo_seqcount, seq));

带有相关联锁的序列计数器(seqcount_LOCKNAME_t

序列计数器(seqcount_t)中所述,序列计数器写端临界区必须序列化且不可抢占。此序列计数器变体在初始化时关联用于写者序列化的锁,这使得 `lockdep` 能够验证写端临界区是否正确序列化。

如果 `lockdep` 被禁用,此锁关联是无操作(NOOP),没有存储或运行时开销。如果 `lockdep` 被启用,锁指针将存储在struct seqcount中,并且 `lockdep` 的“锁已持有”断言将在写端临界区开始时注入,以验证其是否受到正确保护。

对于不隐式禁用抢占的锁类型,抢占保护在写端函数中强制执行。

定义了以下带有相关联锁的序列计数器

  • seqcount_spinlock_t

  • seqcount_raw_spinlock_t

  • seqcount_rwlock_t

  • seqcount_mutex_t

  • seqcount_ww_mutex_t

序列计数器读写 API 可以接受普通的 `seqcount_t` 或上述任何 `seqcount_LOCKNAME_t` 变体。

初始化(将“LOCKNAME”替换为支持的锁之一)

/* dynamic */
seqcount_LOCKNAME_t foo_seqcount;
seqcount_LOCKNAME_init(&foo_seqcount, &lock);

/* static */
static seqcount_LOCKNAME_t foo_seqcount =
        SEQCNT_LOCKNAME_ZERO(foo_seqcount, &lock);

/* C99 struct init */
struct {
        .seq   = SEQCNT_LOCKNAME_ZERO(foo.seq, &lock),
} foo;

写路径:与序列计数器(seqcount_t)中相同,同时在获取关联的写序列化锁的上下文中运行。

读路径:与序列计数器(seqcount_t)中相同。

闩锁序列计数器(seqcount_latch_t

闩锁序列计数器是一种多版本并发控制机制,其中嵌入的 `seqcount_t` 计数器的奇偶值用于在受保护数据的两个副本之间切换。这允许序列计数器读取路径安全地中断其自身的写端临界区。

当写端区段无法防止被读者中断时,使用 `seqcount_latch_t`。这通常是当读取端可以从 NMI 处理程序调用时的情况。

有关更多信息,请查看write_seqcount_latch()

顺序锁(seqlock_t

这包含前面讨论的序列计数器(seqcount_t)机制,以及一个用于写者序列化和不可抢占性的嵌入式自旋锁。

如果读取端区段可以从硬中断或软中断上下文调用,请使用分别禁用中断或下半部的写端函数变体。

初始化

/* dynamic */
seqlock_t foo_seqlock;
seqlock_init(&foo_seqlock);

/* static */
static DEFINE_SEQLOCK(foo_seqlock);

/* C99 struct init */
struct {
        .seql   = __SEQLOCK_UNLOCKED(foo.seql)
} foo;

写路径

write_seqlock(&foo_seqlock);

/* ... [[write-side critical section]] ... */

write_sequnlock(&foo_seqlock);

读路径,分为三类

  1. 普通序列读取器,它们从不阻塞写者,但如果写者正在进行中,它们必须通过检测序列号变化来重试。写者不等待序列读取器

    do {
            seq = read_seqbegin(&foo_seqlock);
    
            /* ... [[read-side critical section]] ... */
    
    } while (read_seqretry(&foo_seqlock, seq));
    
  2. 锁定读取器,如果写者或另一个锁定读取器正在进行中,它们将等待。正在进行的锁定读取器也会阻止写者进入其临界区。此读锁是独占的。与 `rwlock_t` 不同,只有一个锁定读取器可以获取它

    read_seqlock_excl(&foo_seqlock);
    
    /* ... [[read-side critical section]] ... */
    
    read_sequnlock_excl(&foo_seqlock);
    
  3. 条件无锁读取器(如1所示),或锁定读取器(如2所示),根据传递的标记决定。这用于在写活动急剧增加时避免无锁读取器饥饿(过多重试循环)。首先尝试无锁读取(传递偶数标记)。如果尝试失败(序列计数器不匹配),将下一个迭代的标记设为奇数,无锁读取转换为完全锁定读取,并且不需要重试循环,例如

    /* marker; even initialization */
    int seq = 1;
    do {
            seq++; /* 2 on the 1st/lockless path, otherwise odd */
            read_seqbegin_or_lock(&foo_seqlock, &seq);
    
            /* ... [[read-side critical section]] ... */
    
    } while (need_seqretry(&foo_seqlock, seq));
    done_seqretry(&foo_seqlock, seq);
    

API 文档

seqcount_init

seqcount_init (s)

`seqcount_t` 的运行时初始化器

参数

s

指向 `seqcount_t` 实例的指针

SEQCNT_ZERO

SEQCNT_ZERO (name)

`seqcount_t` 的静态初始化器

参数

name

`seqcount_t` 实例的名称

__read_seqcount_begin

__read_seqcount_begin (s)

开始一个 `seqcount_t` 读取区段

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

返回

要传递给read_seqcount_retry()的计数

raw_read_seqcount_begin

raw_read_seqcount_begin (s)

开始一个不带 `lockdep` 的 `seqcount_t` 读取区段

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

返回

要传递给read_seqcount_retry()的计数

read_seqcount_begin

read_seqcount_begin (s)

开始一个 `seqcount_t` 读取临界区

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

返回

要传递给read_seqcount_retry()的计数

raw_read_seqcount

raw_read_seqcount (s)

读取原始 `seqcount_t` 计数器值

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

描述

`raw_read_seqcount` 打开给定 `seqcount_t` 的读取临界区,不进行任何 `lockdep` 检查,也不检查或屏蔽序列计数器的最低有效位 (LSB)。调用代码负责处理这些。

返回

要传递给read_seqcount_retry()的计数

raw_seqcount_try_begin

raw_seqcount_try_begin (s, start)

开始一个不带 `lockdep` 且不进行计数器稳定的 `seqcount_t` 读取临界区

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

start

要传递给read_seqcount_retry()的计数

描述

类似于raw_seqcount_begin(),但它允许在计数器为奇数时完全省略临界区,而不是明知会失败仍进行推测。

当计数器稳定化与获取锁大致等效且存在执行该操作的慢路径时很有用。

如果为真,`start` 将被设置为读取到的(偶数)序列计数。

返回

当读取临界区启动时为真。

raw_seqcount_begin

raw_seqcount_begin (s)

开始一个不带 `lockdep` 且不进行计数器稳定的 `seqcount_t` 读取临界区

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

描述

`raw_seqcount_begin` 打开给定 `seqcount_t` 的读取临界区。与read_seqcount_begin()不同,此函数不会等待计数稳定。如果写者在开始时处于活跃状态,它将在读取临界区结束时使read_seqcount_retry()失败,而不是在开始时进行稳定。

仅在特殊内核热路径中使用此函数,这些路径的读取区段很小,并且通过其他外部手段成功的概率很高。它将节省一条分支指令。

返回

要传递给read_seqcount_retry()的计数

__read_seqcount_retry

__read_seqcount_retry (s, start)

结束一个不带内存屏障的 `seqcount_t` 读取区段

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

start

计数,来自read_seqcount_begin()

描述

`__read_seqcount_retry` 类似于 `read_seqcount_retry`,但没有smp_rmb()内存屏障。调用者应确保在实际加载此临界区中要保护的任何变量之前,提供了smp_rmb()或等效的排序。

谨慎使用,仅限关键代码,并注释如何提供内存屏障。

返回

如果需要重试读取区段则为真,否则为假

read_seqcount_retry

read_seqcount_retry (s, start)

结束一个 `seqcount_t` 读取临界区

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

start

计数,来自read_seqcount_begin()

描述

`read_seqcount_retry` 关闭给定 `seqcount_t` 的读取临界区。如果临界区无效,则必须忽略(通常重试)。

返回

如果需要重试读取区段则为真,否则为假

raw_write_seqcount_begin

raw_write_seqcount_begin (s)

开始一个不带 `lockdep` 的 `seqcount_t` 写入区段

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

上下文

检查write_seqcount_begin()

raw_write_seqcount_end

raw_write_seqcount_end (s)

结束一个不带 `lockdep` 的 `seqcount_t` 写入区段

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

上下文

检查write_seqcount_end()

write_seqcount_begin_nested

write_seqcount_begin_nested (s, subclass)

使用自定义 `lockdep` 嵌套级别开始 `seqcount_t` 写入区段

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

子类

`lockdep` 嵌套级别

描述

参见运行时锁定正确性验证器

上下文

检查write_seqcount_begin()

write_seqcount_begin

write_seqcount_begin (s)

开始一个 `seqcount_t` 写端临界区

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

上下文

序列计数器写端区段必须是序列化且不可抢占的。当且仅当序列计数器写序列化锁已关联且可抢占时,抢占将自动禁用。如果读取器可以在硬中断或软中断上下文调用,则必须分别禁用中断或下半部。

write_seqcount_end

write_seqcount_end (s)

结束一个 `seqcount_t` 写端临界区

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

上下文

当且仅当序列计数器写序列化锁已关联且可抢占时,抢占将自动重新启用。

raw_write_seqcount_barrier

raw_write_seqcount_barrier (s)

执行一个 `seqcount_t` 写内存屏障

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

描述

这可以用于提供排序保证而不是通常的一致性保证。它节省一次 `wmb` 操作,因为它能合并两个连续的wmb()

注意,内存屏障周围的写入应声明为原子操作(例如通过 `WRITE_ONCE`):a) 确保写入对其他线程原子可见,避免编译器优化;b) 记录哪些写入旨在传播到读取临界区。这是必要的,因为内存屏障之前或之后的写入都不包含在序列写者临界区中,该临界区会确保读取者了解正在进行的写入。

seqcount_t seq;
bool X = true, Y = false;

void read(void)
{
        bool x, y;

        do {
                int s = read_seqcount_begin(&seq);

                x = X; y = Y;

        } while (read_seqcount_retry(&seq, s));

        BUG_ON(!x && !y);
}

void write(void)
{
        WRITE_ONCE(Y, true);

        raw_write_seqcount_barrier(seq);

        WRITE_ONCE(X, false);
}
write_seqcount_invalidate

write_seqcount_invalidate (s)

使正在进行的 `seqcount_t` 读取端操作失效

参数

s

指向 `seqcount_t` 或任何 `seqcount_LOCKNAME_t` 变体的指针

描述

在 `write_seqcount_invalidate` 之后,任何 `seqcount_t` 读取端操作都无法成功完成并看到比此旧的数据。

SEQCNT_LATCH_ZERO

SEQCNT_LATCH_ZERO (seq_name)

`seqcount_latch_t` 的静态初始化器

参数

seq_name

`seqcount_latch_t` 实例的名称

seqcount_latch_init

seqcount_latch_init (s)

`seqcount_latch_t` 的运行时初始化器

参数

s

指向 `seqcount_latch_t` 实例的指针

unsigned raw_read_seqcount_latch(const seqcount_latch_t *s)

选择偶数/奇数闩锁数据副本

参数

const seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

描述

有关详细信息和完整的读写器使用示例,请参见raw_write_seqcount_latch()

返回

序列计数器原始值。使用最低位作为索引来选择要读取的数据副本。然后必须使用raw_read_seqcount_latch_retry()检查完整计数器。

unsigned read_seqcount_latch(const seqcount_latch_t *s)

选择偶数/奇数闩锁数据副本

参数

const seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

描述

有关详细信息和完整的读写器使用示例,请参见write_seqcount_latch()

返回

序列计数器原始值。使用最低位作为索引来选择要读取的数据副本。然后必须使用read_seqcount_latch_retry()检查完整计数器。

int raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)

结束一个 `seqcount_latch_t` 读取区段

参数

const seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

unsigned start

计数,来自raw_read_seqcount_latch()

返回

如果需要重试读取区段则为真,否则为假

int read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)

结束一个 `seqcount_latch_t` 读取区段

参数

const seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

unsigned start

计数,来自read_seqcount_latch()

返回

如果需要重试读取区段则为真,否则为假

void raw_write_seqcount_latch(seqcount_latch_t *s)

将闩锁读取器重定向到偶数/奇数副本

参数

seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

void write_seqcount_latch_begin(seqcount_latch_t *s)

将闩锁读取器重定向到奇数副本

参数

seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

描述

闩锁技术是一种多版本并发控制方法,允许在非原子修改期间进行查询。如果您能保证查询永远不会中断修改——例如,并发严格发生在 CPU 之间——那么您很可能不需要它。

传统的 RCU/无锁数据结构依赖于原子修改来确保查询观察到旧状态或新状态,而闩锁允许对非原子更新实现同样的效果。代价是存储成本翻倍;我们必须维护整个数据结构的两个副本。

简单来说:我们首先修改一份副本,然后修改另一份。这确保始终有一份副本处于稳定状态,准备好给我们提供答案。

基本形式是类似的数据结构

struct latch_struct {
        seqcount_latch_t        seq;
        struct data_struct      data[2];
};

在这里,假设外部序列化的修改执行以下操作

void latch_modify(struct latch_struct *latch, ...)
{
        write_seqcount_latch_begin(&latch->seq);
        modify(latch->data[0], ...);
        write_seqcount_latch(&latch->seq);
        modify(latch->data[1], ...);
        write_seqcount_latch_end(&latch->seq);
}

查询将具有以下形式

struct entry *latch_query(struct latch_struct *latch, ...)
{
        struct entry *entry;
        unsigned seq, idx;

        do {
                seq = read_seqcount_latch(&latch->seq);

                idx = seq & 0x01;
                entry = data_query(latch->data[idx], ...);

        // This includes needed smp_rmb()
        } while (read_seqcount_latch_retry(&latch->seq, seq));

        return entry;
}

因此,在修改期间,查询首先被重定向到 `data[1]`。然后我们修改 `data[0]`。完成后,我们将查询重定向回 `data[0]`,然后我们可以修改 `data[1]`。

注意

对于原子修改的非要求_不_包括在数据是动态数据结构的情况下发布新条目。

一次迭代可能从 `data[0]` 开始,并被挂起足够长的时间以错过整个修改序列,一旦恢复,它可能会观察到新条目。

注意2

当数据是动态数据结构时;应使用常规 RCU 模式来管理其中对象的生命周期。

void write_seqcount_latch(seqcount_latch_t *s)

将闩锁读取器重定向到偶数副本

参数

seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

void write_seqcount_latch_end(seqcount_latch_t *s)

结束一个 `seqcount_latch_t` 写入区段

参数

seqcount_latch_t *s

指向 `seqcount_latch_t` 的指针

描述

标记 `seqcount_latch_t` 写者区段的结束,此时闩锁保护数据的所有副本都已更新。

seqlock_init

seqlock_init (sl)

`seqlock_t` 的动态初始化器

参数

sl

指向 `seqlock_t` 实例的指针

DEFINE_SEQLOCK

DEFINE_SEQLOCK (sl)

定义一个静态分配的 `seqlock_t`

参数

sl

`seqlock_t` 实例的名称

unsigned read_seqbegin(const seqlock_t *sl)

开始一个 `seqlock_t` 读端临界区

参数

const seqlock_t *sl

指向 `seqlock_t` 的指针

返回

计数,将传递给read_seqretry()

unsigned read_seqretry(const seqlock_t *sl, unsigned start)

结束一个 `seqlock_t` 读端区段

参数

const seqlock_t *sl

指向 `seqlock_t` 的指针

unsigned start

计数,来自read_seqbegin()

描述

`read_seqretry` 关闭给定 `seqlock_t` 的读端临界区。如果临界区无效,则必须忽略(通常重试)。

返回

如果需要重试读取区段则为真,否则为假

void write_seqlock(seqlock_t *sl)

开始一个 `seqlock_t` 写端临界区

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

`write_seqlock` 为给定的 `seqlock_t` 打开一个写端临界区。它还隐式地获取嵌入在该顺序锁中的 `spinlock_t`。因此,所有 `seqlock_t` 写端区段都自动序列化且不可抢占。

上下文

如果 `seqlock_t` 读区段或其他写端临界区可以从硬中断或软中断上下文调用,请改用此函数的 `_irqsave` 或 `_bh` 变体。

void write_sequnlock(seqlock_t *sl)

结束一个 `seqlock_t` 写端临界区

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

`write_sequnlock` 关闭给定 `seqlock_t` 的(序列化且不可抢占的)写端临界区。

void write_seqlock_bh(seqlock_t *sl)

开始一个禁用软中断的 `seqlock_t` 写入区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

write_seqlock()的 `_bh` 变体。仅当读端区段或其他写端区段可以从软中断上下文调用时使用。

void write_sequnlock_bh(seqlock_t *sl)

结束一个禁用软中断的 `seqlock_t` 写入区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

`write_sequnlock_bh` 关闭使用write_seqlock_bh()打开的序列化、不可抢占且禁用软中断的 `seqlock_t` 写端临界区。

void write_seqlock_irq(seqlock_t *sl)

开始一个不可中断的 `seqlock_t` 写入区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

write_seqlock()的 `_irq` 变体。仅当读端区段或其他写区段可以从硬中断上下文调用时使用。

void write_sequnlock_irq(seqlock_t *sl)

结束一个不可中断的 `seqlock_t` 写入区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

`write_sequnlock_irq` 关闭使用write_seqlock_irq()打开的序列化且不可中断的 `seqlock_t` 写端临界区。

write_seqlock_irqsave

write_seqlock_irqsave (lock, flags)

开始一个不可中断的 `seqlock_t` 写入区段

参数

lock

指向 `seqlock_t` 的指针

flags

栈分配的存储空间,用于保存调用者的本地中断状态,将传递给write_sequnlock_irqrestore()

描述

write_seqlock()的 `_irqsave` 变体。仅当读端区段或其他写区段可以从硬中断上下文调用时使用它。

void write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags)

结束不可中断的 `seqlock_t` 写入区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

unsigned long flags

调用者保存的中断状态,来自write_seqlock_irqsave()

描述

`write_sequnlock_irqrestore` 关闭先前使用write_seqlock_irqsave()打开的序列化且不可中断的 `seqlock_t` 写入区段。

void read_seqlock_excl(seqlock_t *sl)

开始一个 `seqlock_t` 锁定读取器区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

`read_seqlock_excl` 打开一个 `seqlock_t` 锁定读取器临界区。一个锁定读取器独占地锁住_其他写者_和_其他锁定读取器_,但它不更新嵌入的序列号。

锁定读取器表现得像普通的spin_lock()/spin_unlock()

打开的读取区段必须使用read_sequnlock_excl()关闭。

上下文

如果 `seqlock_t` 写区段_或其他读区段_可以从硬中断或软中断上下文调用,请改用此函数的 `_irqsave` 或 `_bh` 变体。

void read_sequnlock_excl(seqlock_t *sl)

结束一个 `seqlock_t` 锁定读取器临界区

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

void read_seqlock_excl_bh(seqlock_t *sl)

开始一个禁用软中断的 `seqlock_t` 锁定读取器区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

read_seqlock_excl()的 `_bh` 变体。仅当 `seqlock_t` 写端区段_或其他读区段_可以从软中断上下文调用时才使用此变体。

void read_sequnlock_excl_bh(seqlock_t *sl)

停止一个 `seqlock_t` 禁用软中断的锁定读取器区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

void read_seqlock_excl_irq(seqlock_t *sl)

开始一个不可中断的 `seqlock_t` 锁定读取器区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

描述

read_seqlock_excl()的 `_irq` 变体。仅当 `seqlock_t` 写端区段_或其他读区段_可以从硬中断上下文调用时才使用此函数。

void read_sequnlock_excl_irq(seqlock_t *sl)

结束一个禁用中断的 `seqlock_t` 锁定读取器区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

read_seqlock_excl_irqsave

read_seqlock_excl_irqsave (lock, flags)

开始一个不可中断的 `seqlock_t` 锁定读取器区段

参数

lock

指向 `seqlock_t` 的指针

flags

栈分配的存储空间,用于保存调用者的本地中断状态,将传递给read_sequnlock_excl_irqrestore()

描述

read_seqlock_excl()的 `_irqsave` 变体。仅当 `seqlock_t` 写端区段_或其他读区段_可以从硬中断上下文调用时才使用此函数。

void read_sequnlock_excl_irqrestore(seqlock_t *sl, unsigned long flags)

结束不可中断的 `seqlock_t` 锁定读取器区段

参数

seqlock_t *sl

指向 `seqlock_t` 的指针

unsigned long flags

调用者保存的中断状态,来自read_seqlock_excl_irqsave()

void read_seqbegin_or_lock(seqlock_t *lock, int *seq)

开始一个 `seqlock_t` 无锁或锁定读取器

参数

seqlock_t *lock

指向 `seqlock_t` 的指针

int *seq

标记和返回参数。如果传递的值为偶数,读取器将成为read_seqbegin()中的_无锁_ `seqlock_t` 读取器。如果传递的值为奇数,读取器将成为read_seqlock_excl()中的_锁定_读取器。在首次调用此函数时,调用者_必须_初始化并向**seq**传递一个偶数值;这样,可以首先乐观地尝试无锁读取。

描述

`read_seqbegin_or_lock` 是一个 API,旨在首先乐观地尝试正常的无锁 `seqlock_t` 读取区段。如果找到奇数计数器,则无锁读取尝试失败,并且下一次读取迭代将自身转换为完全的 `seqlock_t` 锁定读取器。

这通常用于在写端活动急剧增加时,避免 `seqlock_t` 无锁读取器饥饿(过多重试循环)。

检查序列计数器和顺序锁以获取模板示例代码。

上下文

如果 `seqlock_t` 写区段_或其他读区段_可以从硬中断或软中断上下文调用,请改用此函数的 `_irqsave` 或 `_bh` 变体。

返回

遇到的序列计数器值,通过作为返回参数重载的**seq**参数返回。此返回值必须通过need_seqretry()进行检查。如果读取区段需要重试,则此返回值也必须作为下一次read_seqbegin_or_lock()迭代的**seq**参数传递。

int need_seqretry(seqlock_t *lock, int seq)

验证 `seqlock_t` “锁定或无锁”读取区段

参数

seqlock_t *lock

指向 `seqlock_t` 的指针

int seq

序列计数,来自read_seqbegin_or_lock()

返回

如果需要重试读取区段则为真,否则为假

void done_seqretry(seqlock_t *lock, int seq)

结束 `seqlock_t` “锁定或无锁”读取器区段

参数

seqlock_t *lock

指向 `seqlock_t` 的指针

int seq

计数,来自read_seqbegin_or_lock()

描述

`done_seqretry` 结束使用read_seqbegin_or_lock()开始并由need_seqretry()验证的 `seqlock_t` 读端临界区。

unsigned long read_seqbegin_or_lock_irqsave(seqlock_t *lock, int *seq)

开始一个 `seqlock_t` 无锁读取器,或一个不可中断的锁定读取器

参数

seqlock_t *lock

指向 `seqlock_t` 的指针

int *seq

标记和返回参数。检查read_seqbegin_or_lock()

描述

这是read_seqbegin_or_lock()的 `_irqsave` 变体。仅当 `seqlock_t` 写区段_或其他读区段_可以从硬中断上下文调用时才使用它。

  1. 在锁定读取器情况下保存的本地中断状态,将传递给done_seqretry_irqrestore()

  2. 遇到的序列计数器值,通过作为返回参数重载的**seq**返回。检查read_seqbegin_or_lock()

注意

中断仅在“锁定读取器”模式下禁用。

void done_seqretry_irqrestore(seqlock_t *lock, int seq, unsigned long flags)

结束一个 `seqlock_t` 无锁读取器,或一个不可中断的锁定读取器区段

参数

seqlock_t *lock

指向 `seqlock_t` 的指针

int seq

计数,来自read_seqbegin_or_lock_irqsave()

unsigned long flags

调用者在锁定读取器情况下保存的本地中断状态,也来自read_seqbegin_or_lock_irqsave()

描述

这是done_seqretry()的 `_irqrestore` 变体。读取区段必须已通过read_seqbegin_or_lock_irqsave()打开,并由need_seqretry()验证。

scoped_seqlock_read

scoped_seqlock_read (_seqlock, _target)

执行读端临界区,无需手动序列计数器处理或调用其他辅助函数

参数

_seqlock

指向保护数据的 `seqlock_t` 的指针

_target

一个enum ss_state:`{ss_lock, ss_lock_irqsave, ss_lockless}` 之一,指示临界读取区段的类型

描述

示例

scoped_seqlock_read (&lock, ss_lock) {
    // read-side critical section
}

首先尝试无锁通过。如果失败,则在持有锁的情况下重新启动临界区。