序列計數器和順序鎖

簡介

序列計數器是一種讀寫一致性機制,具有無鎖讀取器(只讀重試迴圈),並且沒有寫者飢餓。 它們用於很少寫入的資料(例如,系統時間),其中讀取器需要一組一致的資訊,並且願意在該資訊更改時重試。

當讀取端臨界區開始時的序列計數為偶數,並且在臨界區結束時再次讀取相同的序列計數值時,資料集是一致的。 集合中的資料必須在讀取端臨界區內複製出來。 如果在臨界區的開始和結束之間序列計數已更改,則讀取器必須重試。

寫者在其臨界區的開始和結束時遞增序列計數。 啟動臨界區後,序列計數為奇數,並向讀取器指示正在進行更新。 在寫入端臨界區的末尾,序列計數再次變為偶數,這使讀取器可以取得進展。

序列計數器寫入端臨界區絕不能被讀取端部分搶佔或中斷。 否則,由於奇數序列計數值和中斷的寫入器,讀取器將旋轉整個排程程式節拍。 如果該讀取器屬於即時排程類,則它可以永遠旋轉,並且核心將發生死鎖。

如果受保護的資料包含指標,則無法使用此機制,因為寫入器可能會使讀取器正在跟蹤的指標無效。

序列計數器 (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() 驗證。