序列计数器和顺序锁

简介

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

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

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

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

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

序列计数器 (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_try_begin

raw_seqcount_try_begin (s, start)

开始没有 lockdep 且没有计数器稳定的 seqcount_t 读取临界区

参数

s

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

start

要传递给 read_seqcount_retry() 的计数

描述

raw_seqcount_begin() 类似,除非它可以完全省略奇数的临界区,而不是进行已知会失败的推测。

当计数器稳定或多或少等同于获取锁并且存在执行此操作的慢速路径时,此方法很有用。

如果为 true,则 start 将设置为读取的(偶数)序列计数。

返回值

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

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)

启动没有 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 变体的指针

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-writer 临界区中,该临界区将确保读取器知道正在进行的写入

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

标志

用于保存调用方的本地中断状态的堆栈分配存储,要传递给 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 的指针

标志

用于保存调用方的本地中断状态的堆栈分配存储,要传递给 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

标记和返回参数。 如果传递的值为偶数,则读取器将成为一个_无锁_ seqlock_t 读取器,如 read_seqbegin() 中所示。 如果传递的值为奇数,则读取器将成为一个_锁定_读取器,如 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() 验证。