工作佇列¶
- 日期:
2010 年 9 月
- 作者:
Tejun Heo <tj@kernel.org>
- 作者:
Florian Mickler <florian@mickler.org>
引言¶
在許多情況下,需要非同步程序執行上下文,而工作佇列(wq)API 是此類情況最常用的機制。
當需要此類非同步執行上下文時,會將描述要執行的函式的“工作項”放入佇列中。一個獨立的工作執行緒充當非同步執行上下文。該佇列稱為“工作佇列”,該執行緒稱為“工作者”。
當工作佇列中有工作項時,工作者會一個接一個地執行與這些工作項關聯的函式。當工作佇列中沒有剩餘工作項時,工作者會變為空閒狀態。當新的工作項被排隊時,工作者會再次開始執行。
為什麼選擇併發管理工作佇列?¶
在原始的工作佇列(wq)實現中,多執行緒(MT)工作佇列每個 CPU 有一個工作執行緒,而單執行緒(ST)工作佇列在系統範圍內只有一個工作執行緒。一個 MT 工作佇列需要維護與 CPU 數量相同的工作者數量。多年來,核心中出現了許多 MT 工作佇列使用者,隨著 CPU 核心數量的持續增長,一些系統在啟動時就耗盡了預設的 32k PID 空間。
儘管 MT 工作佇列浪費了大量資源,但其提供的併發級別並不令人滿意。這種限制對 ST 和 MT 工作佇列都普遍存在,儘管在 MT 上不那麼嚴重。每個工作佇列都維護自己獨立的工作執行緒池。一個 MT 工作佇列每個 CPU 只能提供一個執行上下文,而一個 ST 工作佇列整個系統只有一個。工作項必須爭奪這些非常有限的執行上下文,導致各種問題,包括圍繞單個執行上下文容易發生死鎖。
所提供的併發級別與資源使用之間的矛盾,也迫使工作佇列使用者做出不必要的權衡,例如 libata 選擇使用 ST 工作佇列進行輪詢 PIO,並接受了兩個輪詢 PIO 不能同時進行的不必要限制。由於 MT 工作佇列沒有提供更好的併發性,需要更高併發級別的使用者,如 async 或 fscache,不得不實現自己的執行緒池。
併發管理工作佇列(cmwq)是工作佇列的重新實現,重點關注以下目標。
保持與原始工作佇列 API 的相容性。
使用所有工作佇列共享的每 CPU 統一工作執行緒池,以按需提供靈活的併發級別,同時不浪費大量資源。
自動調節工作執行緒池和併發級別,使 API 使用者無需擔心此類細節。
設計¶
為了簡化函式的非同步執行,引入了一個新的抽象——工作項。
工作項是一個簡單的結構體,它包含一個指向將要非同步執行的函式的指標。每當驅動程式或子系統希望非同步執行某個函式時,它必須設定一個指向該函式的工作項,並將該工作項排隊到工作佇列中。
工作項可以線上程上下文或 BH(軟中斷)上下文中執行。
對於執行緒化工作佇列,專門的執行緒,稱為 [k]工作執行緒,一個接一個地執行佇列中的函式。如果沒有工作排隊,工作執行緒將變為空閒狀態。這些工作執行緒在工作執行緒池中進行管理。
cmwq 的設計區分了子系統和驅動程式用於排隊工作項的使用者面向工作佇列,以及管理工作執行緒池和處理已排隊工作項的後端機制。
每個可能的 CPU 有兩個工作執行緒池,一個用於普通工作項,另一個用於高優先順序工作項;還有一些額外的工作執行緒池用於服務排隊到非繫結工作佇列的工作項——這些後端池的數量是動態的。
BH 工作佇列使用相同的框架。然而,由於只能有一個併發執行上下文,因此無需擔心併發性。每個每 CPU BH 工作執行緒池只包含一個偽工作者,它代表 BH 執行上下文。BH 工作佇列可以被視為軟中斷的便捷介面。
子系統和驅動程式可以根據需要透過特殊的工作佇列 API 函式建立和排隊工作項。它們可以透過在工作佇列上設定標誌來影響工作項的執行方式。這些標誌包括 CPU 區域性性、併發限制、優先順序等。要獲取詳細概述,請參考下方 alloc_workqueue() 的 API 描述。
當工作項排隊到工作佇列時,目標工作執行緒池會根據佇列引數和工作佇列屬性確定,並附加到工作執行緒池的共享工作列表中。例如,除非特別覆蓋,繫結工作佇列的工作項將排隊到與發起者執行的 CPU 相關聯的普通或高優先順序工作執行緒池的工作列表中。
對於任何執行緒池實現,管理併發級別(有多少執行上下文是活動的)是一個重要問題。cmwq 試圖將併發性保持在最小但足夠的水平。最小是為了節省資源,足夠是確保系統以其最大容量執行。
每個繫結到實際 CPU 的工作執行緒池透過掛接到排程器來實現併發管理。每當活躍的工作者喚醒或休眠時,工作執行緒池都會收到通知,並跟蹤當前可執行的工作者數量。通常,工作項不應占用大量 CPU 並消耗大量週期。這意味著保持足夠的併發性以防止工作處理停滯是最佳的。只要 CPU 上有一個或多個可執行的工作者,工作執行緒池就不會開始執行新的工作,但是,當最後一個正在執行的工作者進入睡眠狀態時,它會立即排程一個新的工作者,以便在有待處理工作項時 CPU 不會空閒。這允許使用最少數量的工作者而不會損失執行頻寬。
保留空閒工作者除了 kworker 執行緒的記憶體空間外沒有其他成本,因此 cmwq 會在終止它們之前保留空閒工作者一段時間。
對於非繫結工作佇列,後端池的數量是動態的。可以使用 apply_workqueue_attrs() 為非繫結工作佇列分配自定義屬性,工作佇列將自動建立與這些屬性匹配的後端工作執行緒池。調節併發級別的責任在於使用者。還有一個標誌可以標記繫結工作佇列以忽略併發管理。詳情請參考 API 部分。
前進保障依賴於在需要更多執行上下文時可以建立工作者,這反過來又透過使用救援工作者得到保障。所有可能在處理記憶體回收的程式碼路徑中使用的工作項都必須排隊到為記憶體壓力下的執行預留了救援工作者的工作佇列中。否則,工作執行緒池可能會因為等待執行上下文釋放而死鎖。
應用程式程式設計介面 (API)¶
alloc_workqueue() 分配一個工作佇列。原始的 create_*workqueue() 函式已被棄用,並計劃移除。alloc_workqueue() 接受三個引數——@name、@flags 和 @max_active。@name 是工作佇列的名稱,如果存在救援執行緒,它也用作救援執行緒的名稱。
工作佇列不再管理執行資源,而是作為前進保障、重新整理和工作項屬性的域。@flags 和 @max_active 控制工作項如何分配執行資源、排程和執行。
flags¶
WQ_BHBH 工作佇列可以被視為軟中斷的便捷介面。BH 工作佇列始終是每 CPU 的,所有 BH 工作項都按排隊順序在排隊 CPU 的軟中斷上下文中執行。
所有 BH 工作佇列的
max_active必須為 0,並且WQ_HIGHPRI是唯一允許的附加標誌。BH 工作項不能睡眠。所有其他功能,如延遲排隊、重新整理和取消,都受支援。
WQ_UNBOUND排隊到非繫結工作佇列的工作項由特殊的工作執行緒池服務,這些池託管不繫結到任何特定 CPU 的工作者。這使得工作隊列表現為一個簡單的執行上下文提供者,不帶併發管理。非繫結工作執行緒池會盡快嘗試啟動工作項的執行。非繫結工作佇列犧牲了局部性,但適用於以下情況。
預計併發級別需求會有較大波動,並且使用繫結工作佇列可能會導致在發起者在不同 CPU 之間跳轉時,跨不同 CPU 建立大量大部分未使用的工作者。
長時間執行的 CPU 密集型工作負載,可以由系統排程器更好地管理。
WQ_FREEZABLE可凍結工作佇列參與系統掛起操作的凍結階段。工作佇列上的工作項將被耗盡,並且在解凍之前不會有新的工作項開始執行。
WQ_MEM_RECLAIM所有可能在記憶體回收路徑中使用的工作佇列必須設定此標誌。無論記憶體壓力如何,工作佇列都保證至少有一個執行上下文。
WQ_HIGHPRI高優先順序工作佇列的工作項被排隊到目標 CPU 的高優先順序工作執行緒池。高優先順序工作執行緒池由具有更高 nice 級別的工作執行緒服務。
請注意,普通和高優先順序工作執行緒池之間不相互作用。每個都維護自己獨立的工作者池,並在其工作者之間實現併發管理。
WQ_CPU_INTENSIVECPU 密集型工作佇列的工作項不計入併發級別。換句話說,可執行的 CPU 密集型工作項不會阻止同一工作執行緒池中的其他工作項開始執行。這對於預期會佔用大量 CPU 週期並由系統排程器調節其執行的繫結工作項很有用。
儘管 CPU 密集型工作項不計入併發級別,但它們的執行開始仍然受併發管理調節,並且可執行的非 CPU 密集型工作項可能會延遲 CPU 密集型工作項的執行。
此標誌對於非繫結工作佇列無意義。
max_active¶
@max_active 決定了每個 CPU 可以分配給工作佇列的工作項的最大執行上下文數量。例如,當 @max_active 為 16 時,每個 CPU 最多可以同時執行 16 個工作佇列的工作項。這始終是每 CPU 屬性,即使對於非繫結工作佇列也是如此。
@max_active 的最大限制是 2048,當指定為 0 時使用的預設值是 1024。這些值被選擇得足夠高,以便它們不是限制因素,同時在失控情況下提供保護。
工作佇列的活躍工作項數量通常由工作佇列的使用者調節,更具體地說,由使用者可以同時排隊的工作項數量決定。除非有特定的需要來限制活躍工作項的數量,否則建議指定“0”。
一些使用者依賴嚴格的執行順序,即在任何給定時間只有一個工作項正在執行,並且工作項按排隊順序處理。雖然過去透過組合 @max_active 為 1 和 WQ_UNBOUND 可以實現這種行為,但現在不再是這樣。請改用 alloc_ordered_workqueue()。
執行場景示例¶
以下執行場景示例試圖說明 cmwq 在不同配置下的行為。
工作項 w0、w1、w2 被排隊到同一 CPU 上的繫結工作佇列 q0。w0 佔用 CPU 5 毫秒,然後休眠 10 毫秒,然後在完成前再次佔用 CPU 5 毫秒。w1 和 w2 佔用 CPU 5 毫秒,然後休眠 10 毫秒。
忽略所有其他任務、工作和處理開銷,並假設簡單的 FIFO 排程,以下是原始工作佇列可能事件序列的一個高度簡化版本。
TIME IN MSECS EVENT
0 w0 starts and burns CPU
5 w0 sleeps
15 w0 wakes up and burns CPU
20 w0 finishes
20 w1 starts and burns CPU
25 w1 sleeps
35 w1 wakes up and finishes
35 w2 starts and burns CPU
40 w2 sleeps
50 w2 wakes up and finishes
而使用 @max_active >= 3 的 cmwq,
TIME IN MSECS EVENT
0 w0 starts and burns CPU
5 w0 sleeps
5 w1 starts and burns CPU
10 w1 sleeps
10 w2 starts and burns CPU
15 w2 sleeps
15 w0 wakes up and burns CPU
20 w0 finishes
20 w1 wakes up and finishes
25 w2 wakes up and finishes
如果 @max_active == 2,
TIME IN MSECS EVENT
0 w0 starts and burns CPU
5 w0 sleeps
5 w1 starts and burns CPU
10 w1 sleeps
15 w0 wakes up and burns CPU
20 w0 finishes
20 w1 wakes up and finishes
20 w2 starts and burns CPU
25 w2 sleeps
35 w2 wakes up and finishes
現在,假設 w1 和 w2 被排隊到另一個設定了 WQ_CPU_INTENSIVE 的工作佇列 q1,
TIME IN MSECS EVENT
0 w0 starts and burns CPU
5 w0 sleeps
5 w1 and w2 start and burn CPU
10 w1 sleeps
15 w2 sleeps
15 w0 wakes up and burns CPU
20 w0 finishes
20 w1 wakes up and finishes
25 w2 wakes up and finishes
指導原則¶
如果工作佇列可能處理在記憶體回收期間使用的工作項,請不要忘記使用
WQ_MEM_RECLAIM。每個設定了WQ_MEM_RECLAIM的工作佇列都為其保留了一個執行上下文。如果在記憶體回收期間使用的多個工作項之間存在依賴關係,它們應該被排隊到各自獨立的、都設定了WQ_MEM_RECLAIM的工作佇列中。除非需要嚴格的排序,否則沒有必要使用 ST 工作佇列。
除非有特定需求,否則建議將 @max_active 設定為 0。在大多數用例中,併發級別通常遠低於預設限制。
工作佇列充當前進保障(
WQ_MEM_RECLAIM)、重新整理和工作項屬性的域。不涉及記憶體回收、不需要作為一組工作項的一部分進行重新整理,且不需要任何特殊屬性的工作項,可以使用系統工作佇列之一。使用專用工作佇列和系統工作佇列在執行特性上沒有區別。注意:如果某些東西可能生成超過 @max_active 的未完成工作項(請對您的生產者進行壓力測試),它可能會使系統工作佇列飽和,並可能導致死鎖。它應該使用自己的專用工作佇列,而不是系統工作佇列。
除非工作項預計會消耗大量 CPU 週期,否則使用繫結工作佇列通常是有益的,因為這會增加工作佇列操作和工作項執行的區域性性。
親和範圍¶
非繫結工作佇列根據其親和範圍對 CPU 進行分組,以提高快取區域性性。例如,如果一個工作佇列使用預設的“cache”親和範圍,它將根據最後一級快取邊界對 CPU 進行分組。排隊到該工作佇列的工作項將被分配給與發起 CPU 共享最後一級快取的某個 CPU 上的工作者。一旦啟動,工作者是否允許移出該範圍取決於該範圍的 affinity_strict 設定。
工作佇列目前支援以下親和範圍。
default(預設)使用模組引數
workqueue.default_affinity_scope中的範圍,該引數始終設定為以下範圍之一。cpuCPU 不分組。在一個 CPU 上發出的工作項由同一 CPU 上的工作者處理。這使得非繫結工作隊列表現為不帶併發管理的每 CPU 工作佇列。
smtCPU 根據 SMT 邊界分組。這通常意味著每個物理 CPU 核心的邏輯執行緒被分組在一起。
cache(快取)CPU 根據快取邊界分組。使用哪個具體的快取邊界由架構程式碼決定。在許多情況下使用 L3。這是預設的親和範圍。
numaCPU 根據 NUMA 邊界分組。
system(系統)所有 CPU 都放在同一個組中。工作佇列不嘗試在靠近發起 CPU 的 CPU 上處理工作項。
預設親和範圍可以透過模組引數 workqueue.default_affinity_scope 更改,特定工作佇列的親和範圍可以使用 apply_workqueue_attrs() 更改。
如果設定了 WQ_SYSFS,工作佇列將在其 /sys/devices/virtual/workqueue/WQ_NAME/ 目錄下擁有以下與親和範圍相關的介面檔案。
affinity_scope讀取以檢視當前親和範圍。寫入以更改。
噹噹前範圍是 default 時,讀取此檔案還將顯示括號中的當前有效範圍,例如
default (cache)。affinity_strict預設為 0,表示親和範圍不嚴格。當一個工作項開始執行時,工作佇列會盡力確保工作者在其親和範圍之內,這被稱為“歸位”(repatriation)。一旦啟動,排程器可以根據需要自由地將工作者移動到系統中的任何位置。這使得在受益於範圍區域性性的同時,如果必要且可用,仍能利用其他 CPU。
如果設定為 1,則保證該範圍內的所有工作者始終在該範圍之內。這在跨越親和範圍可能產生其他影響時可能有用,例如在功耗或工作負載隔離方面。嚴格的 NUMA 範圍也可以用於匹配舊核心的工作佇列行為。
親和範圍與效能¶
如果非繫結工作佇列的行為在絕大多數用例中無需進一步調優即可達到最佳,那將是理想的。不幸的是,在當前核心中,區域性性和利用率之間存在顯著的權衡,這使得在大量使用工作佇列時需要明確配置。
更高的區域性性帶來更高的效率,即在消耗相同 CPU 週期數的情況下完成更多工作。然而,如果工作項沒有被髮起者充分分散到各個親和範圍中,更高的區域性性也可能導致整體系統利用率降低。以下使用 dm-crypt 進行的效能測試清楚地說明了這種權衡。
測試在一顆擁有 12 核心/24 執行緒、分佈在四個 L3 快取(AMD Ryzen 9 3900x)上的 CPU 上執行。為保持一致性,CPU 時鐘加速已關閉。/dev/dm-0 是在 NVME SSD(三星 990 PRO)上建立的 dm-crypt 裝置,並使用預設設定的 cryptsetup 開啟。
場景 1:足夠多的發起者和工作分佈在整機上¶
使用的命令
$ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k --ioengine=libaio \
--iodepth=64 --runtime=60 --numjobs=24 --time_based --group_reporting \
--name=iops-test-job --verify=sha512
有 24 個發起者,每個併發發出 64 個 IO。 --verify=sha512 使 fio 每次都生成並回讀內容,這使得發起者和 kcryptd 之間的執行區域性性變得重要。以下是根據 kcryptd 上的不同親和範圍設定,經過五次執行測量的讀取頻寬和 CPU 利用率。頻寬單位為 MiBps,CPU 利用率單位為百分比。
親和性 |
頻寬 (MiBps) |
CPU 利用率 (%) |
|---|---|---|
system(系統) |
1159.40 ±1.34 |
99.31 ±0.02 |
cache(快取) |
1166.40 ±0.89 |
99.34 ±0.01 |
cache (嚴格) |
1166.00 ±0.71 |
99.35 ±0.01 |
當有足夠多的發起者分佈在整個系統時,“cache”模式,無論是嚴格的還是非嚴格的,都沒有缺點。所有三種配置都使整個機器達到飽和,但由於區域性性提高,快取親和性配置的效能優於其他配置 0.6%。
場景 2:發起者較少,但工作量足以達到飽和¶
使用的命令
$ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \
--ioengine=libaio --iodepth=64 --runtime=60 --numjobs=8 \
--time_based --group_reporting --name=iops-test-job --verify=sha512
與上一個場景唯一的區別是 --numjobs=8。發起者數量只有原來的三分之一,但總工作量仍然足以使系統飽和。
親和性 |
頻寬 (MiBps) |
CPU 利用率 (%) |
|---|---|---|
system(系統) |
1155.40 ±0.89 |
97.41 ±0.05 |
cache(快取) |
1154.40 ±1.14 |
96.15 ±0.09 |
cache (嚴格) |
1112.00 ±4.64 |
93.26 ±0.35 |
這足以使系統飽和。“system”和“cache”都幾乎使機器飽和,但未完全飽和。“cache”使用較少的 CPU,但更好的效率使其達到與“system”相同的頻寬。
八個發起者在四個 L3 快取範圍上移動,仍然使“cache (嚴格)”模式基本飽和機器,但現在工作守恆的損失開始顯現,導致 3.7% 的頻寬損失。
場景 3:發起者更少,工作量不足以達到飽和¶
使用的命令
$ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \
--ioengine=libaio --iodepth=64 --runtime=60 --numjobs=4 \
--time_based --group_reporting --name=iops-test-job --verify=sha512
同樣,唯一的區別是 --numjobs=4。發起者數量減少到四個後,現在沒有足夠的工作使整個系統飽和,頻寬變得依賴於完成延遲。
親和性 |
頻寬 (MiBps) |
CPU 利用率 (%) |
|---|---|---|
system(系統) |
993.60 ±1.82 |
75.49 ±0.06 |
cache(快取) |
973.40 ±1.52 |
74.90 ±0.07 |
cache (嚴格) |
828.20 ±4.49 |
66.84 ±0.29 |
現在,區域性性與利用率之間的權衡更加清晰。“cache”模式相比“system”模式顯示 2% 的頻寬損失,而“cache (嚴格)”模式則高達 20%。
結論與建議¶
在上述實驗中,“cache”親和範圍相對於“system”的效率優勢雖然一致且顯著,但相對較小。然而,其影響取決於範圍之間的距離,在拓撲結構更復雜的處理器中可能更明顯。
雖然在某些場景下工作守恆的損失令人不悅,但這比“cache (嚴格)”模式要好得多,而且無論如何,最大化工作佇列利用率不太可能是常見情況。因此,“cache”是非繫結池的預設親和範圍。
由於沒有一種選項適用於大多數情況,建議可能消耗大量 CPU 的工作佇列使用者使用
apply_workqueue_attrs()配置工作佇列和/或啟用WQ_SYSFS。具有嚴格“cpu”親和範圍的非繫結工作佇列的行為與
WQ_CPU_INTENSIVE每 CPU 工作佇列相同。後者並沒有真正的優勢,而非繫結工作佇列提供了更多的靈活性。親和範圍在 Linux v6.5 中引入。要模擬之前的行為,請使用嚴格的“numa”親和範圍。
非嚴格親和範圍中工作守恆的損失可能源於排程器。從理論上講,核心沒有理由不能做正確的事情並在大多數情況下保持工作守恆。因此,未來的排程器改進可能會使大多數這些可調引數變得不必要。
檢查配置¶
使用 tools/workqueue/wq_dump.py 來檢查非繫結 CPU 親和配置、工作執行緒池以及工作佇列如何對映到這些池
$ tools/workqueue/wq_dump.py
Affinity Scopes
===============
wq_unbound_cpumask=0000000f
CPU
nr_pods 4
pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008
pod_node [0]=0 [1]=0 [2]=1 [3]=1
cpu_pod [0]=0 [1]=1 [2]=2 [3]=3
SMT
nr_pods 4
pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008
pod_node [0]=0 [1]=0 [2]=1 [3]=1
cpu_pod [0]=0 [1]=1 [2]=2 [3]=3
CACHE (default)
nr_pods 2
pod_cpus [0]=00000003 [1]=0000000c
pod_node [0]=0 [1]=1
cpu_pod [0]=0 [1]=0 [2]=1 [3]=1
NUMA
nr_pods 2
pod_cpus [0]=00000003 [1]=0000000c
pod_node [0]=0 [1]=1
cpu_pod [0]=0 [1]=0 [2]=1 [3]=1
SYSTEM
nr_pods 1
pod_cpus [0]=0000000f
pod_node [0]=-1
cpu_pod [0]=0 [1]=0 [2]=0 [3]=0
Worker Pools
============
pool[00] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 0
pool[01] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 0
pool[02] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 1
pool[03] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 1
pool[04] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 2
pool[05] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 2
pool[06] ref= 1 nice= 0 idle/workers= 3/ 3 cpu= 3
pool[07] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 3
pool[08] ref=42 nice= 0 idle/workers= 6/ 6 cpus=0000000f
pool[09] ref=28 nice= 0 idle/workers= 3/ 3 cpus=00000003
pool[10] ref=28 nice= 0 idle/workers= 17/ 17 cpus=0000000c
pool[11] ref= 1 nice=-20 idle/workers= 1/ 1 cpus=0000000f
pool[12] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=00000003
pool[13] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=0000000c
Workqueue CPU -> pool
=====================
[ workqueue \ CPU 0 1 2 3 dfl]
events percpu 0 2 4 6
events_highpri percpu 1 3 5 7
events_long percpu 0 2 4 6
events_unbound unbound 9 9 10 10 8
events_freezable percpu 0 2 4 6
events_power_efficient percpu 0 2 4 6
events_freezable_pwr_ef percpu 0 2 4 6
rcu_gp percpu 0 2 4 6
rcu_par_gp percpu 0 2 4 6
slub_flushwq percpu 0 2 4 6
netns ordered 8 8 8 8 8
...
有關更多資訊,請參閱命令的幫助資訊。
監控¶
使用 tools/workqueue/wq_monitor.py 監控工作佇列操作
$ tools/workqueue/wq_monitor.py events
total infl CPUtime CPUhog CMW/RPR mayday rescued
events 18545 0 6.1 0 5 - -
events_highpri 8 0 0.0 0 0 - -
events_long 3 0 0.0 0 0 - -
events_unbound 38306 0 0.1 - 7 - -
events_freezable 0 0 0.0 0 0 - -
events_power_efficient 29598 0 0.2 0 0 - -
events_freezable_pwr_ef 10 0 0.0 0 0 - -
sock_diag_events 0 0 0.0 0 0 - -
total infl CPUtime CPUhog CMW/RPR mayday rescued
events 18548 0 6.1 0 5 - -
events_highpri 8 0 0.0 0 0 - -
events_long 3 0 0.0 0 0 - -
events_unbound 38322 0 0.1 - 7 - -
events_freezable 0 0 0.0 0 0 - -
events_power_efficient 29603 0 0.2 0 0 - -
events_freezable_pwr_ef 10 0 0.0 0 0 - -
sock_diag_events 0 0 0.0 0 0 - -
...
有關更多資訊,請參閱命令的幫助資訊。
除錯¶
由於工作函式由通用工作執行緒執行,因此需要一些技巧來揭示行為不當的工作佇列使用者。
工作執行緒在程序列表中顯示為
root 5671 0.0 0.0 0 0 ? S 12:07 0:00 [kworker/0:1]
root 5672 0.0 0.0 0 0 ? S 12:07 0:00 [kworker/1:2]
root 5673 0.0 0.0 0 0 ? S 12:12 0:00 [kworker/0:0]
root 5674 0.0 0.0 0 0 ? S 12:13 0:00 [kworker/1:0]
如果 kworker 表現異常(使用過多 CPU),可能存在兩種型別的問題
某物被快速連續排程
一個消耗大量 CPU 週期的單個工作項
第一個問題可以使用跟蹤來追蹤
$ echo workqueue:workqueue_queue_work > /sys/kernel/tracing/set_event
$ cat /sys/kernel/tracing/trace_pipe > out.txt
(wait a few secs)
^C
如果某個程序在工作排隊上忙迴圈,它將主導輸出,並且可以透過工作項函式確定肇事者。
對於第二類問題,應該只需檢查有問題的¹工作執行緒的堆疊跟蹤即可。
$ cat /proc/THE_OFFENDING_KWORKER/stack
工作項的函式應該在堆疊跟蹤中顯而易見。
非重入條件¶
工作佇列保證,如果在工作項排隊後滿足以下條件,則工作項不能重入:
工作函式未被更改。
沒有人將該工作項排隊到另一個工作佇列。
工作項未被重新初始化。
換句話說,如果上述條件成立,則保證工作項在任何給定時間在系統範圍內最多由一個工作者執行。
請注意,在自身函式中重新排隊工作項(到同一佇列)不會破壞這些條件,因此這樣做是安全的。否則,在工作函式內部破壞這些條件時需要謹慎。
核心內聯文件參考¶
-
struct workqueue_attrs¶
一個用於工作佇列屬性的結構體。
定義:
struct workqueue_attrs {
int nice;
cpumask_var_t cpumask;
cpumask_var_t __pod_cpumask;
bool affn_strict;
enum wq_affn_scope affn_scope;
bool ordered;
};
成員
nicenice 級別
cpumask允許的 CPU
此工作佇列中的工作項親和於這些 CPU,不允許在其他 CPU 上執行。服務於工作佇列的池必須具有相同的 cpumask。
__pod_cpumask用於建立每 pod 池的內部屬性
僅限內部使用。
每 pod 非繫結工作執行緒池用於提高區域性性。它始終是 ->cpumask 的一個子集。一個工作佇列可以與多個具有不相交 __pod_cpumask 的工作執行緒池關聯。池的 __pod_cpumask 的強制執行是否嚴格取決於 affn_strict。
affn_strict親和範圍嚴格
如果清除,工作佇列將盡力在 __pod_cpumask 內部啟動工作者,但排程器可以自由地將其遷移到外部。
如果設定,工作者只允許在 __pod_cpumask 內部執行。
affn_scope非繫結 CPU 親和範圍
CPU pod 用於改善非繫結工作項的執行區域性性。存在多種 pod 型別,每種 wq_affn_scope 對應一種,系統中每個 CPU 都屬於每種 pod 型別中的一個 pod。屬於同一 pod 的 CPU 共享工作執行緒池。例如,選擇
WQ_AFFN_NUMA會使工作佇列為每個 NUMA 節點使用單獨的工作執行緒池。ordered(有序)工作項必須按排隊順序逐個執行
描述
這可以用來改變非繫結工作佇列的屬性。
-
work_pending¶
work_pending (work)
查詢工作項是否當前掛起
引數
work相關的工作項
-
delayed_work_pending¶
delayed_work_pending (w)
查詢可延遲工作項是否當前掛起
引數
w相關的工作項
-
struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, int max_active, ...)¶
分配一個工作佇列
引數
const char *fmt工作佇列名稱的 printf 格式
unsigned int flagsWQ_* 標誌
int max_active最大在途工作項數,0 為預設值
...fmt 的引數
描述
對於每 CPU 工作佇列,max_active 限制了每個 CPU 的在途工作項數量。例如,max_active 為 1 表示每個 CPU 最多可以執行一個工作佇列的工作項。
對於非繫結工作佇列,max_active 限制了整個系統的在途工作項數量。例如,max_active 為 16 表示整個系統最多可以有 16 個工作佇列的工作項正在執行。
由於在多個 NUMA 節點之間共享非繫結工作佇列的相同活躍計數器可能開銷很大,因此 max_active 會根據線上 CPU 數量的比例分配給每個 NUMA 節點,並獨立執行。
根據線上 CPU 分佈,一個節點的每節點 max_active 可能會顯著低於 max_active,如果每節點併發限制低於工作佇列的最大相互依賴工作項數量,這可能導致死鎖。
為了無論線上 CPU 分佈如何都能保證前進,每個節點的併發限制保證等於或大於 min_active,min_active 設定為 min(max_active, WQ_DFL_MIN_ACTIVE)。這意味著每個節點的 max_active 總和可能大於 max_active。
有關 WQ_* 標誌的詳細資訊,請參考工作佇列。
返回
成功時返回指向已分配工作佇列的指標,失敗時返回 NULL。
-
struct workqueue_struct *alloc_workqueue_lockdep_map(const char *fmt, unsigned int flags, int max_active, struct lockdep_map *lockdep_map, ...)¶
分配一個帶使用者定義 lockdep_map 的工作佇列
引數
const char *fmt工作佇列名稱的 printf 格式
unsigned int flagsWQ_* 標誌
int max_active最大在途工作項數,0 為預設值
struct lockdep_map *lockdep_map使用者定義的 lockdep_map
...fmt 的引數
描述
與 alloc_workqueue 相同,但使用使用者定義的 lockdep_map。對於為相同目的建立的工作佇列很有用,並且可以避免在每次建立工作佇列時洩露 lockdep_map。
返回
成功時返回指向已分配工作佇列的指標,失敗時返回 NULL。
-
alloc_ordered_workqueue_lockdep_map¶
alloc_ordered_workqueue_lockdep_map (fmt, flags, lockdep_map, args...)
分配一個帶使用者定義 lockdep_map 的有序工作佇列
引數
fmt工作佇列名稱的 printf 格式
flagsWQ_* 標誌(只有 WQ_FREEZABLE 和 WQ_MEM_RECLAIM 有意義)
lockdep_map使用者定義的 lockdep_map
args...fmt 的引數
描述
與 alloc_ordered_workqueue 相同,但使用使用者定義的 lockdep_map。對於為相同目的建立的工作佇列很有用,並且可以避免在每次建立工作佇列時洩露 lockdep_map。
返回
成功時返回指向已分配工作佇列的指標,失敗時返回 NULL。
-
alloc_ordered_workqueue¶
alloc_ordered_workqueue (fmt, flags, args...)
分配一個有序工作佇列
引數
fmt工作佇列名稱的 printf 格式
flagsWQ_* 標誌(只有 WQ_FREEZABLE 和 WQ_MEM_RECLAIM 有意義)
args...fmt 的引數
描述
分配一個有序工作佇列。有序工作佇列在任何給定時間最多執行一個工作項,並按排隊順序執行。它們實現為 max_active 為一的非繫結工作佇列。
返回
成功時返回指向已分配工作佇列的指標,失敗時返回 NULL。
-
bool queue_work(struct workqueue_struct *wq, struct work_struct *work)¶
在工作佇列中排隊工作
引數
struct workqueue_struct *wq要使用的工作佇列
struct work_struct *work要排隊的工作
描述
如果 work 已在佇列中,則返回 false,否則返回 true。
我們將工作排隊到提交它的 CPU 上,但如果該 CPU 死亡,它可以透過另一個 CPU 處理。
記憶體排序屬性:如果返回 true,則保證在程式順序中呼叫 queue_work() 之前的所有儲存,在該工作執行時對執行 work 的 CPU 可見,例如:
{ x 最初為 0 }
CPU0 CPU1
WRITE_ONCE(x, 1); [ work 正在執行 ] r0 = queue_work(wq, work); r1 = READ_ONCE(x);
禁止:r0 == true && r1 == 0
-
bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)¶
在延遲後將工作排隊到工作佇列
引數
struct workqueue_struct *wq要使用的工作佇列
struct delayed_work *dwork要排隊的可延遲工作
unsigned long delay排隊前等待的 jiffies 數量
描述
相當於 queue_delayed_work_on(),但嘗試使用本地 CPU。
-
bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)¶
修改延遲或排隊一個延遲工作
引數
struct workqueue_struct *wq要使用的工作佇列
struct delayed_work *dwork要排隊的工作
unsigned long delay排隊前等待的 jiffies 數量
描述
在本地 CPU 上執行 mod_delayed_work_on()。
-
bool schedule_work_on(int cpu, struct work_struct *work)¶
將工作任務放置到特定 CPU
引數
int cpu放置工作任務的 CPU
struct work_struct *work待完成的工作
描述
這將一個任務放置到特定的 CPU 上
-
bool schedule_work(struct work_struct *work)¶
將工作任務放入全域性工作佇列
引數
struct work_struct *work待完成的工作
描述
如果 work 已在核心全域性工作佇列中,則返回 false,否則返回 true。
如果任務尚未排隊,則將其放入核心全域性工作佇列;否則,將其保留在核心全域性工作佇列中的相同位置。
與 queue_work() 共享相同的記憶體排序屬性,參見 queue_work() 的 DocBook 頭。
-
bool enable_and_queue_work(struct workqueue_struct *wq, struct work_struct *work)¶
在指定工作佇列上啟用並排隊一個工作項
引數
struct workqueue_struct *wq目標工作佇列
struct work_struct *work要啟用和排隊的工作項
描述
此函式結合了 enable_work() 和 queue_work() 的操作,提供了一種方便的方法,可以在單個呼叫中啟用和排隊工作項。它對 work 呼叫 enable_work(),如果停用深度達到 0,則將其排隊。如果停用深度達到 0 且 work 已排隊,則返回 true,否則返回 false。
請注意,當停用深度達到零時,work 總是會被排隊。如果期望的行為是僅當 work 被停用期間發生了某些事件時才排隊,使用者應實現必要的狀態跟蹤並在 enable_work() 後執行明確的條件排隊。
-
bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay)¶
在延遲後將工作排隊到 CPU 上的全域性工作佇列
引數
int cpu要使用的 CPU
struct delayed_work *dwork待完成的工作
unsigned long delay等待的 jiffies 數量
描述
在等待給定時間後,這會將一個任務放入指定 CPU 上的核心全域性工作佇列。
-
bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)¶
在延遲後將工作任務放入全域性工作佇列
引數
struct delayed_work *dwork待完成的工作
unsigned long delay等待的 jiffies 數量,或 0 表示立即執行
描述
在等待給定時間後,這會將一個任務放入核心全域性工作佇列。
-
for_each_pool¶
for_each_pool (pool, pi)
遍歷系統中所有 worker_pool
引數
pool迭代遊標
pi用於迭代的整數
描述
這必須在持有 wq_pool_mutex 或 RCU 讀鎖定下呼叫。如果池需要在當前鎖定失效後繼續使用,呼叫者有責任保證該池保持線上。
if/else 子句僅用於 lockdep 斷言,可以忽略。
-
for_each_pool_worker¶
for_each_pool_worker (worker, pool)
遍歷 worker_pool 的所有工作者
引數
worker迭代遊標
pool要迭代其工作者的 worker_pool
描述
這必須在持有 wq_pool_attach_mutex 的情況下呼叫。
if/else 子句僅用於 lockdep 斷言,可以忽略。
-
for_each_pwq¶
for_each_pwq (pwq, wq)
遍歷指定工作佇列的所有 pool_workqueue
引數
pwq迭代遊標
wq目標工作佇列
描述
這必須在持有 wq->mutex 或 RCU 讀鎖定下呼叫。如果 pwq 需要在當前鎖定失效後繼續使用,呼叫者有責任保證該 pwq 保持線上。
if/else 子句僅用於 lockdep 斷言,可以忽略。
-
int worker_pool_assign_id(struct worker_pool *pool)¶
分配 ID 並將其分配給 pool
引數
struct worker_pool *pool相關池的指標
描述
如果在 [0, WORK_OFFQ_POOL_NONE) 範圍內的 ID 被成功分配和賦值,則返回 0,失敗時返回 -errno。
-
struct cpumask *unbound_effective_cpumask(struct workqueue_struct *wq)¶
非繫結工作佇列的有效 cpumask
引數
struct workqueue_struct *wq相關的工作佇列
描述
wq->unbound_attrs->cpumask 包含使用者請求的 cpumask,該 cpumask 與 wq_unbound_cpumask 進行掩碼操作以確定有效的 cpumask。預設的 pwq 總是對映到具有當前有效 cpumask 的池。
-
struct worker_pool *get_work_pool(struct work_struct *work)¶
返回給定工作項所關聯的 worker_pool
引數
struct work_struct *work相關的工作項
描述
池在 wq_pool_mutex 下建立和銷燬,並允許在 RCU 讀鎖定下進行讀訪問。因此,此函式應在 wq_pool_mutex 下或 rcu_read_lock() 區域內呼叫。
只要上述鎖定有效,返回池的所有欄位均可訪問。如果返回的池需要在臨界區之外使用,呼叫者有責任確保返回的池線上並保持線上。
返回
work 最後關聯的 worker_pool。如果沒有,則為 NULL。
引數
struct worker *worker自身
unsigned int flags要設定的標誌
描述
在 worker->flags 中設定 flags 並相應調整 nr_running。
引數
struct worker *worker自身
unsigned int flags要清除的標誌
描述
在 worker->flags 中清除 flags 並相應調整 nr_running。
引數
struct worker *worker正在進入空閒狀態的工作者
描述
worker 正在進入空閒狀態。如有必要,更新統計資訊和空閒定時器。
鎖定:raw_spin_lock_irq(pool->lock)。
引數
struct worker *worker正在離開空閒狀態的工作者
描述
worker 正在離開空閒狀態。更新統計資訊。
鎖定:raw_spin_lock_irq(pool->lock)。
-
struct worker *find_worker_executing_work(struct worker_pool *pool, struct work_struct *work)¶
查詢正在執行某個工作的工作者
引數
struct worker_pool *pool相關池
struct work_struct *work要查詢工作者對應的工作
描述
透過搜尋以 work 地址為鍵的 pool->busy_hash 來查詢正在 pool 上執行 work 的工作者。為了匹配,工作者的當前執行應與 work 的地址及其工作函式匹配。這是為了避免透過在工作項仍在執行時被回收而導致不相關工作執行之間產生不必要的依賴。
這有點棘手。工作項一旦開始執行就可能被釋放,並且沒有任何東西可以阻止被釋放的區域被回收用於另一個工作項。如果同一工作項地址在原始執行完成之前被重用,工作佇列會將回收的工作項識別為當前正在執行的工作項,並使其等待直到當前執行完成,從而引入不必要的依賴。
此函式檢查工作項地址和工作函式以避免誤報。請注意,這並不完全,因為有人可能會構造一個工作函式,透過回收的工作項引入對自身的依賴。好吧,如果有人執意要自找麻煩,我們能做的也有限,而且如果確實發生了這種死鎖,應該很容易找到罪魁禍禍的工作函式。
上下文
raw_spin_lock_irq(pool->lock)。
返回
如果找到,則指向正在執行 work 的工作者的指標,否則為 NULL。
-
void move_linked_works(struct work_struct *work, struct list_head *head, struct work_struct **nextp)¶
將關聯工作項移動到列表
引數
struct work_struct *work要排程的系列工作項的起始
struct list_head *head要將 work 追加到的目標列表
struct work_struct **nextp用於巢狀工作列表遍歷的輸出引數
描述
將從 work 開始的關聯工作項排程到 head。要排程的系列工作項從 work 開始,幷包含其前身設定了 WORK_STRUCT_LINKED 標誌的任何連續工作項。有關 nextp 的詳細資訊,請參閱 assign_work()。
上下文
raw_spin_lock_irq(pool->lock)。
-
bool assign_work(struct work_struct *work, struct worker *worker, struct work_struct **nextp)¶
將工作項及其關聯的工作項分配給一個工作者
引數
struct work_struct *work要分配的工作項
struct worker *worker要分配到的工作者
struct work_struct **nextp用於巢狀工作列表遍歷的輸出引數
描述
將 work 及其關聯的工作項分配給 worker。如果 work 已經在同一工作池中的另一個工作者執行,它將被轉移到那裡。
如果 nextp 不為 NULL,則更新它以指向上次排程工作的下一個工作項。這允許 assign_work() 巢狀在 list_for_each_entry_safe() 中。
如果 work 成功分配給 worker,則返回 true。如果 work 被轉移到已經執行它的另一個工作者,則返回 false。
-
bool kick_pool(struct worker_pool *pool)¶
如有必要,喚醒一個空閒工作者
引數
struct worker_pool *pool要踢動的工作池
描述
pool 可能有待處理的工作項。如有必要,喚醒工作者。返回是否喚醒了工作者。
-
void wq_worker_running(struct task_struct *task)¶
一個工作者再次執行
引數
struct task_struct *task正在喚醒的任務
描述
當一個工作者從 schedule() 返回時呼叫此函式
-
void wq_worker_sleeping(struct task_struct *task)¶
一個工作者即將進入睡眠
引數
struct task_struct *task即將進入睡眠的任務
描述
當一個繁忙的工作者即將進入睡眠時,此函式從 schedule() 呼叫。
-
void wq_worker_tick(struct task_struct *task)¶
當一個 kworker 執行時發生了排程器時鐘中斷
引數
struct task_struct *task當前正在執行的任務
描述
從 sched_tick() 呼叫。我們處於 IRQ 上下文,並且可以安全地訪問遵循“K”鎖定規則的當前工作者的欄位。
-
work_func_t wq_worker_last_func(struct task_struct *task)¶
檢索工作者的最後一個工作函式
引數
struct task_struct *task要檢索其最後一個工作函式的任務。
描述
確定工作者執行的最後一個函式。此函式從排程器呼叫,以獲取工作者最後已知身份。
當一個 kworker 即將進入睡眠時,此函式在 schedule() 期間呼叫。它被 psi 用於在出隊期間識別聚合工作者,以便當該工作者是系統或 cgroup 中最後一個進入睡眠的任務時,允許週期性聚合關閉。
由於此函式不涉及任何工作佇列相關的鎖定,因此只有在從排程器的入隊和出隊路徑內部呼叫時,它才返回穩定值,此時 task(必須是 kworker)保證沒有處理任何工作。
上下文
raw_spin_lock_irq(rq->lock)
返回
作為工作者執行的最後一個工作函式 current,如果尚未執行任何工作,則為 NULL。
-
struct wq_node_nr_active *wq_node_nr_active(struct workqueue_struct *wq, int node)¶
確定要使用的 wq_node_nr_active
引數
struct workqueue_struct *wq相關的工作佇列
int nodeNUMA 節點,可以是
NUMA_NO_NODE
描述
確定用於 node 上 wq 的 wq_node_nr_active。返回
對於每 CPU 工作佇列返回
NULL,因為它們不需要使用共享的 nr_active。如果 node 是
NUMA_NO_NODE,則為 node_nr_active[nr_node_ids]。否則,為 node_nr_active[node]。
-
void wq_update_node_max_active(struct workqueue_struct *wq, int off_cpu)¶
更新要使用的每節點 max_actives
引數
struct workqueue_struct *wq要更新的工作佇列
int off_cpu即將關閉的 CPU,如果 CPU 沒有關閉則為 -1
描述
更新 wq->node_nr_active**[]->max。wq 必須是未繫結的。max_active 根據線上 CPU 數量的比例分佈在各個節點之間。結果總是在 wq->min_active 和 max_active 之間。
-
void get_pwq(struct pool_workqueue *pwq)¶
獲取指定 pool_workqueue 的額外引用
引數
struct pool_workqueue *pwq要獲取的 pool_workqueue
描述
獲取 pwq 的額外引用。呼叫者應保證 pwq 具有正的引用計數 (refcnt) 並持有匹配的 pool->lock。
-
void put_pwq(struct pool_workqueue *pwq)¶
釋放一個 pool_workqueue 引用
引數
struct pool_workqueue *pwq要釋放的 pool_workqueue
描述
釋放 pwq 的一個引用。如果其引用計數 (refcnt) 達到零,則安排其銷燬。呼叫者應持有匹配的 pool->lock。
-
bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill)¶
嘗試遞增 pwq 的 nr_active
引數
struct pool_workqueue *pwq關注的 pool_workqueue
bool fillmax_active 可能已增加,嘗試提高併發級別
描述
嘗試遞增 pwq 的 nr_active。如果成功獲取 nr_active 計數,則返回 true。否則返回 false。
-
bool pwq_activate_first_inactive(struct pool_workqueue *pwq, bool fill)¶
啟用 pwq 上的第一個非活動工作項
引數
struct pool_workqueue *pwq關注的 pool_workqueue
bool fillmax_active 可能已增加,嘗試提高併發級別
描述
如果可用且允許由 max_active 限制,則啟用 pwq 的第一個非活動工作項。
如果已啟用非活動工作項,則返回 true。如果未找到非活動工作項或達到 max_active 限制,則返回 false。
-
void unplug_oldest_pwq(struct workqueue_struct *wq)¶
拔出最舊的 pool_workqueue
引數
struct workqueue_struct *wq要拔出其最舊 pwq 的 workqueue_struct
描述
此函式僅應為有序工作佇列呼叫,其中只有最舊的 pwq 被拔出,其他 pwq 被插入以暫停執行,從而確保正確的工作項順序。
dfl_pwq --------------+ [P] - plugged
|
v
pwqs -> A -> B [P] -> C [P] (newest)
| | |
1 3 5
| | |
2 4 6
當最舊的 pwq 被耗盡並移除時,應呼叫此函式以拔出下一個最舊的 pwq,以開始其工作項執行。請注意,pwq 按照從舊到新的順序連結到 wq->pwqs 中,因此列表中的第一個是最舊的。
-
void node_activate_pending_pwq(struct wq_node_nr_active *nna, struct worker_pool *caller_pool)¶
啟用 wq_node_nr_active 上的待處理 pwq
引數
struct wq_node_nr_active *nna要為其啟用待處理 pwq 的 wq_node_nr_active
struct worker_pool *caller_pool呼叫者正在鎖定的 worker_pool
描述
啟用 nna->pending_pwqs 中的一個 pwq。在 caller_pool 已鎖定的情況下呼叫。caller_pool 可能會被解鎖並重新鎖定以鎖定其他 worker_pool。
-
void pwq_dec_nr_active(struct pool_workqueue *pwq)¶
解除一個活躍計數
引數
struct pool_workqueue *pwq關注的 pool_workqueue
描述
遞減 pwq 的 nr_active 並嘗試啟用第一個非活動工作項。對於非繫結工作佇列,此函式可能會暫時釋放 pwq->pool->lock。
-
void pwq_dec_nr_in_flight(struct pool_workqueue *pwq, unsigned long work_data)¶
遞減 pwq 的 nr_in_flight
引數
struct pool_workqueue *pwq關注的 pwq
unsigned long work_data已離開佇列的工作項的 work_data
描述
工作項已完成或已從待處理佇列中移除,遞減其 pwq 的 nr_in_flight 並處理工作佇列重新整理。
注意
對於非繫結工作佇列,此函式可能會暫時釋放 pwq->pool->lock,因此應在所有其他飛行中工作項的狀態更新完成後呼叫。
上下文
raw_spin_lock_irq(pool->lock)。
-
int try_to_grab_pending(struct work_struct *work, u32 cflags, unsigned long *irq_flags)¶
從工作列表中竊取工作項並停用中斷
引數
struct work_struct *work要竊取的工作項
u32 cflagsWORK_CANCEL_標誌unsigned long *irq_flags儲存中斷狀態的位置
描述
嘗試獲取 work 的 PENDING 位。此函式可以處理處於任何穩定狀態的 work - 空閒、在計時器上或在工作列表中。
成功返回時,如果 >= 0,則中斷被停用,呼叫者負責使用 local_irq_restore(*irq_flags) 釋放它。
此函式可以從包括 IRQ 處理程式在內的任何上下文安全呼叫。
返回
1
如果 work 待處理並且我們成功竊取了 PENDING
0
如果 work 空閒並且我們聲明瞭 PENDING
-EAGAIN
如果 PENDING 當前無法獲取,則可安全地忙重試
注意
在 >= 0 返回時,呼叫者擁有 work 的 PENDING 位。為避免在持有 PENDING 且 work 不在佇列中時被中斷,入口處必須停用中斷。這與 delayed_work->timer 是中斷安全的結合,確保我們在有限的短時間內返回 -EAGAIN。
-
bool work_grab_pending(struct work_struct *work, u32 cflags, unsigned long *irq_flags)¶
從工作列表中竊取工作項並停用中斷
引數
struct work_struct *work要竊取的工作項
u32 cflagsWORK_CANCEL_標誌unsigned long *irq_flags儲存 IRQ 狀態的位置
描述
獲取 work 的 PENDING 位。work 可以處於任何穩定狀態 - 空閒、在計時器上或在工作列表中。
可以從任何上下文呼叫。返回時中斷被停用,IRQ 狀態儲存在 *irq_flags 中。呼叫者負責使用 local_irq_restore() 重新啟用它。
如果 work 待處理,則返回 true。如果空閒,則返回 false。
-
void insert_work(struct pool_workqueue *pwq, struct work_struct *work, struct list_head *head, unsigned int extra_flags)¶
將工作項插入到工作池中
引數
struct pool_workqueue *pwqwork 所屬的 pwq
struct work_struct *work要插入的工作項
struct list_head *head插入點
unsigned int extra_flags要設定的額外 WORK_STRUCT_* 標誌
描述
將屬於 pwq 的 work 插入到 head 之後。extra_flags 將與 work_struct 標誌進行或運算。
上下文
raw_spin_lock_irq(pool->lock)。
-
bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work)¶
將工作排隊到特定 CPU
引數
int cpu執行工作的 CPU 編號
struct workqueue_struct *wq要使用的工作佇列
struct work_struct *work要排隊的工作
描述
我們將工作排隊到特定 CPU,呼叫者必須確保它不會消失。未能確保指定 CPU 不會消失的呼叫者將在隨機選擇的 CPU 上執行。但請注意,指定從未上線過的 CPU 的呼叫者將遇到問題。
返回
如果 work 已在佇列中,則為 false;否則為 true。
-
int select_numa_node_cpu(int node)¶
根據 NUMA 節點選擇 CPU
引數
int node要從中選擇 CPU 的 NUMA 節點 ID
描述
此函式將嘗試在給定節點上查詢一個“隨機”可用 CPU。如果給定節點上沒有可用 CPU,它將返回 WORK_CPU_UNBOUND,表示如果我們需要排程此工作,則應將其排程到任何可用 CPU。
-
bool queue_work_node(int node, struct workqueue_struct *wq, struct work_struct *work)¶
將工作排隊到給定 NUMA 節點的“隨機”CPU 上
引數
int node我們要將工作定位到的 NUMA 節點
struct workqueue_struct *wq要使用的工作佇列
struct work_struct *work要排隊的工作
描述
我們將工作排隊到給定 NUMA 節點內的“隨機”CPU 上。這裡的基本思想是提供一種將工作與給定 NUMA 節點關聯起來的方法。
此函式只會盡力嘗試將其置於正確的 NUMA 節點上。如果沒有請求節點或請求的節點處於離線狀態,我們只會回退到標準的 queue_work 行為。
目前,“隨機”CPU 最終是 cpu_online_mask 和節點 cpumask 交集中的第一個可用 CPU,除非我們正在節點上執行。在這種情況下,我們只使用當前 CPU。
返回
如果 work 已在佇列中,則為 false;否則為 true。
-
bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)¶
在延遲後將工作排隊到特定 CPU
引數
int cpu執行工作的 CPU 編號
struct workqueue_struct *wq要使用的工作佇列
struct delayed_work *dwork要排隊的工作
unsigned long delay排隊前等待的 jiffies 數量
描述
我們將 delayed_work 排隊到特定 CPU,對於非零延遲,呼叫者必須確保它線上且不會消失。未能確保這一點的呼叫者,可能會導致 dwork->timer 排隊到離線 CPU,這將阻止 dwork->work 的排隊,除非離線 CPU 再次上線。
返回
如果 work 已在佇列中,則為 false;否則為 true。如果 delay 為零且 dwork 空閒,它將被安排立即執行。
-
bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)¶
修改特定 CPU 上的延遲工作項的延遲或將其排隊
引數
int cpu執行工作的 CPU 編號
struct workqueue_struct *wq要使用的工作佇列
struct delayed_work *dwork要排隊的工作
unsigned long delay排隊前等待的 jiffies 數量
描述
如果 dwork 空閒,則等同於 queue_delayed_work_on();否則,修改 dwork 的計時器,使其在 delay 後到期。如果 delay 為零,則無論其當前狀態如何,work 都保證立即排程。
此函式可以從包括 IRQ 處理程式在內的任何上下文安全呼叫。有關詳細資訊,請參閱 try_to_grab_pending()。
返回
如果 dwork 空閒並已排隊,則為 false;如果 dwork 待處理且其計時器已修改,則為 true。
-
bool queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork)¶
在 RCU 寬限期後排隊工作
引數
struct workqueue_struct *wq要使用的工作佇列
struct rcu_work *rwork要排隊的工作
返回
如果 rwork 已待處理,則為 false;否則為 true。請注意,只有在返回 true 後才保證完整的 RCU 寬限期。雖然在返回 false 後 rwork 保證執行,但執行可能發生在完整的 RCU 寬限期透過之前。
引數
struct worker *worker要附加的工作者
struct worker_pool *pool目標工作池
描述
將 worker 附加到 pool。一旦附加,worker 的 WORKER_UNBOUND 標誌和 CPU 繫結將在 CPU 熱插拔/拔出時與工作池保持協調。
引數
struct worker *worker已附加到其工作池的工作者
描述
撤銷在 worker_attach_to_pool() 中完成的附加操作。呼叫者工作者在分離後不應訪問工作池,除非它對工作池有其他引用。
-
struct worker *create_worker(struct worker_pool *pool)¶
建立一個新的工作佇列工作者
引數
struct worker_pool *pool新工作者將所屬的工作池
描述
建立並啟動一個附加到 pool 的新工作者。
上下文
可能休眠。執行 GFP_KERNEL 分配。
返回
指向新建立工作者的指標。
引數
struct worker *worker要銷燬的工作者
struct list_head *list將工作者從其 pool->idle_list 轉移到 list 中
描述
標記 worker 以進行銷燬,並相應調整 pool 統計資料。工作者應處於空閒狀態。
上下文
raw_spin_lock_irq(pool->lock)。
-
void idle_worker_timeout(struct timer_list *t)¶
檢查是否有空閒工作者現在可以刪除。
引數
struct timer_list *t剛剛到期的工作池的 idle_timer
描述
計時器在 worker_enter_idle() 中武裝。請注意,它不會在 worker_leave_idle() 中解除武裝,因為當工作者在空閒和活躍之間切換,而其工作池處於 too_many_workers() 臨界點時,會導致過多的計時器管理開銷。由於 IDLE_WORKER_TIMEOUT 足夠長,我們只是讓它過期並從那裡重新評估情況。
-
void idle_cull_fn(struct work_struct *work)¶
清除空閒時間過長的工作者。
引數
struct work_struct *work工作池處理這些空閒工作的工作項
描述
這會遍歷工作池的空閒工作者,並清除那些空閒時間至少為 IDLE_WORKER_TIMEOUT 秒的工作者。
我們不希望由於清除 pcpu kworker 而打擾隔離的 CPU,因此這也重置了工作者親和性。這需要一個可休眠的上下文,因此計時器回撥和工作項之間存在分離。
-
void maybe_create_worker(struct worker_pool *pool)¶
如有必要,建立一個新工作者
引數
struct worker_pool *pool要為其建立新工作者的工作池
描述
如有必要,為 pool 建立一個新工作者。此函式返回時,pool 保證至少有一個空閒工作者。如果建立新工作者所需時間超過 MAYDAY_INTERVAL,則會向所有在 pool 上安排了工作項的救援者傳送求救訊號,以解決可能的分配死鎖。
返回時,need_to_create_worker() 保證為 false,may_start_working() 保證為 true。
鎖定:raw_spin_lock_irq(pool->lock),它可能會被多次釋放和重新獲取。執行 GFP_KERNEL 分配。僅從管理器呼叫。
引數
struct worker *worker自身
描述
承擔管理器角色並管理 worker 所屬的工作者池。在任何給定時間,每個工作池只能有零個或一個管理器。此函式會自動處理排他性。
在返回 false 時,呼叫者可以安全地開始處理工作。在返回 true 時,保證 need_to_create_worker() 為 false 且 may_start_working() 為 true。
上下文
raw_spin_lock_irq(pool->lock),它可能會被多次釋放和重新獲取。執行 GFP_KERNEL 分配。
返回
如果工作池不需要管理且呼叫者可以安全地開始處理工作,則為 false;如果已執行管理功能,並且呼叫者在呼叫該函式之前驗證的條件可能不再為真,則為 true。
引數
struct worker *worker自身
struct work_struct *work要處理的工作項
描述
處理 work。此函式包含處理單個工作項所需的所有邏輯,包括與同一 CPU 上的其他工作者的同步和互動、排隊和重新整理。只要滿足上下文要求,任何工作者都可以呼叫此函式來處理工作。
上下文
raw_spin_lock_irq(pool->lock),它會被釋放並重新獲取。
引數
struct worker *worker自身
描述
處理所有已排程工作項。請注意,在處理工作時,已排程列表可能會更改,因此此函式會重複從頂部獲取工作並執行它。
上下文
raw_spin_lock_irq(pool->lock),它可能會被多次釋放和重新獲取。
-
int worker_thread(void *__worker)¶
工作者執行緒函式
引數
void *__worker自身
描述
工作者執行緒函式。所有工作者都屬於一個 worker_pool - 無論是每 CPU 工作池還是動態非繫結工作池。這些工作者處理所有工作項,無論其特定的目標工作佇列是什麼。唯一的例外是屬於帶有救援者(將在 rescuer_thread() 中解釋)的工作佇列的工作項。
返回
0
-
int rescuer_thread(void *__rescuer)¶
救援者執行緒函式
引數
void *__rescuer自身
描述
工作佇列救援者執行緒函式。對於每個設定了 WQ_MEM_RECLAIM 的工作佇列,都有一個救援者。
工作池上的常規工作處理可能會在嘗試建立新工作者時阻塞,該新工作者使用 GFP_KERNEL 分配,如果當前在同一佇列上的一些工作需要處理以滿足 GFP_KERNEL 分配,則存在發展為死鎖的微小可能性。這就是救援者解決的問題。
當這種情況可能發生時,工作池會召集所有在該工作池上排隊有工作項的工作佇列的救援者,讓他們處理這些工作,以保證前向進度。
這應該很少發生。
返回
0
-
void check_flush_dependency(struct workqueue_struct *target_wq, struct work_struct *target_work, bool from_cancel)¶
檢查重新整理依賴的健全性
引數
struct workqueue_struct *target_wq正在重新整理的工作佇列
struct work_struct *target_work正在重新整理的工作項(對於工作佇列重新整理為 NULL)
bool from_cancel我們是否從工作取消路徑呼叫
描述
current 正在嘗試重新整理整個 target_wq 或其上的 target_work。如果這不是取消路徑(這意味著正在重新整理的工作要麼已經在執行,要麼根本不會執行),請檢查 target_wq 是否沒有 WQ_MEM_RECLAIM,並驗證 current 沒有在回收記憶體或在沒有 WQ_MEM_RECLAIM 的工作佇列上執行,因為這可能破壞前向進度保證,導致死鎖。
-
void insert_wq_barrier(struct pool_workqueue *pwq, struct wq_barrier *barr, struct work_struct *target, struct worker *worker)¶
插入一個屏障工作項
引數
struct pool_workqueue *pwq要插入屏障的 pwq
struct wq_barrier *barr要插入的 wq_barrier
struct work_struct *target要將 barr 附加到的目標工作項
struct worker *worker當前正在執行 target 的工作者,如果 target 未在執行則為 NULL
描述
barr 與 target 連結,使得 barr 僅在 target 完成執行後才完成。請注意,順序保證僅在相對於 target 和在本地 CPU 上觀察到。
目前,已排隊的屏障無法取消。這是因為 try_to_grab_pending() 無法確定要獲取的工作是否在佇列的頭部,因此無法清除前一個工作的 LINKED 標誌,而設定了 LINKED 標誌的工作之後必須有有效的下一個工作。
請注意,當 worker 非 NULL 時,target 可能會在我們下方被修改,因此我們無法可靠地從 target 確定 pwq。
上下文
raw_spin_lock_irq(pool->lock)。
-
bool flush_workqueue_prep_pwqs(struct workqueue_struct *wq, int flush_color, int work_color)¶
準備 pwq 以進行工作佇列重新整理
引數
struct workqueue_struct *wq正在重新整理的工作佇列
int flush_color新的重新整理顏色,< 0 表示無操作
int work_color新的工作顏色,< 0 表示無操作
描述
準備 pwq 以進行工作佇列重新整理。
如果 flush_color 非負,則所有 pwq 上的 flush_color 應為 -1。如果沒有任何 pwq 在指定顏色下有進行中的命令,則所有 pwq->flush_color 保持為 -1 並返回 false。如果任何 pwq 有進行中的命令,其 pwq->flush_color 將設定為 flush_color,wq->nr_pwqs_to_flush 將相應更新,pwq 喚醒邏輯將被武裝並返回 true。
呼叫者在呼叫此函式並使用非負 flush_color 之前,應已初始化 wq->first_flusher。如果 flush_color 為負,則不執行重新整理顏色更新並返回 false。
如果 work_color 非負,則所有 pwq 都應具有與 work_color 之前的相同的 work_color,並且所有 pwq 都將前進到 work_color。
上下文
mutex_lock(wq->mutex)。
返回
如果 flush_color >= 0 且有東西要重新整理,則為 true。否則為 false。
-
void __flush_workqueue(struct workqueue_struct *wq)¶
確保所有已排程工作都已執行完成。
引數
struct workqueue_struct *wq要重新整理的工作佇列
描述
此函式會休眠,直到所有在進入時已排隊的工作項都已完成執行,但不會被新的傳入工作項導致活鎖。
-
void drain_workqueue(struct workqueue_struct *wq)¶
耗盡一個工作佇列
引數
struct workqueue_struct *wq要耗盡的工作佇列
描述
等待直到工作佇列變空。在耗盡過程中,只允許鏈式排隊。換句話說,只有當前在 wq 上待處理或執行的工作項才能在其上排隊更多工作項。wq 被重複重新整理直到變空。重新整理的次數由鏈的深度決定,應該相對較短。如果花費太長時間則會發出警告。
-
bool flush_work(struct work_struct *work)¶
等待工作項完成執行最後一個排隊例項
引數
struct work_struct *work要重新整理的工作項
描述
等待直到 work 完成執行。如果 work 自重新整理開始以來未被重新排隊,則返回時保證其處於空閒狀態。
返回
如果 flush_work() 等待工作完成執行,則為 true;如果它已空閒,則為 false。
-
bool flush_delayed_work(struct delayed_work *dwork)¶
等待 dwork 完成執行最後一個排隊
引數
struct delayed_work *dwork要重新整理的延遲工作項
描述
延遲計時器被取消,待處理工作被排隊立即執行。與 flush_work() 類似,此函式僅考慮 dwork 的最後一個排隊例項。
返回
如果 flush_work() 等待工作完成執行,則為 true;如果它已空閒,則為 false。
-
bool flush_rcu_work(struct rcu_work *rwork)¶
等待 rwork 完成執行最後一個排隊
-
bool cancel_work_sync(struct work_struct *work)¶
取消工作項並等待其完成
引數
struct work_struct *work要取消的工作項
描述
取消 work 並等待其執行完成。即使工作項重新排隊或遷移到另一個工作佇列,此函式也可以使用。從此函式返回時,只要沒有競爭性入隊操作,work 保證不在任何 CPU 上待處理或執行。
cancel_work_sync(delayed_work->work) 不得用於 delayed_work。請改用 cancel_delayed_work_sync()。
如果 work 最後排隊在非 BH 工作佇列上,則必須從可休眠上下文呼叫。如果 work 最後排隊在 BH 工作佇列上,也可以從包括 BH 在內的非硬中斷原子上下文呼叫。
如果 work 待處理,則返回 true;否則為 false。
-
bool cancel_delayed_work(struct delayed_work *dwork)¶
取消延遲工作項
引數
struct delayed_work *dwork要取消的延遲工作項
描述
終止一個待處理的 delayed_work。
此函式可以從包括 IRQ 處理程式在內的任何上下文安全呼叫。
返回
如果 dwork 待處理並已取消,則為 true;如果它沒有待處理,則為 false。
注意
返回時工作回撥函式可能仍在執行,除非它返回 true 且工作不重新武裝自身。顯式重新整理或使用 cancel_delayed_work_sync() 來等待它。
-
bool cancel_delayed_work_sync(struct delayed_work *dwork)¶
取消延遲工作項並等待其完成
引數
struct delayed_work *dwork要取消的延遲工作項
描述
這是針對延遲工作項的 cancel_work_sync()。
返回
如果 dwork 待處理,則為 true;否則為 false。
-
bool disable_work(struct work_struct *work)¶
停用並取消工作項
引數
struct work_struct *work要停用的工作項
描述
透過遞增 work 的停用計數來停用它,如果當前待處理則取消它。只要停用計數非零,任何排隊 work 的嘗試都將失敗並返回 false。支援的最大停用深度是 2 的 WORK_OFFQ_DISABLE_BITS 次冪,目前為 65536。
可以從任何上下文呼叫。如果 work 待處理,則返回 true;否則為 false。
-
bool disable_work_sync(struct work_struct *work)¶
停用、取消並耗盡工作項
引數
struct work_struct *work要停用的工作項
描述
類似於 disable_work(),但如果 work 當前正在執行,也會等待其完成。
如果 work 最後排隊在非 BH 工作佇列上,則必須從可休眠上下文呼叫。如果 work 最後排隊在 BH 工作佇列上,也可以從包括 BH 在內的非硬中斷原子上下文呼叫。
如果 work 待處理,則返回 true;否則為 false。
-
bool enable_work(struct work_struct *work)¶
啟用工作項
引數
struct work_struct *work要啟用的工作項
描述
透過遞減 work 的停用計數來撤銷 disable_work[_sync]()。只有當 work 的停用計數為 0 時才能排隊。
可以從任何上下文呼叫。如果停用計數達到 0,則返回 true。否則為 false。
-
bool disable_delayed_work(struct delayed_work *dwork)¶
停用並取消延遲工作項
-
bool disable_delayed_work_sync(struct delayed_work *dwork)¶
停用、取消並耗盡延遲工作項
-
bool enable_delayed_work(struct delayed_work *dwork)¶
啟用延遲工作項
-
int schedule_on_each_cpu(work_func_t func)¶
在每個線上 CPU 上同步執行函式
引數
work_func_t func要呼叫的函式
描述
schedule_on_each_cpu() 使用系統工作佇列在每個線上 CPU 上執行 func,並阻塞直到所有 CPU 完成。 schedule_on_each_cpu() 非常慢。
返回
成功時返回 0,失敗時返回 -errno。
-
int execute_in_process_context(work_func_t fn, struct execute_work *ew)¶
在使用者上下文中可靠地執行例程
引數
work_func_t fn要執行的函式
struct execute_work *ew執行工作結構體的保證儲存(工作執行時必須可用)
描述
如果程序上下文可用,則立即執行該函式;否則,將該函式排程為延遲執行。
返回
- 0 - 函式已執行
1 - 函式已排程執行
-
void free_workqueue_attrs(struct workqueue_attrs *attrs)¶
釋放一個工作佇列屬性
-
struct workqueue_attrs *alloc_workqueue_attrs(void)¶
分配一個工作佇列屬性
引數
void無引數
描述
分配一個新的工作佇列屬性,用預設設定初始化並返回。
返回
成功時返回分配的新工作佇列屬性。失敗時返回 NULL。
-
int init_worker_pool(struct worker_pool *pool)¶
初始化一個新分配並零初始化的工作者池
引數
struct worker_pool *pool要初始化的工作者池
描述
初始化一個新分配並零初始化的 pool。它還會分配 pool->attrs。
返回
成功時返回 0,失敗時返回 -errno。即使失敗,pool 內部的所有欄位也已初始化,並且可以安全地對 pool 呼叫 put_unbound_pool() 來釋放它。
-
void put_unbound_pool(struct worker_pool *pool)¶
釋放一個工作者池
引數
struct worker_pool *pool要釋放的工作者池
描述
釋放 pool。如果其引用計數(refcnt)達到零,它將以 RCU 安全的方式銷燬。get_unbound_pool() 在其失敗路徑上呼叫此函式,並且此函式應該能夠釋放已透過(無論成功與否)init_worker_pool() 的池。
應在持有 wq_pool_mutex 的情況下呼叫。
-
struct worker_pool *get_unbound_pool(const struct workqueue_attrs *attrs)¶
獲取具有指定屬性的工作者池
引數
const struct workqueue_attrs *attrs要獲取的工作者池的屬性
描述
獲取一個與 attrs 具有相同屬性的工作者池,增加引用計數並返回它。如果已存在匹配的工作者池,則使用現有池;否則,此函式嘗試建立一個新池。
應在持有 wq_pool_mutex 的情況下呼叫。
返回
成功時,返回與 attrs 具有相同屬性的工作者池。失敗時返回 NULL。
-
void wq_calc_pod_cpumask(struct workqueue_attrs *attrs, int cpu)¶
計算一個工作佇列屬性(wq_attrs)的 CPU 掩碼(cpumask),用於一個 pod
引數
struct workqueue_attrs *attrs目標工作佇列的預設 pwq 的 wq_attrs
int cpu目標 CPU
描述
計算具有 attrs 的工作佇列應在 pod 上使用的 CPU 掩碼。結果儲存在 attrs->__pod_cpumask 中。
如果未啟用 pod 親和性,則始終使用 attrs->cpumask。如果已啟用,且 pod 包含 attrs 所請求的線上 CPU,則返回的 CPU 掩碼是 pod 的可能 CPU 與 attrs->cpumask 的交集。
呼叫者負責確保 pod 的 CPU 掩碼保持穩定。
-
int apply_workqueue_attrs(struct workqueue_struct *wq, const struct workqueue_attrs *attrs)¶
將新的工作佇列屬性應用於無繫結工作佇列
引數
struct workqueue_struct *wq目標工作佇列
const struct workqueue_attrs *attrs要應用的工作佇列屬性,使用
alloc_workqueue_attrs()分配
描述
將 attrs 應用於無繫結工作佇列 wq。除非停用,此函式會將獨立的 pwq 對映到每個 CPU pod,其中包含 attrs->cpumask 中可能的 CPU,以便工作項與其被髮出的 pod 保持親和性。舊的 pwq 會在正在執行的工作項完成後被釋放。請注意,連續重新入隊的工作項將保留在其當前的 pwq 上。
執行 GFP_KERNEL 分配。
返回
成功時返回 0,失敗時返回 -errno。
-
void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)¶
為 CPU 熱插拔更新 pwq 插槽
引數
struct workqueue_struct *wq目標工作佇列
int cpu要更新 pwq 插槽的 CPU
描述
此函式應從 CPU_DOWN_PREPARE、CPU_ONLINE 和 CPU_DOWN_FAILED 中呼叫。cpu 位於正在熱插拔的 CPU 的同一個 pod 中。
如果由於記憶體分配失敗而無法調整 pod 親和性,它將回退到 wq->dfl_pwq,這可能不是最優的,但總是正確的。
請注意,當一個 pod 的最後一個允許的 CPU 對於一個跨多個 pod 的 CPU 掩碼(cpumask)的工作佇列離線時,已經為該工作佇列執行工作項的工作者將失去其 CPU 親和性,並可能在任何 CPU 上執行。這類似於每 CPU 工作佇列在 CPU_DOWN 時的行為。如果工作佇列使用者需要嚴格的親和性,則使用者有責任從 CPU_DOWN_PREPARE 中重新整理工作項。
-
void wq_adjust_max_active(struct workqueue_struct *wq)¶
將工作佇列的 max_active 更新為當前設定
引數
struct workqueue_struct *wq目標工作佇列
描述
如果 wq 未凍結,則將 wq->max_active 設定為 saved_max_active 並相應地啟用不活躍的工作項。如果 wq 正在凍結,則將 wq->max_active 清零。
-
void destroy_workqueue(struct workqueue_struct *wq)¶
安全地終止一個工作佇列
引數
struct workqueue_struct *wq目標工作佇列
描述
安全地銷燬一個工作佇列。所有當前待處理的工作將首先完成。
此函式不保證使用 queue_delayed_work() 及類似函式提交的非待處理工作會在銷燬工作佇列之前完成。根本問題是,目前工作佇列無法訪問非待處理的延遲工作(delayed_work)。延遲工作只在定時器側鏈接。因此,在呼叫此函式之前,所有延遲工作都必須被取消。
待辦事項:如果上述問題不存在,並且 destroy_workqueue() 能夠乾淨地取消所有待處理和非待處理的延遲工作,那會更好。
-
void workqueue_set_max_active(struct workqueue_struct *wq, int max_active)¶
調整工作佇列的 max_active
引數
struct workqueue_struct *wq目標工作佇列
int max_active新的 max_active 值。
描述
將 wq 的 max_active 設定為 max_active。參見 alloc_workqueue() 函式註釋。
上下文
不要在中斷上下文(IRQ context)中呼叫。
-
void workqueue_set_min_active(struct workqueue_struct *wq, int min_active)¶
調整無繫結工作佇列的 min_active
引數
struct workqueue_struct *wq目標無繫結工作佇列
int min_active新的 min_active 值
描述
設定無繫結工作佇列的 min_active。與其他型別的工作佇列不同,無繫結工作佇列不保證能夠處理 max_active 個相互依賴的工作項。相反,無繫結工作佇列保證能夠處理 min_active 個相互依賴的工作項,預設為 WQ_DFL_MIN_ACTIVE。
使用此函式將 min_active 值調整到 0 和當前 max_active 之間。
-
struct work_struct *current_work(void)¶
檢索
current任務的工作結構體
引數
void無引數
描述
判斷 current 任務是否為工作佇列工作者以及它正在處理什麼。這有助於查明 current 任務執行的上下文。
返回
如果 current 任務是工作佇列工作者,則返回工作結構體;否則返回 NULL。
-
bool current_is_workqueue_rescuer(void)¶
current是工作佇列救援者嗎?
引數
void無引數
描述
確定 current 是否為工作佇列救援者。可用於工作函式中,以確定其是否由救援任務執行。
返回
如果 current 是工作佇列救援者,則返回 true。否則返回 false。
-
bool workqueue_congested(int cpu, struct workqueue_struct *wq)¶
測試工作佇列是否擁堵
引數
int cpu相關 CPU
struct workqueue_struct *wq目標工作佇列
描述
測試 wq 的 CPU 工作佇列(用於 cpu)是否擁堵。此函式沒有同步機制,測試結果不可靠,僅作為參考或用於除錯。
如果 cpu 是 WORK_CPU_UNBOUND,則在本地 CPU 上執行測試。
除了有序工作佇列外,所有工作佇列都具有每 CPU 的 pool_workqueue,每個都有自己的擁堵狀態。一個工作佇列在一個 CPU 上擁堵並不意味著該工作佇列在其他任何 CPU 上也擁堵。
返回
如果擁堵,則返回 true;否則返回 false。
-
unsigned int work_busy(struct work_struct *work)¶
測試一個工作當前是待處理還是正在執行
引數
struct work_struct *work要測試的工作
描述
測試 work 當前是待處理還是正在執行。此函式沒有同步機制,測試結果不可靠,僅作為參考或用於除錯。
返回
WORK_BUSY_* 位的或操作位掩碼。
-
void set_worker_desc(const char *fmt, ...)¶
為當前工作項設定描述
引數
const char *fmtprintf 風格的格式字串
...格式字串的引數
描述
此函式可以由正在執行的工作函式呼叫,以描述工作項的用途。如果工作者任務被轉儲,此資訊將一起打印出來以幫助除錯。描述的最大長度為 WORKER_DESC_LEN,包括末尾的 '0'。
-
void print_worker_info(const char *log_lvl, struct task_struct *task)¶
列印工作者資訊和描述
引數
const char *log_lvl列印時使用的日誌級別
struct task_struct *task目標任務
描述
如果 task 是工作者且當前正在執行工作項,則打印出正在服務的工工作佇列名稱以及當前執行的工作項透過 set_worker_desc() 設定的工作者描述。
只要 task_struct 本身可訪問,此函式就可以安全地在任何任務上呼叫。雖然安全,但此函式未同步,可能會打印出有限長度的混合或垃圾資訊。
-
void show_one_workqueue(struct workqueue_struct *wq)¶
轉儲指定工作佇列的狀態
引數
struct workqueue_struct *wq將列印其狀態的工作佇列
-
void show_one_worker_pool(struct worker_pool *pool)¶
轉儲指定工作者池的狀態
引數
struct worker_pool *pool將列印其狀態的工作者池
-
void show_all_workqueues(void)¶
轉儲工作佇列狀態
引數
void無引數
描述
由系統請求處理程式呼叫,並打印出所有忙碌的工作佇列和池。
-
void show_freezable_workqueues(void)¶
轉儲可凍結工作佇列狀態
引數
void無引數
描述
由 try_to_freeze_tasks() 呼叫,並打印出所有仍然忙碌的可凍結工作佇列。
-
void rebind_workers(struct worker_pool *pool)¶
將池的所有工作者重新繫結到關聯的 CPU
引數
struct worker_pool *pool相關池
描述
pool->cpu 正在上線。將所有工作者重新繫結到該 CPU。
-
void restore_unbound_workers_cpumask(struct worker_pool *pool, int cpu)¶
恢復無繫結工作者的 CPU 掩碼
引數
struct worker_pool *pool感興趣的無繫結池
int cpu正在上線的 CPU
描述
無繫結池最終可能擁有一個不包含任何線上 CPU 的 CPU 掩碼。當此池的工作者被排程時,排程器會重置其 cpus_allowed。如果 cpu 位於 pool 的 CPU 掩碼中,且該掩碼之前沒有任何線上 CPU,則應恢復其所有工作者的 cpus_allowed。
-
long work_on_cpu_key(int cpu, long (*fn)(void*), void *arg, struct lock_class_key *key)¶
在特定 CPU 的執行緒上下文中執行函式
引數
int cpu要執行的 CPU
long (*fn)(void *)要執行的函式
void *arg函式引數
struct lock_class_key *key用於鎖除錯目的的鎖類鍵
描述
呼叫者有責任確保 CPU 不會離線。呼叫者不得持有任何會阻止 fn 完成的鎖。
返回
fn 返回的值。
-
long work_on_cpu_safe_key(int cpu, long (*fn)(void*), void *arg, struct lock_class_key *key)¶
在特定 CPU 的執行緒上下文中執行函式
引數
int cpu要執行的 CPU
long (*fn)(void *)要執行的函式
void *arg函式引數
struct lock_class_key *key用於鎖除錯目的的鎖類鍵
描述
停用 CPU 熱插拔並呼叫 work_on_cpu()。呼叫者不得持有任何會阻止 fn 完成的鎖。
返回
fn 返回的值。
-
void freeze_workqueues_begin(void)¶
開始凍結工作佇列
引數
void無引數
描述
開始凍結工作佇列。此函式返回後,所有可凍結工作佇列將把新工作排隊到其 inactive_works 列表,而不是 pool->worklist。
上下文
獲取並釋放 wq_pool_mutex、wq->mutex 和 pool->lock。
-
bool freeze_workqueues_busy(void)¶
可凍結工作佇列是否仍忙碌?
引數
void無引數
描述
檢查凍結是否完成。此函式必須在 freeze_workqueues_begin() 和 thaw_workqueues() 之間呼叫。
上下文
獲取並釋放 wq_pool_mutex。
返回
如果某些可凍結工作佇列仍在忙碌,則返回 true;如果凍結完成,則返回 false。
-
void thaw_workqueues(void)¶
解凍工作佇列
引數
void無引數
描述
解凍工作佇列。恢復正常排隊,所有收集到的凍結工作項被轉移到各自的池工作列表。
上下文
獲取並釋放 wq_pool_mutex、wq->mutex 和 pool->lock。
-
int workqueue_unbound_exclude_cpumask(cpumask_var_t exclude_cpumask)¶
將給定 CPU 從無繫結 CPU 掩碼中排除
引數
cpumask_var_t exclude_cpumask要從 wq_unbound_cpumask 中排除的 CPU 掩碼
描述
此函式可以從 cpuset 程式碼中呼叫,以提供一組應從 wq_unbound_cpumask 中排除的隔離 CPU。
-
int workqueue_set_unbound_cpumask(cpumask_var_t cpumask)¶
設定低階無繫結 CPU 掩碼
引數
cpumask_var_t cpumask要設定的 CPU 掩碼
低階工作佇列的 CPU 掩碼是一個全域性 CPU 掩碼,它限制了所有無繫結工作佇列的親和性。此函式檢查 cpumask 並將其應用於所有無繫結工作佇列,同時更新它們的所有 pwq。
返回
- 0 - 成功
-EINVAL - 無效的 cpumask -ENOMEM - 為 attrs 或 pwqs 分配記憶體失敗。
-
int workqueue_sysfs_register(struct workqueue_struct *wq)¶
使工作佇列在 sysfs 中可見
引數
struct workqueue_struct *wq要註冊的工作佇列
描述
在 sysfs 的 /sys/bus/workqueue/devices 目錄下公開 wq。如果設定了 WQ_SYSFS,alloc_workqueue*() 會自動呼叫此函式,這是首選方法。
工作佇列使用者只有在希望在工作佇列在 sysfs 中可見之前應用 workqueue_attrs 時,才應直接使用此函式;否則,apply_workqueue_attrs() 可能會與使用者空間更新屬性的操作發生競爭。
返回
成功時返回 0,失敗時返回 -errno。
-
void workqueue_sysfs_unregister(struct workqueue_struct *wq)¶
撤銷
workqueue_sysfs_register()的操作
-
void workqueue_init_early(void)¶
工作佇列子系統的早期初始化
引數
void無引數
描述
這是三階段工作佇列子系統初始化的第一步,在記憶體分配、CPU 掩碼和 IDR 等基本要素就緒後立即呼叫。它設定所有資料結構和系統工作佇列,並允許早期啟動程式碼建立工作佇列以及排隊/取消工作項。實際工作項的執行僅在核心執行緒(kthreads)可以建立和排程,且就在早期初始化呼叫(early initcalls)之前開始。
-
void workqueue_init(void)¶
使工作佇列子系統完全上線
引數
void無引數
描述
這是三階段工作佇列子系統初始化的第二步,一旦核心執行緒(kthreads)可以建立和排程就立即呼叫。工作佇列已經建立,工作項也已排隊,但目前還沒有工作者執行這些工作項。此函式用初始工作者填充工作者池,並啟用未來的核心工作者建立。
-
void workqueue_init_topology(void)¶
為無繫結工作佇列初始化 CPU pod
引數
void無引數
描述
這是三階段工作佇列子系統初始化的第三步,在對稱多處理(SMP)和拓撲資訊完全初始化後呼叫。它相應地初始化無繫結 CPU pod。