序列计数器和顺序锁

简介

序列计数器是一种读写一致性机制,具有无锁读取器(只读重试循环),并且没有写入器饥饿。 它们用于很少写入的数据(例如系统时间),其中读取器需要一组一致的信息,并且如果该信息发生更改,则愿意重试。

当读取端临界区开始时的序列计数是偶数,并且在临界区末尾再次读取相同的序列计数值时,数据集是一致的。 必须在读取端临界区内复制出数据集中的数据。 如果序列计数在临界区的开始和结束之间发生更改,则读取器必须重试。

写入器在其临界区的开始和结束时递增序列计数。 启动临界区后,序列计数为奇数,并向读取器指示正在进行更新。 在写入端临界区结束时,序列计数再次变为偶数,这使读取器可以继续进行。

序列计数器写入端临界区绝不能被读取端部分抢占或中断。 否则,由于奇数序列计数值和被中断的写入器,读取器将在整个调度程序滴答内旋转。 如果该读取器属于实时调度类,则它可能会永远旋转,并且内核将发生活锁。

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

序列计数器 (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 = 0;
    do {
            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_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() 或等效的排序。

请谨慎使用,仅在关键代码中使用,并注释如何提供屏障。

返回

如果需要重试读取段,则为 true,否则为 false

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 的读取临界区。如果临界区无效,则必须忽略(并通常重试)。

返回

如果需要重试读取段,则为 true,否则为 false

raw_write_seqcount_begin

raw_write_seqcount_begin (s)

开始一个 seqcount_t 写入段,不带 lockdep

参数

s

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

上下文

检查 write_seqcount_begin()

raw_write_seqcount_end

raw_write_seqcount_end (s)

结束一个 seqcount_t 写入段,不带 lockdep

参数

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 变体的指针

subclass

lockdep 嵌套级别

描述

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

上下文

检查 write_seqcount_begin()

write_seqcount_begin

write_seqcount_begin (s)

开始一个 seqcount_t 写入端临界区

参数

s

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

上下文

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

write_seqcount_end

write_seqcount_end (s)

结束一个 seqcount_t 写入端临界区

参数

s

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

上下文

当且仅当 seqcount 写入串行化锁已关联且可抢占时,才会自动重新启用抢占。

raw_write_seqcount_barrier

raw_write_seqcount_barrier (s)

执行一个 seqcount_t 写入屏障

参数

s

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

描述

这可以用来提供排序保证,而不是通常的一致性保证。它便宜一个 wmb,因为它能折叠两个背靠背的 wmb()。

请注意,屏障周围的写入操作应声明为原子的(例如,通过 WRITE_ONCE):a) 确保写入操作对其他线程以原子方式可见,避免编译器优化; b) 记录哪些写入操作是要传播到读取器临界区的。这是必要的,因为屏障之前或之后的写入操作都不在 seq-写入器临界区中,后者将确保读取器意识到正在进行的写入操作

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()

返回

如果需要重试读取段,则为 true,否则为 false

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()

返回

如果需要重试读取段,则为 true,否则为 false

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]。

注意2

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

注意

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

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

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 的读取侧临界区。如果临界区无效,则必须忽略(并且通常会重试)。

返回

如果需要重试读取段,则为 true,否则为 false

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()

返回

如果需要重试读取部分,则为 true;否则为 false

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()验证。