緩衝區共享與同步 (dma-buf)¶
dma-buf 子系統提供了跨多個裝置驅動程式和子系統共享硬體(DMA)訪問的緩衝區的框架,以及同步非同步硬體訪問的框架。
例如,DRM 子系統廣泛使用它來在程序、上下文、同一程序中的庫 API 之間交換緩衝區,以及與其他子系統(如 V4L2)交換緩衝區。
本文件描述了核心子系統如何使用 dma-buf 提供的三個主要原語以及如何與之互動
dma-buf,表示 sg_table,並作為檔案描述符暴露給使用者空間,以允許在程序、子系統、裝置等之間傳遞;
dma-fence,提供了一種機制來指示非同步硬體操作何時完成;以及
dma-resv,它為特定的 dma-buf 管理一組 dma-fences,允許對工作進行隱式(核心排序的)同步,以保留相干訪問的錯覺
使用者空間 API 原則和使用¶
有關如何為 dma-buf 使用設計子系統的 API 的更多詳細資訊,請參閱 交換畫素緩衝區。
預留物件¶
預留物件提供了一種機制來管理與資源關聯的 dma_fence 物件的容器。 一個預留物件可以附加任意數量的 fence。 每個 fence 都帶有一個使用引數,該引數確定 fence 所代表的操作如何使用該資源。 RCU 機制用於保護對 fence 的讀取訪問,防止鎖定的寫入端更新。
有關更多詳細資訊,請參見 struct dma_resv。
引數
struct dma_resv *obj預留物件
引數
struct dma_resv *obj預留物件
-
int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)¶
為 dma_resv 物件新增 fence 保留空間。
引數
struct dma_resv *obj預留物件
unsigned int num_fences我們要新增的 fence 的數量
描述
應在 dma_resv_add_fence() 之前呼叫。 必須透過 dma_resv_lock() 鎖定 obj 來呼叫。
請注意,如果在呼叫 dma_resv_add_fence() 之前 obj 在任何時候被解鎖,則需要重新預留預分配的插槽。 當 CONFIG_DEBUG_MUTEXES 啟用時,會驗證這一點。
返回值:成功時為零,或 -errno
引數
struct dma_resv *obj要重置的 dma_resv 物件
描述
重置預先預留的 fence 插槽的數量,以測試驅動程式是否使用 dma_resv_reserve_fences() 執行正確的插槽分配。 另請參見 dma_resv_list.max_fences。
-
void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence, enum dma_resv_usage usage)¶
將 fence 新增到 dma_resv obj
引數
struct dma_resv *obj預留物件
struct dma_fence *fence要新增的 fence
enum dma_resv_usage usage如何使用 fence,請參見
enum dma_resv_usage
描述
將 fence 新增到一個插槽,必須使用 dma_resv_lock() 鎖定 obj,並且已呼叫 dma_resv_reserve_fences()。
有關語義的討論,另請參見 dma_resv.fence。
-
void dma_resv_replace_fences(struct dma_resv *obj, uint64_t context, struct dma_fence *replacement, enum dma_resv_usage usage)¶
替換 dma_resv obj 中的 fence
引數
struct dma_resv *obj預留物件
uint64_t context要替換的 fence 的上下文
struct dma_fence *replacement要改用的新 fence
enum dma_resv_usage usage如何使用新 fence,請參見
enum dma_resv_usage
描述
將具有指定上下文的 fence 替換為新的 fence。 僅當原始 fence 所代表的操作在新的 fence 完成時不再可以訪問 dma_resv 物件所代表的資源時才有效。
使用此方法的一個示例是用頁表更新 fence 替換搶佔 fence,這會使資源無法訪問。
-
struct dma_fence *dma_resv_iter_first_unlocked(struct dma_resv_iter *cursor)¶
未鎖定的 dma_resv obj 中的第一個 fence。
引數
struct dma_resv_iter *cursor帶有當前位置的游標
描述
後續 fence 使用 dma_resv_iter_next_unlocked() 進行迭代。
請注意,迭代器可以重新啟動。 累積統計資訊或類似資訊的程式碼需要使用 dma_resv_iter_is_restarted() 檢查這一點。 因此,請儘可能使用鎖定的 dma_resv_iter_first()。
返回來自未鎖定的 dma_resv obj 的第一個 fence。
-
struct dma_fence *dma_resv_iter_next_unlocked(struct dma_resv_iter *cursor)¶
未鎖定的 dma_resv obj 中的下一個 fence。
引數
struct dma_resv_iter *cursor帶有當前位置的游標
描述
請注意,迭代器可以重新啟動。 累積統計資訊或類似資訊的程式碼需要使用 dma_resv_iter_is_restarted() 檢查這一點。 因此,請儘可能使用鎖定的 dma_resv_iter_next()。
返回來自未鎖定的 dma_resv obj 的下一個 fence。
-
struct dma_fence *dma_resv_iter_first(struct dma_resv_iter *cursor)¶
從已鎖定的 dma_resv 物件獲取第一個 fence。
引數
struct dma_resv_iter *cursor用於記錄當前位置的游標。
描述
後續 fence 使用 dma_resv_iter_next_unlocked() 進行迭代。
返回 dma_resv 物件中的第一個 fence,同時持有 dma_resv.lock。
-
struct dma_fence *dma_resv_iter_next(struct dma_resv_iter *cursor)¶
從已鎖定的 dma_resv 物件獲取下一個 fence。
引數
struct dma_resv *dst目標 reservation 物件。
struct dma_resv *src源 reservation 物件。
描述
將所有 fence 從 src 複製到 dst。必須持有 dst-lock。
-
int dma_resv_get_fences(struct dma_resv *obj, enum dma_resv_usage usage, unsigned int *num_fences, struct dma_fence ***fences)¶
獲取物件的 fences,無需持有 update side lock。
引數
struct dma_resv *obj預留物件
enum dma_resv_usage usage控制包含哪些 fences,請參閱
enum dma_resv_usage。unsigned int *num_fences返回的 fence 數量。
struct dma_fence ***fences返回的 fence 指標陣列 (陣列將 krealloc’d 到所需大小,並且必須由呼叫者釋放)
描述
從 reservation 物件檢索所有 fence。返回零或 -ENOMEM。
-
int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage, struct dma_fence **fence)¶
獲取表示所有 fences 的單個 fence。
引數
struct dma_resv *obj預留物件
enum dma_resv_usage usage控制包含哪些 fences,請參閱
enum dma_resv_usage。struct dma_fence **fence生成的 fence。
描述
獲取表示 resv 物件中所有 fences 的單個 fence。成功時返回 0,失敗時返回負錯誤值。
警告:將 fence 添加回 resv 物件時,不能像這樣使用它,因為在完成 dma_fence_array 時,這可能會導致堆疊損壞。
成功時返回 0,失敗時返回負錯誤值。
-
long dma_resv_wait_timeout(struct dma_resv *obj, enum dma_resv_usage usage, bool intr, unsigned long timeout)¶
等待 reservation 物件的 fences。
引數
struct dma_resv *obj預留物件
enum dma_resv_usage usage控制包含哪些 fences,請參閱
enum dma_resv_usage。bool intr如果為 true,則執行可中斷的等待。
unsigned long timeoutjiffies 中的超時值,或零以立即返回。
描述
呼叫者不需要持有特定的鎖,但可能已經持有 dma_resv_lock()。如果被中斷,返回 -ERESTARTSYS;如果等待超時,返回 0;成功時返回大於零的值。
-
void dma_resv_set_deadline(struct dma_resv *obj, enum dma_resv_usage usage, ktime_t deadline)¶
在 reservation 物件的 fences 上設定截止時間。
引數
struct dma_resv *obj預留物件
enum dma_resv_usage usage控制包含哪些 fences,請參閱
enum dma_resv_usage。ktime_t deadline請求的截止時間 (MONOTONIC)。
描述
可以在不持有 dma_resv 鎖的情況下呼叫。在所有由 **usage** 過濾的 fences 上設定 **deadline**。
-
bool dma_resv_test_signaled(struct dma_resv *obj, enum dma_resv_usage usage)¶
測試 reservation 物件的 fences 是否已發出訊號。
引數
struct dma_resv *obj預留物件
enum dma_resv_usage usage控制包含哪些 fences,請參閱
enum dma_resv_usage。
描述
呼叫者不需要持有特定的鎖,但可能已經持有 dma_resv_lock()。
返回值
如果所有 fences 都已發出訊號,則為 True,否則為 false。
引數
struct dma_resv *obj預留物件
struct seq_file *seq要將描述轉儲到的 seq_file。
描述
將 dma_resv 物件中的 fences 的文字描述轉儲到 seq_file 中。
-
enum dma_resv_usage¶
dma_resv 物件中的 fences 的使用方式。
常量
DMA_RESV_USAGE_KERNEL僅用於核心記憶體管理。
這應該僅用於使用 DMA 硬體引擎複製或清除記憶體,以用於核心記憶體管理的情況。
驅動程式在訪問受 dma_resv 物件保護的資源之前*始終*必須等待這些 fences。唯一的例外是已知資源透過先前固定它而被鎖定到位的情況。
DMA_RESV_USAGE_WRITE隱式寫入同步。
這應該僅用於新增隱式寫入依賴項的使用者空間命令提交。
DMA_RESV_USAGE_READ隱式讀取同步。
這應該僅用於新增隱式讀取依賴項的使用者空間命令提交。
DMA_RESV_USAGE_BOOKKEEP沒有隱式同步。
這應該由不希望參與任何隱式同步的提交使用。
最常見的情況是搶佔 fences、頁表更新、TLB 重新整理以及顯式同步的使用者提交。
顯式同步的使用者提交可以根據需要使用
dma_buf_import_sync_file()提升到 DMA_RESV_USAGE_READ 或 DMA_RESV_USAGE_WRITE,以便在初始新增 fence 後,隱式同步變得必要。
描述
此列舉描述了 dma_resv 物件的不同用例,並控制查詢時返回哪些 fences。
一個重要的事實是,順序為 KERNEL<WRITE<READ<BOOKKEEP,當 dma_resv 物件被要求提供一個用例的 fences 時,也會返回較低用例的 fences。
例如,當請求 WRITE fences 時,也會返回 KERNEL fences。類似地,當請求 READ fences 時,也會返回 WRITE 和 KERNEL fences。
已經使用的 fences 可以透過再次新增此用法的 fence 來提升,例如,具有 DMA_RESV_USAGE_BOOKKEEP 的 fence 可以變為 DMA_RESV_USAGE_READ。但是,fences 永遠無法降級,例如,具有 DMA_RESV_USAGE_WRITE 的 fence 無法變為 DMA_RESV_USAGE_READ。
-
enum dma_resv_usage dma_resv_usage_rw(bool write)¶
隱式同步的輔助函式。
引數
bool write如果我們要建立新的隱式同步寫入,則為 true。
描述
這返回用於寫入或讀取訪問的隱式同步用法,請參閱 enum dma_resv_usage 和 dma_buf.resv。
-
struct dma_resv¶
一個 reservation 物件,用於管理緩衝區的 fences。
定義:
struct dma_resv {
struct ww_mutex lock;
struct dma_resv_list __rcu *fences;
};
成員
lock更新側鎖。不要直接使用,而是使用包裝器函式,例如
dma_resv_lock()和dma_resv_unlock()。使用 reservation 物件動態管理記憶體的驅動程式也使用此鎖來保護緩衝區物件狀態,例如放置、分配策略或整個命令提交。
fences已新增到 dma_resv 物件的 fences 陣列。
透過呼叫
dma_resv_add_fence()新增新的 fence。由於這通常需要在命令提交中無法返回的點之後完成,因此它不能失敗,因此需要透過呼叫dma_resv_reserve_fences()保留足夠的 slots。
描述
這是 dma_fence 物件的容器,需要處理多個用例。
一種用途是同步對 struct dma_buf 的跨驅動程式訪問,無論是用於動態緩衝區管理,還是僅處理使用者空間中緩衝區不同使用者之間的隱式同步。有關更深入的討論,請參閱 dma_buf.resv。
另一個主要用途是在基於緩衝區的記憶體管理器中管理驅動程式內的訪問和鎖定。struct ttm_buffer_object 是這裡的規範示例,因為這是 reservation 物件最初的來源。但在驅動程式中的使用正在擴充套件,並且一些驅動程式還使用相同的方案管理 struct drm_gem_object。
-
struct dma_resv_iter¶
dma_resv fences 中的當前位置。
定義:
struct dma_resv_iter {
struct dma_resv *obj;
enum dma_resv_usage usage;
struct dma_fence *fence;
enum dma_resv_usage fence_usage;
unsigned int index;
struct dma_resv_list *fences;
unsigned int num_fences;
bool is_restarted;
};
成員
obj我們要迭代的 dma_resv 物件。
usage返回具有此用法或更低用法的 fences。
fence當前處理的 fence。
fence_usage當前 fence 的用法。
index共享 fences 的索引。
fences共享 fences;私有,*必須* 不取消引用。
num_fencesfences 的數量。
is_restarted如果這是第一個返回的 fence,則為 true。
描述
不要在驅動程式中直接觸控此物件,而是使用訪問器函式。
重要提示
當使用無鎖迭代器(例如 dma_resv_iter_next_unlocked() 或 dma_resv_for_each_fence_unlocked())時,請注意迭代器可以重新啟動。使用 dma_resv_iter_is_restarted() 檢查是否正在累積統計資訊或類似資訊。
-
void dma_resv_iter_begin(struct dma_resv_iter *cursor, struct dma_resv *obj, enum dma_resv_usage usage)¶
初始化 dma_resv_iter 物件。
引數
struct dma_resv_iter *cursor要初始化的 dma_resv_iter 物件。
struct dma_resv *obj我們要迭代的 dma_resv 物件。
enum dma_resv_usage usage控制包含哪些 fences,請參閱
enum dma_resv_usage。
-
void dma_resv_iter_end(struct dma_resv_iter *cursor)¶
清理 dma_resv_iter 物件。
引數
struct dma_resv_iter *cursor應該清理的 dma_resv_iter 物件。
描述
確保正確刪除對游標中 fence 的引用。
-
enum dma_resv_usage dma_resv_iter_usage(struct dma_resv_iter *cursor)¶
返回當前 fence 的 usage。
引數
struct dma_resv_iter *cursor當前位置的游標。
描述
返回當前處理的 fence 的 usage。
-
bool dma_resv_iter_is_restarted(struct dma_resv_iter *cursor)¶
測試這是否是重啟後的第一個 fence。
引數
struct dma_resv_iter *cursor帶有當前位置的游標
描述
如果這是重啟後迭代中的第一個 fence,則返回 true。
-
dma_resv_for_each_fence_unlocked¶
dma_resv_for_each_fence_unlocked (cursor, fence)
未鎖定的 fence 迭代器。
引數
游標。一個
struct dma_resv_iter指標。fence當前的 fence。
描述
在不持有 struct dma_resv 物件的 dma_resv.lock 鎖的情況下,使用 RCU 迭代 fences。 游標需要使用 dma_resv_iter_begin() 初始化,並使用 dma_resv_iter_end() 清理。 在迭代器內部,持有對 dma_fence 的引用,並釋放 RCU 鎖。
請注意,當修改 cursor 的 struct dma_resv 時,迭代器可以重新啟動。 積累統計資訊或類似資訊的程式碼需要使用 dma_resv_iter_is_restarted() 檢查這一點。 因此,儘可能優先選擇鎖迭代器 dma_resv_for_each_fence()。
-
dma_resv_for_each_fence¶
dma_resv_for_each_fence (cursor, obj, usage, fence)
fence 迭代器。
引數
游標。一個
struct dma_resv_iter指標。obj一個 dma_resv 物件指標。
usage控制返回哪些 fences。
fence當前的 fence。
描述
在持有 struct dma_resv 物件的 dma_resv.lock 鎖的情況下,迭代 fences。 all_fences 控制是否也返回共享的 fences。 游標初始化是迭代器的一部分,並且 fence 只要鎖被持有就保持有效,因此不會對 fence 進行額外的引用。
引數
struct dma_resv *obj預留物件
struct ww_acquire_ctx *ctx鎖定上下文。
描述
鎖定 reservation 物件以進行獨佔訪問和修改。 請注意,該鎖僅針對其他寫入者,讀取者將與 RCU 下的寫入者併發執行。 seqlock 用於在讀者與作者重疊時通知讀者。
由於 reservation 物件可能被多個參與者以未定義的順序鎖定,因此傳遞一個 #ww_acquire_ctx,以便在檢測到迴圈時進行回退。 請參閱 ww_mutex_lock() 和 ww_acquire_init()。 reservation 物件可以透過傳遞 NULL 作為 ctx 來鎖定自身。
當返回 -EDEADLK 指示出現死鎖情況時,必須解鎖 ctx 持有的所有鎖,然後在 obj 上呼叫 dma_resv_lock_slow()。
透過呼叫 dma_resv_unlock() 解鎖。
另請參閱 dma_resv_lock_interruptible() 的可中斷變體。
-
int dma_resv_lock_interruptible(struct dma_resv *obj, struct ww_acquire_ctx *ctx)¶
鎖定 reservation 物件。
引數
struct dma_resv *obj預留物件
struct ww_acquire_ctx *ctx鎖定上下文。
描述
以可中斷的方式鎖定 reservation 物件以進行獨佔訪問和修改。 請注意,該鎖僅針對其他寫入者,讀取者將與 RCU 下的寫入者併發執行。 seqlock 用於在讀者與作者重疊時通知讀者。
由於 reservation 物件可能被多個參與者以未定義的順序鎖定,因此傳遞一個 #ww_acquire_ctx,以便在檢測到迴圈時進行回退。 請參閱 ww_mutex_lock() 和 ww_acquire_init()。 reservation 物件可以透過傳遞 NULL 作為 ctx 來鎖定自身。
當返回 -EDEADLK 指示出現死鎖情況時,必須解鎖 ctx 持有的所有鎖,然後在 obj 上呼叫 dma_resv_lock_slow_interruptible()。
透過呼叫 dma_resv_unlock() 解鎖。
-
void dma_resv_lock_slow(struct dma_resv *obj, struct ww_acquire_ctx *ctx)¶
slowpath 鎖定 reservation 物件。
引數
struct dma_resv *obj預留物件
struct ww_acquire_ctx *ctx鎖定上下文。
描述
在死鎖情況後獲取 reservation 物件。 此函式將休眠直到鎖變為可用。 另請參閱 dma_resv_lock()。
另請參閱 dma_resv_lock_slow_interruptible() 的可中斷變體。
-
int dma_resv_lock_slow_interruptible(struct dma_resv *obj, struct ww_acquire_ctx *ctx)¶
slowpath 鎖定 reservation 物件,可中斷。
引數
struct dma_resv *obj預留物件
struct ww_acquire_ctx *ctx鎖定上下文。
描述
在死鎖情況後以可中斷的方式獲取 reservation 物件。 此函式將休眠直到鎖變為可用。 另請參閱 dma_resv_lock_interruptible()。
引數
struct dma_resv *obj預留物件
描述
嘗試鎖定 reservation 物件以進行獨佔訪問和修改。 請注意,該鎖僅針對其他寫入者,讀取者將與 RCU 下的寫入者併發執行。 seqlock 用於在讀者與作者重疊時通知讀者。
另請注意,由於未提供上下文,因此無法進行死鎖保護,對於 trylock 而言也不需要死鎖保護。
如果獲取了鎖,則返回 true,否則返回 false。
引數
struct dma_resv *obj預留物件
描述
如果互斥鎖已鎖定,則返回 true,如果未鎖定,則返回 false。
引數
struct dma_resv *obj預留物件
描述
返回用於鎖定 reservation 物件的上下文,如果未使用上下文或物件根本未鎖定,則返回 NULL。
警告:此介面非常糟糕,但 TTM 需要它,因為它沒有在一些非常長的呼叫鏈中傳遞 struct ww_acquire_ctx。 其他人都只是用它來檢查他們是否持有 reservation。
引數
struct dma_resv *obj預留物件
描述
在獨佔訪問之後解鎖 reservation 物件。
DMA Fences¶
DMA fences,由 struct dma_fence 表示,是用於 DMA 操作的核心內部同步原語,例如 GPU 渲染、影片編碼/解碼或在螢幕上顯示緩衝區。
使用 dma_fence_init() 初始化 fence,並使用 dma_fence_signal() 完成 fence。 Fences 與上下文相關聯,該上下文透過 dma_fence_context_alloc() 分配,並且同一上下文上的所有 fences 都完全排序。
由於 fences 的目的是促進跨裝置和跨應用程式同步,因此有多種使用方式。
單個 fences 可以作為
sync_file公開,作為使用者空間的 file descriptor 訪問,透過呼叫sync_file_create()建立。 這被稱為顯式 fencing,因為使用者空間傳遞顯式同步點。一些子系統也有它們自己的顯式 fencing 原語,例如
drm_syncobj。 與sync_file相比,drm_syncobj允許更新底層 fence。還有隱式 fencing,其中同步點作為共享
dma_buf例項的一部分隱式傳遞。 這些隱式 fences 儲存在struct dma_resv中,透過dma_buf.resv指標。
DMA Fence 跨驅動程式約定¶
由於 dma_fence 提供跨驅動程式約定,因此所有驅動程式必須遵循相同的規則。
Fences 必須在合理的時間內完成。 代表使用者空間提交的 kernels 和 shaders 的 fences,可能會永遠執行,必須由超時和 gpu hang recovery 程式碼支援。 至少該程式碼必須阻止進一步的命令提交併強制完成所有正在執行的 fences,例如,當驅動程式或硬體不支援 gpu 重置,或者如果由於某種原因 gpu 重置失敗時。 理想情況下,驅動程式支援 gpu recovery,它隻影響有問題的使用者空間上下文,而不影響其他使用者空間提交。
驅動程式可能對在合理時間內完成意味著什麼有不同的想法。 一些 hang recovery 程式碼使用固定的超時,另一些則混合使用觀察前進進度和越來越嚴格的超時。 驅動程式不應試圖猜測來自其他驅動程式的 fences 的超時處理。
為了確保
dma_fence_wait()不會死鎖與其他鎖,驅動程式應使用dma_fence_begin_signalling()和dma_fence_end_signalling()註釋所有需要到達dma_fence_signal()的程式碼,該程式碼完成 fences。驅動程式允許在持有
dma_resv_lock()時呼叫dma_fence_wait()。 這意味著 fence 完成所需的任何程式碼都無法獲取dma_resv鎖。 請注意,這也會引入圍繞dma_resv_lock()和dma_resv_unlock()的整個已建立的鎖定層次結構。驅動程式允許從他們的
shrinker回撥中呼叫dma_fence_wait()。 這意味著 fence 完成所需的任何程式碼都無法使用 GFP_KERNEL 分配記憶體。驅動程式允許從他們的
mmu_notifier分別是mmu_interval_notifier回撥中呼叫dma_fence_wait()。 這意味著 fence 完成所需的任何程式碼都無法使用 GFP_NOFS 或 GFP_NOIO 分配記憶體。 只允許 GFP_ATOMIC,這可能會失敗。
請注意,只有 GPU 驅動程式有合理的理由同時需要 mmu_interval_notifier 和 shrinker 回撥,同時還必須使用 dma_fence 跟蹤非同步計算工作。 drivers/gpu 之外的任何驅動程式都不應在這種情況下呼叫 dma_fence_wait()。
DMA Fence Signaling 註釋¶
對於一些原因,透過程式碼審查和測試來證明圍繞 dma_fence 的所有核心程式碼的正確性是很棘手的。
這是一個跨驅動程式約定,因此所有驅動程式必須遵循相同的鎖定巢狀順序規則,各種函式的呼叫上下文以及對核心內介面有意義的任何其他內容。 但也不可能在一臺機器中測試所有驅動程式,因此不可能對所有組合進行暴力 N vs. N 測試。 即使只是限制可能的組合也是不可行的。
涉及大量的驅動程式程式碼。 對於渲染驅動程式,在釋出 fences 之後,命令提交的尾部、排程程式程式碼、中斷和處理 job 完成的 workers 以及超時、gpu 重置和 gpu hang recovery 程式碼。 此外,為了與核心 mm 整合,我們有
mmu_notifier,分別是mmu_interval_notifier和shrinker。 對於 modesetting 驅動程式,在釋出原子 modeset 的 fences 和相應的 vblank 完成之間,有 commit 尾部函式,包括任何中斷處理和相關的 workers。 審計所有驅動程式上的所有這些程式碼是不可行的。由於涉及許多其他子系統以及由此引入的鎖定層次結構,因此驅動程式特定的差異幾乎沒有迴旋餘地。
dma_fence透過dma_resv、dma_resv_lock()和dma_resv_unlock()與幾乎所有核心記憶體處理(透過頁面錯誤處理程式)進行互動。另一方面,它還透過mmu_notifier和shrinker與所有分配點進行互動。
此外,lockdep 不處理跨釋出依賴關係,這意味著 dma_fence_wait() 和 dma_fence_signal() 之間的任何死鎖都無法在執行時透過一些快速測試來捕獲。最簡單的例子是一個執行緒等待 dma_fence,同時持有一個鎖
lock(A);
dma_fence_wait(B);
unlock(A);
而另一個執行緒被阻止嘗試獲取相同的鎖,這阻止了它發出前一個執行緒被阻止等待的 fence 訊號
lock(A);
unlock(A);
dma_fence_signal(B);
透過手動註釋所有與發出 dma_fence 訊號相關的程式碼,我們可以教導 lockdep 關於這些依賴關係,這也有助於驗證難題,因為現在 lockdep 可以為我們檢查所有規則
cookie = dma_fence_begin_signalling();
lock(A);
unlock(A);
dma_fence_signal(B);
dma_fence_end_signalling(cookie);
對於使用 dma_fence_begin_signalling() 和 dma_fence_end_signalling() 來註釋關鍵部分,需要遵守以下規則
必須註釋完成
dma_fence所需的所有程式碼,從 fence 可供其他執行緒訪問的點到呼叫dma_fence_signal()的點。未註釋的程式碼可能包含死鎖問題,並且由於非常嚴格的規則和許多極端情況,僅透過審查或正常的壓力測試無法捕獲這些問題。struct dma_resv值得特別注意,因為讀取器僅受 rcu 保護。這意味著信令關鍵部分從安裝新 fence 時開始,甚至在呼叫dma_resv_unlock()之前。唯一的例外是快速路徑和機會主義信令程式碼,它們呼叫
dma_fence_signal()純粹是為了最佳化,但不需要保證dma_fence的完成。通常的例子是等待 IOCTL 呼叫dma_fence_signal(),而強制完成路徑透過硬體中斷和可能的工作完成程式。為了幫助程式碼的可組合性,只要總體鎖定層次結構一致,註釋可以自由巢狀。註釋也可以在中斷和程序上下文中工作。由於實現細節,這需要呼叫者將來自
dma_fence_begin_signalling()的不透明 cookie 傳遞給dma_fence_end_signalling()。針對跨驅動程式合同的驗證是透過在啟動時使用相關層次結構來啟動 lockdep 來實現的。這意味著即使僅使用單個裝置進行測試也足以驗證驅動程式,至少就
dma_fence_wait()和dma_fence_signal()之間的死鎖而言。
DMA Fence 截止期限提示¶
在一個理想的世界中,有可能充分流水線化工作負載,以至於基於利用率的裝置頻率調節器可以達到滿足用例要求的最低頻率,從而最大限度地降低功耗。但在現實世界中,許多工作負載都與這種理想背道而馳。例如,但不限於
在裝置和 CPU 之間來回切換的工作負載,CPU 交替等待裝置,裝置等待 CPU。這可能導致 devfreq 和 cpufreq 在各自的域中看到空閒時間,並導致降低頻率。
與基於時間的週期性截止期限互動的工作負載,例如雙緩衝 GPU 渲染與 vblank 同步的頁面翻轉。在這種情況下,錯過 vblank 截止期限會導致 GPU 空閒時間增加(因為它必須等待額外的 vblank 週期),從而向 GPU 的 devfreq 傳送訊號以降低頻率,而實際上需要的是相反的。
為此,可以透過 dma_fence_set_deadline(或透過面向使用者空間的 ioctl,如 sync_set_deadline)在 dma_fence 上設定截止期限提示。截止期限提示提供了一種方式,讓等待的驅動程式或使用者空間向信令驅動程式傳達適當的緊迫感。
截止期限提示以絕對 ktime 給出(面向使用者空間的 API 為 CLOCK_MONOTONIC)。該時間可以是未來的某個時間點(例如頁面翻轉的基於 vblank 的截止期限,或合成器的合成週期的開始),也可以是當前時間以指示立即截止期限提示(即,在發出此 fence 訊號之前,無法取得進展)。
可以在給定的 fence 上設定多個截止期限,甚至可以並行設定。請參閱 dma_fence_ops.set_deadline 的文件。
截止期限提示只是一個提示。建立 fence 的驅動程式可能會透過提高頻率、做出不同的排程選擇等來做出反應。或者什麼都不做。
DMA Fences 函式參考¶
引數
void沒有引數
描述
返回已發出訊號的存根 fence。該 fence 的時間戳對應於啟動後首次呼叫此函式的時間。
引數
ktime_t timestampfence 發出訊號的時間戳
描述
返回新分配和已發出訊號的存根 fence。
-
u64 dma_fence_context_alloc(unsigned num)¶
分配 fence 上下文陣列
引數
unsigned num要分配的上下文數量
描述
此函式將返回分配的 fence 上下文數量的第一個索引。fence 上下文用於透過將上下文傳遞給 dma_fence_init(),將 dma_fence.context 設定為唯一數字。
-
bool dma_fence_begin_signalling(void)¶
開始關鍵的 DMA fence 信令部分
引數
void沒有引數
描述
驅動程式應使用它來註釋最終需要透過呼叫 dma_fence_signal() 來完成 dma_fence 的任何程式碼部分的開頭。
這些關鍵部分的結尾用 dma_fence_end_signalling() 註釋。
實現所需的不透明 cookie,需要傳遞給 dma_fence_end_signalling()。
-
void dma_fence_end_signalling(bool cookie)¶
結束關鍵的 DMA fence 信令部分
引數
bool cookie來自
dma_fence_begin_signalling()的不透明 cookie
描述
關閉由 dma_fence_begin_signalling() 開啟的關鍵部分註釋。
引數
struct dma_fence *fence要發出訊號的 fence
ktime_t timestamp核心 CLOCK_MONOTONIC 時域中的 fence 訊號時間戳
描述
發出 fence 上軟體回撥的完成訊號,這將解除 dma_fence_wait() 呼叫的阻止,並執行使用 dma_fence_add_callback() 新增的所有回撥。可以多次呼叫,但由於 fence 只能從未發出訊號的狀態變為已發出訊號的狀態,而不能返回,因此它只會在第一次生效。將提供的時間戳設定為 fence 訊號時間戳。
與 dma_fence_signal_timestamp() 不同,必須在持有 dma_fence.lock 的情況下呼叫此函式。
成功時返回 0,如果已發出 fence 訊號,則返回負錯誤值。
引數
struct dma_fence *fence要發出訊號的 fence
ktime_t timestamp核心 CLOCK_MONOTONIC 時域中的 fence 訊號時間戳
描述
發出 fence 上軟體回撥的完成訊號,這將解除 dma_fence_wait() 呼叫的阻止,並執行使用 dma_fence_add_callback() 新增的所有回撥。可以多次呼叫,但由於 fence 只能從未發出訊號的狀態變為已發出訊號的狀態,而不能返回,因此它只會在第一次生效。將提供的時間戳設定為 fence 訊號時間戳。
成功時返回 0,如果已發出 fence 訊號,則返回負錯誤值。
引數
struct dma_fence *fence要發出訊號的 fence
描述
發出 fence 上軟體回撥的完成訊號,這將解除 dma_fence_wait() 呼叫的阻止,並執行使用 dma_fence_add_callback() 新增的所有回撥。可以多次呼叫,但由於 fence 只能從未發出訊號的狀態變為已發出訊號的狀態,而不能返回,因此它只會在第一次生效。
與 dma_fence_signal() 不同,必須在持有 dma_fence.lock 的情況下呼叫此函式。
成功時返回 0,如果已發出 fence 訊號,則返回負錯誤值。
引數
struct dma_fence *fence要發出訊號的 fence
描述
發出 fence 上軟體回撥的完成訊號,這將解除 dma_fence_wait() 呼叫的阻止,並執行使用 dma_fence_add_callback() 新增的所有回撥。可以多次呼叫,但由於 fence 只能從未發出訊號的狀態變為已發出訊號的狀態,而不能返回,因此它只會在第一次生效。
成功時返回 0,如果已發出 fence 訊號,則返回負錯誤值。
-
signed long dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)¶
睡眠,直到 fence 發出訊號或直到超時時間過去
引數
struct dma_fence *fence要等待的 fence
bool intr如果為 true,則進行可中斷的等待
signed long timeoutjiffies 中的超時值,或 MAX_SCHEDULE_TIMEOUT
描述
如果被中斷,則返回 -ERESTARTSYS;如果等待超時,則返回 0;如果成功,則返回剩餘的 jiffies 超時時間。其他錯誤值可能會在自定義實現中返回。
對此 fence 執行同步等待。假定呼叫者直接或間接地(預留和提交之間的 buf-mgr)持有對 fence 的引用,否則 fence 可能會在返回之前被釋放,從而導致未定義的行為。
引數
struct dma_fence *fence要啟用的 fence
描述
這將請求啟用 sw 信令,以便儘快完成 fence。這在內部呼叫 dma_fence_ops.enable_signaling。
-
int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb, dma_fence_func_t func)¶
新增要在發出 fence 訊號時呼叫的回撥
引數
struct dma_fence *fence要等待的 fence
struct dma_fence_cb *cb要註冊的回撥函式
dma_fence_func_t func要呼叫的函式
描述
向 fence 新增一個軟體回撥。呼叫者應保持對 fence 的引用。
cb 將由 dma_fence_add_callback() 初始化,呼叫者無需進行初始化。可以向一個 fence 註冊任意數量的回撥,但一個回撥一次只能註冊到一個 fence。
如果 fence 已經發出訊號,此函式將返回 -ENOENT(並且 *不* 呼叫回撥)。
請注意,回撥可以從原子上下文或 irq 上下文中呼叫。
成功時返回 0,如果 fence 已經發出訊號則返回 -ENOENT,如果出錯則返回 -EINVAL。
引數
struct dma_fence *fence要查詢的 dma_fence
描述
這封裝了 dma_fence_get_status_locked() 以在已發出訊號的 fence 上返回錯誤狀態條件。有關更多詳細資訊,請參見 dma_fence_get_status_locked()。
如果 fence 尚未發出訊號,則返回 0;如果 fence 已發出訊號且沒有錯誤條件,則返回 1;如果 fence 已在 err 中完成,則返回負錯誤程式碼。
-
bool dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb)¶
從信令列表中刪除回撥
引數
struct dma_fence *fence要等待的 fence
struct dma_fence_cb *cb要刪除的回撥
描述
從 fence 中刪除先前排隊的回撥。如果成功刪除回撥,則此函式返回 true;如果 fence 已經發出訊號,則返回 false。
警告:只有在你真正知道自己在做什麼的情況下,才應該取消回撥,因為很容易發生死鎖和競爭條件。因此,它應該只在硬體鎖定恢復時進行,並且持有對 fence 的引用。
如果 cb 之前未使用 dma_fence_add_callback() 新增到 fence,則行為未定義。
-
signed long dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)¶
預設睡眠,直到 fence 發出訊號或直到超時時間過去
引數
struct dma_fence *fence要等待的 fence
bool intr如果為 true,則進行可中斷的等待
signed long timeoutjiffies 中的超時值,或 MAX_SCHEDULE_TIMEOUT
描述
如果被中斷,則返回 -ERESTARTSYS;如果等待超時,則返回 0;如果成功,則返回 jiffies 中剩餘的超時時間。如果超時時間為零,則如果 fence 已經發出訊號,則返回值 1,以便與其他採用 jiffies 超時的函式保持一致。
-
signed long dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count, bool intr, signed long timeout, uint32_t *idx)¶
睡眠,直到任何 fence 發出訊號或直到超時時間過去
引數
struct dma_fence **fences要等待的 fence 陣列
uint32_t count要等待的 fence 數量
bool intr如果為 true,則進行可中斷的等待
signed long timeoutjiffies 中的超時值,或 MAX_SCHEDULE_TIMEOUT
uint32_t *idx用於儲存第一個發出訊號的 fence 索引,僅在正返回時有意義
描述
如果在自定義 fence 等待實現上,則返回 -EINVAL;如果被中斷,則返回 -ERESTARTSYS;如果等待超時,則返回 0;如果成功,則返回 jiffies 中剩餘的超時時間。
同步等待陣列中第一個發出訊號的 fence。呼叫者需要持有對陣列中所有 fence 的引用,否則 fence 可能會在返回之前被釋放,從而導致未定義的行為。
引數
struct dma_fence *fence要等待的 fence
ktime_t deadline等待者希望 fence 發出訊號的時間
描述
向 fence 傳送者提供有關即將到來的截止日期(例如 vblank)的提示,到那時,等待者希望 fence 發出訊號。 這旨在向 fence 傳送者提供反饋,以幫助進行電源管理決策,例如,如果定期 vblank 截止日期即將到來但 fence 尚未發出訊號,則提高 GPU 頻率。
引數
struct dma_fence *fence要描述的 fence
struct seq_file *seq要將文字描述放入的 seq_file
描述
將 fence 及其狀態的文字描述轉儲到 seq_file 中。
-
void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, spinlock_t *lock, u64 context, u64 seqno)¶
初始化自定義 fence。
引數
struct dma_fence *fence要初始化的 fence
const struct dma_fence_ops *ops用於對此 fence 執行操作的 dma_fence_ops
spinlock_t *lock用於鎖定此 fence 的 irqsafe 自旋鎖
u64 context執行此 fence 的執行上下文
u64 seqno此上下文的線性遞增序列號
描述
初始化已分配的 fence,呼叫者無需在此 fence 提交後保留其引用計數,但如果呼叫了 dma_fence_ops.enable_signaling,則需要再次保留引用計數。
context 和 seqno 用於在 fence 之間輕鬆比較,從而允許透過簡單地使用 dma_fence_later() 來檢查哪個 fence 更晚。
-
struct dma_fence¶
軟體同步原語
定義:
struct dma_fence {
spinlock_t *lock;
const struct dma_fence_ops *ops;
union {
struct list_head cb_list;
ktime_t timestamp;
struct rcu_head rcu;
};
u64 context;
u64 seqno;
unsigned long flags;
struct kref refcount;
int error;
};
成員
lock用於鎖定的 spin_lock_irqsave
ops與此 fence 關聯的 dma_fence_ops
{unnamed_union}匿名
cb_list要呼叫的所有回撥的列表
timestampfence 發出訊號時的時間戳。
rcu用於使用 kfree_rcu 釋放 fence
context此 fence 所屬的執行上下文,由
dma_fence_context_alloc()返回seqno此 fence 在執行上下文中的序列號,可以進行比較以確定哪個 fence 將在稍後發出訊號。
flags下面定義的 DMA_FENCE_FLAG_* 的掩碼
refcount此 fence 的引用計數
error可選,僅當 < 0 時有效,必須在呼叫 dma_fence_signal 之前設定,表示 fence 已完成並出現錯誤。
描述
必須使用適當的原子操作 (bit_*) 來操作和讀取 flags 成員,因此大多數情況下不需要獲取自旋鎖。
DMA_FENCE_FLAG_SIGNALED_BIT - fence 已經發出訊號 DMA_FENCE_FLAG_TIMESTAMP_BIT - 記錄了 fence 信令的時間戳 DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT - 可能已呼叫 enable_signaling DMA_FENCE_FLAG_USER_BITS - 未使用位的開始,可以由 fence 的實現者用於自己的目的。 不同的 fence 實現者可以使用不同的方式,因此不要依賴它。
由於使用了原子位操作,因此不能保證這種情況。 特別是,如果設定了該位,但在設定此位之前立即呼叫了 dma_fence_signal,則它將能夠在呼叫 enable_signaling 之前設定 DMA_FENCE_FLAG_SIGNALED_BIT。 在設定 DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT 之後新增對 DMA_FENCE_FLAG_SIGNALED_BIT 的檢查會關閉此競爭,並確保在呼叫 dma_fence_signal 之後,任何 enable_signaling 呼叫都已完成或根本未呼叫。
-
struct dma_fence_cb¶
定義:
struct dma_fence_cb {
struct list_head node;
dma_fence_func_t func;
};
成員
node由
dma_fence_add_callback()使用以將此結構附加到 fence::cb_listfunc要呼叫的 dma_fence_func_t
描述
此結構將由 dma_fence_add_callback() 初始化,可以透過將 dma_fence_cb 嵌入到另一個結構中來傳遞其他資料。
-
struct dma_fence_ops¶
為 fence 實現的操作
定義:
struct dma_fence_ops {
bool use_64bit_seqno;
const char * (*get_driver_name)(struct dma_fence *fence);
const char * (*get_timeline_name)(struct dma_fence *fence);
bool (*enable_signaling)(struct dma_fence *fence);
bool (*signaled)(struct dma_fence *fence);
signed long (*wait)(struct dma_fence *fence, bool intr, signed long timeout);
void (*release)(struct dma_fence *fence);
void (*set_deadline)(struct dma_fence *fence, ktime_t deadline);
};
成員
use_64bit_seqno如果此 dma_fence 實現使用 64 位 seqno,則為 True,否則為 false。
get_driver_name返回驅動程式名稱。這是一個回撥,允許驅動程式在執行時計算名稱,而無需為每個 fence 永久儲存名稱,或構建某種快取。
此回撥是必需的。
get_timeline_name返回此 fence 所屬的上下文的名稱。這是一個回撥,允許驅動程式在執行時計算名稱,而無需為每個 fence 永久儲存名稱,或構建某種快取。
此回撥是必需的。
enable_signaling啟用 fence 的軟體信令。
對於具有 hw->hw 信令功能的 fence 實現,它們可以實現此操作以啟用必要的 interrupts,或將命令插入到 cmdstream 等中,以避免在僅需要 hw->hw 同步的常見情況下執行這些代價高昂的操作。 這在第一個
dma_fence_wait()或dma_fence_add_callback()路徑中呼叫,以讓 fence 實現知道有另一個驅動程式正在等待訊號(即 hw->sw 情況)。這在停用 irq 的情況下呼叫,因此只有停用 IRQ 的自旋鎖才能在此回撥之外的程式碼中使用。
返回值 false 表示 fence 已經透過,或者發生了某些故障,導致無法啟用信令。 True 表示成功啟用。
可以在 enable_signaling 中設定
dma_fence.error,但僅在返回 false 時。由於許多實現即使在呼叫 enable_signaling 之前也可以呼叫
dma_fence_signal(),因此存在一個競爭視窗,其中dma_fence_signal()可能會導致最終的 fence 引用被釋放,並且其記憶體被釋放。 為了避免這種情況,此回撥的實現應使用dma_fence_get()獲取自己的引用,以便在發出 fence 訊號時(例如,透過中斷處理程式)釋放。此回撥是可選的。 如果不存在此回撥,則驅動程式必須始終啟用信令。
signaled窺視 fence 是否發出訊號,作為例如
dma_fence_wait()或dma_fence_add_callback()的快速路徑最佳化。 請注意,除了指示為已發出訊號的 fence 必須始終從此回撥返回 true 之外,此回撥不需要做出任何保證。 即使 fence 已經完成,此回撥也可能返回 false,在這種情況下,資訊尚未在系統中傳播。 另請參見dma_fence_is_signaled()。如果返回 true,則可以設定
dma_fence.error。此回撥是可選的。
wait自定義等待實現,如果未設定,則預設為
dma_fence_default_wait()。已棄用,不應由新的實現使用。 僅由需要對其硬體重置過程進行特殊處理的現有實現使用。
如果 wait 是 intr = true 並且 wait 被中斷,則必須返回 -ERESTARTSYS;如果 fence 已發出訊號,則返回剩餘的 jiffies;如果等待超時,則返回 0。 也可以在自定義實現上返回其他錯誤值,這些值應被視為 fence 已發出訊號。 例如,硬體鎖定可能會以這種方式報告。
release在銷燬 fence 時呼叫以釋放其他資源。 可以從 irq 上下文中呼叫。 此回撥是可選的。 如果為 NULL,則改為呼叫
dma_fence_free()作為預設實現。set_deadline回撥以允許 fence 等待者通知 fence 信令者即將到來的截止日期,例如 vblank,到那時,等待者希望 fence 發出訊號。 這旨在向 fence 信令者提供反饋,以幫助進行電源管理決策,例如提高 GPU 頻率。
這在沒有持有
dma_fence.lock的情況下呼叫,它可以多次呼叫並且可以從任何上下文中呼叫。 如果 callee 有一些狀態要管理,則鎖定由 callee 決定。 如果設定了多個截止日期,則期望跟蹤最早的截止日期。 如果截止日期早於當前時間,則應將其解釋為立即截止日期。此回撥是可選的。
引數
struct dma_fence *fence要減少引用計數的 fence
引數
struct dma_fence *fence要增加引用計數的 fence
描述
返回相同的 fence,引用計數增加 1。
引數
struct dma_fence *fence要增加引用計數的 fence
描述
如果無法獲得引用計數,則函式返回 NULL 或 fence。
引數
struct dma_fence __rcu **fencep指向要增加引用計數的 fence 的指標
描述
如果無法獲得引用計數,則函式返回 NULL;否則返回 fence。此函式處理獲取對可能在 RCU 寬限期內重新分配的 fence 的引用(例如使用 SLAB_TYPESAFE_BY_RCU),只要呼叫者在指向 fence 的指標上使用 RCU 即可。
另一種機制是使用 seqlock 來保護一組 fence,例如 struct dma_resv 使用的機制。使用 seqlock 時,必須在獲取對 fence 的引用之前獲取 seqlock 並在之後進行檢查(如此處所示)。
呼叫者需要持有 RCU 讀取鎖。
引數
struct dma_fence *fence要檢查的 fence
描述
如果 fence 已經發出訊號,則返回 true,否則返回 false。由於此函式不啟用訊號,因此如果在呼叫 dma_fence_add_callback()、dma_fence_wait() 或 dma_fence_enable_sw_signaling() 之前未呼叫它,則不保證它永遠返回 true。
此函式需要持有 dma_fence.lock。
另請參見 dma_fence_is_signaled()。
引數
struct dma_fence *fence要檢查的 fence
描述
如果 fence 已經發出訊號,則返回 true,否則返回 false。由於此函式不啟用訊號,因此如果在呼叫 dma_fence_add_callback()、dma_fence_wait() 或 dma_fence_enable_sw_signaling() 之前未呼叫它,則不保證它永遠返回 true。
建議 seqno fence 在操作完成時呼叫 dma_fence_signal,這樣可以透過在呼叫特定於硬體的等待指令之前檢查此函式的返回值來防止釋出時間和使用時間之間的環繞問題。
-
bool __dma_fence_is_later(u64 f1, u64 f2, const struct dma_fence_ops *ops)¶
如果 f1 在時間上晚於 f2,則返回 true
引數
u64 f1第一個 fence 的 seqno
u64 f2來自同一上下文的第二個 fence 的 seqno
const struct dma_fence_ops *ops與 seqno 關聯的 dma_fence_ops
描述
如果 f1 在時間上晚於 f2,則返回 true。由於 seqno 在上下文之間不通用,因此兩個 fence 必須來自同一上下文。
引數
struct dma_fence *f1來自同一上下文的第一個 fence
struct dma_fence *f2來自同一上下文的第二個 fence
描述
如果 f1 在時間上晚於 f2,則返回 true。由於 seqno 不會在上下文之間重複使用,因此兩個 fence 必須來自同一上下文。
-
bool dma_fence_is_later_or_same(struct dma_fence *f1, struct dma_fence *f2)¶
如果 f1 晚於或等於 f2,則返回 true
引數
struct dma_fence *f1來自同一上下文的第一個 fence
struct dma_fence *f2來自同一上下文的第二個 fence
描述
如果 f1 在時間上晚於 f2 或與 f2 相同,則返回 true。由於 seqno 不會在上下文之間重複使用,因此兩個 fence 必須來自同一上下文。
引數
struct dma_fence *f1來自同一上下文的第一個 fence
struct dma_fence *f2來自同一上下文的第二個 fence
描述
如果兩個 fence 都已發出訊號,則返回 NULL;否則返回將最後發出訊號的 fence。由於 seqno 不會在上下文之間重複使用,因此兩個 fence 必須來自同一上下文。
引數
struct dma_fence *fence要查詢的 dma_fence
描述
驅動程式可以在發出 fence 訊號之前提供一個可選的錯誤狀態條件(以指示 fence 的完成是由於錯誤而不是成功)。只有在 fence 已發出訊號時,狀態條件的值才有效,dma_fence_get_status_locked() 首先檢查訊號狀態,然後報告錯誤狀態。
如果 fence 尚未發出訊號,則返回 0;如果 fence 已發出訊號且沒有錯誤條件,則返回 1;如果 fence 已在 err 中完成,則返回負錯誤程式碼。
引數
struct dma_fence *fencedma_fence
int error要儲存的錯誤
描述
驅動程式可以在發出 fence 訊號之前提供一個可選的錯誤狀態條件,以指示 fence 的完成是由於錯誤而不是成功。這必須在發出訊號之前設定(以便在喚醒訊號回撥上的任何等待者之前,該值是可見的)。此幫助程式的存在是為了幫助捕獲 #dma_fence.error 的錯誤設定。
驅動程式應使用的錯誤程式碼示例
-ENODATA此操作未生成任何資料,沒有其他操作受到影響。-ECANCELED來自同一上下文的所有操作都已取消。-ETIME操作導致超時,並可能導致裝置重置。
引數
struct dma_fence *fence從中獲取時間戳的 fence。
描述
在發出 fence 訊號後,時間戳會使用訊號時間進行更新,但設定時間戳可能會與等待訊號的任務發生競爭。此幫助程式會忙等待,直到出現正確的時間戳。
引數
struct dma_fence *fence要等待的 fence
bool intr如果為 true,則進行可中斷的等待
描述
如果被訊號中斷,此函式將返回 -ERESTARTSYS;如果 fence 已發出訊號,則返回 0。自定義實現可能會返回其他錯誤值。
對此 fence 執行同步等待。假定呼叫者直接或間接持有對 fence 的引用,否則 fence 可能會在返回之前被釋放,從而導致未定義的行為。
另請參見 dma_fence_wait_timeout() 和 dma_fence_wait_any_timeout()。
引數
struct dma_fence *fence要測試的 fence
描述
如果是 dma_fence_array,則返回 true,否則返回 false。
引數
struct dma_fence *fence要測試的 fence
描述
如果是 dma_fence_chain,則返回 true,否則返回 false。
引數
struct dma_fence *fence要測試的 fence
描述
如果此 fence 是其他 fence 的容器,則返回 true,否則返回 false。這很重要,因為我們不能構建大型 fence 結構,否則在對這些 fence 進行操作時會遇到遞迴。
DMA Fence 陣列¶
-
struct dma_fence_array *dma_fence_array_alloc(int num_fences)¶
分配一個自定義 fence 陣列
引數
int num_fences[in] 要新增到陣列中的 fence 的數量
描述
成功時返回 dma fence 陣列,失敗時返回 NULL
-
void dma_fence_array_init(struct dma_fence_array *array, int num_fences, struct dma_fence **fences, u64 context, unsigned seqno, bool signal_on_any)¶
初始化一個自定義 fence 陣列
引數
struct dma_fence_array *array[in] 要武裝的 dma fence 陣列
int num_fences[in] 要新增到陣列中的 fence 的數量
struct dma_fence **fences[in] 包含 fence 的陣列
u64 context[in] 要使用的 fence 上下文
unsigned seqno[in] 要使用的序列號
bool signal_on_any[in] 如果陣列中的任何 fence 發出訊號,則發出訊號
描述
實現沒有分配的 dma_fence_array_create。可用於在回收或 dma fence 訊號的路徑中初始化預先分配的 dma fence 陣列。
-
struct dma_fence_array *dma_fence_array_create(int num_fences, struct dma_fence **fences, u64 context, unsigned seqno, bool signal_on_any)¶
建立一個自定義 fence 陣列
引數
int num_fences[in] 要新增到陣列中的 fence 的數量
struct dma_fence **fences[in] 包含 fence 的陣列
u64 context[in] 要使用的 fence 上下文
unsigned seqno[in] 要使用的序列號
bool signal_on_any[in] 如果陣列中的任何 fence 發出訊號,則發出訊號
描述
分配一個 dma_fence_array 物件,並使用 dma_fence_init() 初始化基本 fence。如果發生錯誤,則返回 NULL。
呼叫者應使用 num_fences 大小分配 fences 陣列,並使用要新增到物件的 fence 填充它。此陣列的所有權將被獲取,並且在釋放時,會對每個 fence 使用 dma_fence_put()。
如果 signal_on_any 為 true,則如果陣列中的任何 fence 發出訊號,則 fence 陣列會發出訊號;否則,當陣列中的所有 fence 發出訊號時,它會發出訊號。
引數
struct dma_fence *fence[in] fence 或 fence 陣列
u64 context[in] 用於檢查所有 fence 的 fence 上下文
描述
根據給定的上下文檢查提供的 fence,或者對於 fence 陣列,檢查陣列中的所有 fence。如果任何 fence 來自不同的上下文,則返回 false。
-
struct dma_fence_array_cb¶
用於 fence 陣列的回撥幫助程式
定義:
struct dma_fence_array_cb {
struct dma_fence_cb cb;
struct dma_fence_array *array;
};
成員
cb用於訊號的 fence 回撥結構
陣列指向父 fence 陣列物件的引用
-
struct dma_fence_array¶
用於表示 fence 陣列的 fence
定義:
struct dma_fence_array {
struct dma_fence base;
spinlock_t lock;
unsigned num_fences;
atomic_t num_pending;
struct dma_fence **fences;
struct irq_work work;
struct dma_fence_array_cb callbacks[] ;
};
成員
基類fence 基類
lock用於 fence 處理的自旋鎖
num_fences陣列中 fence 的數量
num_pending陣列中仍在掛起的 fence 數量
fencesfence 的陣列
工作項內部 irq_work 函式
回撥回撥助手陣列
-
struct dma_fence_array *to_dma_fence_array(struct dma_fence *fence)¶
將 fence 強制轉換為 dma_fence_array
引數
struct dma_fence *fence要強制轉換為 dma_fence_array 的 fence
描述
如果 fence 不是 dma_fence_array,則返回 NULL;否則返回 dma_fence_array。
-
dma_fence_array_for_each¶
dma_fence_array_for_each (fence, index, head)
迭代陣列中的所有 fence
引數
fence當前的 fence
index陣列中的索引
head潛在的 dma_fence_array 物件
描述
測試 array 是否為 dma_fence_array 物件,如果是,則迭代陣列中的所有 fence。 如果不是,則只迭代 array 自身中的 fence。
有關深度迭代器,請參見 dma_fence_unwrap_for_each()。
DMA Fence 鏈¶
引數
struct dma_fence *fence當前鏈節點
描述
遍歷鏈到下一個節點。如果到達鏈的末尾,則返回下一個 fence 或 NULL。垃圾回收已發出訊號的鏈節點。
引數
struct dma_fence **pfence指向鏈節點開始位置的指標
uint64_t seqno要搜尋的序列號
描述
將 fence 指標提前到將發出此序列號訊號的鏈節點。 如果沒有提供序列號,則這是一個空操作。
如果 fence 不是鏈節點或序列號尚未前進到足夠遠,則返回 EINVAL。
-
void dma_fence_chain_init(struct dma_fence_chain *chain, struct dma_fence *prev, struct dma_fence *fence, uint64_t seqno)¶
初始化 fence 鏈
引數
struct dma_fence_chain *chain要初始化的鏈節點
struct dma_fence *prev先前的 fence
struct dma_fence *fence當前的 fence。
uint64_t seqno用於 fence 鏈的序列號
描述
初始化一個新的鏈節點,並啟動一個新的鏈或將該節點新增到先前 fence 的現有鏈中。
-
struct dma_fence_chain¶
用於表示 fence 鏈節點的 fence
定義:
struct dma_fence_chain {
struct dma_fence base;
struct dma_fence __rcu *prev;
u64 prev_seqno;
struct dma_fence *fence;
union {
struct dma_fence_cb cb;
struct irq_work work;
};
spinlock_t lock;
};
成員
基類fence 基類
prev鏈的先前 fence
prev_seqno垃圾回收前的原始先前 seqno
fence封裝的 fence
{unnamed_union}匿名
cb用於發出訊號的回撥
這用於新增用於發出 fence 鏈完成訊號的回撥。 永遠不要與 irq work 同時使用。
工作項用於發出訊號的 irq work 專案
允許我們添加回調而不遇到鎖反轉的 Irq work 結構。 永遠不要與回撥同時使用。
lock用於 fence 處理的自旋鎖
-
struct dma_fence_chain *to_dma_fence_chain(struct dma_fence *fence)¶
將 fence 強制轉換為 dma_fence_chain
引數
struct dma_fence *fence要強制轉換為 dma_fence_array 的 fence
描述
如果 fence 不是 dma_fence_chain,則返回 NULL;否則返回 dma_fence_chain。
引數
struct dma_fence *fence要測試的 fence
描述
如果 fence 是 dma_fence_chain,則該函式返回包含在鏈物件內的 fence,否則返回 fence 本身。
-
dma_fence_chain_alloc¶
dma_fence_chain_alloc ()
描述
返回一個新的
struct dma_fence_chain物件,如果失敗則返回 NULL。這個專門的分配器必須是一個宏,以便其分配可以單獨計算(具有單獨的 alloc_tag)。 型別轉換是有意強制執行型別安全的。
-
void dma_fence_chain_free(struct dma_fence_chain *chain)¶
引數
struct dma_fence_chain *chain要釋放的鏈節點
描述
釋放已分配但未使用的 struct dma_fence_chain 物件。 這不需要 RCU 寬限期,因為 fence 從未初始化或釋出。 在呼叫 dma_fence_chain_init() 之後,必須透過呼叫 dma_fence_put() 來釋放 fence,而不是透過此函式。
-
dma_fence_chain_for_each¶
dma_fence_chain_for_each (iter, head)
迭代鏈中的所有 fence
引數
iter當前的 fence
head起點
描述
迭代鏈中的所有 fence。 我們在迴圈內保持對當前 fence 的引用,該引用在退出時必須刪除。
有關深度迭代器,請參見 dma_fence_unwrap_for_each()。
DMA Fence 解包¶
-
struct dma_fence_unwrap¶
容器結構中的遊標
定義:
struct dma_fence_unwrap {
struct dma_fence *chain;
struct dma_fence *array;
unsigned int index;
};
成員
chain潛在的 dma_fence_chain,但也可以是其他 fence
陣列潛在的 dma_fence_array,但也可以是其他 fence
index如果 array 確實是一個 dma_fence_array,則最後返回的索引
描述
應與 dma_fence_unwrap_for_each() 迭代器宏一起使用。
-
dma_fence_unwrap_for_each¶
dma_fence_unwrap_for_each (fence, cursor, head)
迭代容器中的所有 fence
引數
fence當前的 fence
游標。容器內的當前位置
head迭代器的起點
描述
解包 dma_fence_chain 和 dma_fence_array 容器,並深入到其中的所有潛在 fence 中。 如果 head 只是一個普通的 fence,則只返回該 fence。
-
dma_fence_unwrap_merge¶
dma_fence_unwrap_merge (...)
解包併合並 fence
引數
...可變引數
描述
作為引數給出的所有 fence 都被解包並作為平面 dma_fence_array 合併回一起。 如果需要將多個容器合併在一起,則很有用。
實現為一個宏,用於在堆疊上分配必要的陣列,並將堆疊幀大小計算到呼叫方。
如果記憶體分配失敗,則返回 NULL;否則返回表示所有給定 fence 的 dma_fence 物件。
DMA Fence 同步檔案¶
引數
struct dma_fence *fence要新增到 sync_fence 的 fence
描述
建立一個包含 fence 的 sync_file。 如果成功,此函式會為新建立的 sync_file 獲取 fence 的附加引用。 可以使用 fput(sync_file->file) 釋放 sync_file。 返回 sync_file,如果出錯則返回 NULL。
引數
int fd從中獲取 fence 的 sync_file fd
描述
確保 fd 引用一個有效的 sync_file,並返回一個表示 sync_file 中所有 fence 的 fence。 如果出錯,則返回 NULL。
-
struct sync_file¶
要匯出到使用者空間的同步檔案
定義:
struct sync_file {
struct file *file;
char user_name[32];
#ifdef CONFIG_DEBUG_FS;
struct list_head sync_file_list;
#endif;
wait_queue_head_t wq;
unsigned long flags;
struct dma_fence *fence;
struct dma_fence_cb cb;
};
成員
file表示此 fence 的檔案
user_name使用者空間提供的同步檔案的名稱,用於合併的 fence。 否則透過驅動程式回撥生成(在這種情況下,整個陣列為 0)。
sync_file_list全域性檔案列表中的成員資格
wq用於 fence 訊號的等待佇列
flags同步檔案的標誌
fence帶有 sync_file 中的 fence 的 fence
cbfence 回撥資訊
描述
標誌:POLL_ENABLED:使用者空間當前是否正在 poll()
DMA Fence 同步檔案 uABI¶
-
struct sync_merge_data¶
SYNC_IOC_MERGE:合併兩個 fence
定義:
struct sync_merge_data {
char name[32];
__s32 fd2;
__s32 fence;
__u32 flags;
__u32 pad;
};
成員
name新 fence 的名稱
fd2第二個 fence 的檔案描述符
fence將新 fence 的 fd 返回給使用者空間
flagsmerge_data 標誌
pad用於 64 位對齊的填充,應始終為零
描述
建立一個新 fence,其中包含呼叫 fd 和 sync_merge_data.fd2 中的 sync_pts 的副本。 在 sync_merge_data.fence 中返回新 fence 的 fd
-
struct sync_fence_info¶
詳細的 fence 資訊
定義:
struct sync_fence_info {
char obj_name[32];
char driver_name[32];
__s32 status;
__u32 flags;
__u64 timestamp_ns;
};
成員
obj_name父 sync_timeline 的名稱
driver_name實現父物件的驅動程式的名稱
statusfence 的狀態 0:active 1:signaled <0:error
flagsfence_info 標誌
timestamp_ns狀態更改的時間戳(以納秒為單位)
-
struct sync_file_info¶
SYNC_IOC_FILE_INFO:獲取有關 sync_file 的詳細資訊
定義:
struct sync_file_info {
char name[32];
__s32 status;
__u32 flags;
__u32 num_fences;
__u32 pad;
__u64 sync_fence_info;
};
成員
namefence 的名稱
statusfence 的狀態。 1: signaled 0:active <0:error
flagssync_file_info 標誌
num_fencessync_file 中的 fence 數量
pad用於 64 位對齊的填充,應始終為零
sync_fence_info指向 struct
sync_fence_info陣列的指標,其中包含 sync_file 中的所有 fence
描述
採用 struct sync_file_info。 如果 num_fences 為 0,則該欄位將更新為實際的 fence 數量。 如果 num_fences > 0,則系統將使用 sync_fence_info 上提供的指標來返回最多 num_fences 個 struct sync_fence_info,其中包含詳細的 fence 資訊。
-
struct sync_set_deadline¶
SYNC_IOC_SET_DEADLINE - 在 fence 上設定截止時間提示
定義:
struct sync_set_deadline {
__u64 deadline_ns;
__u64 pad;
};
成員
deadline_ns截止時間的絕對時間
pad必須為零
描述
允許使用者空間在 fence 上設定截止時間,請參見 dma_fence_set_deadline
截止時間的時間基準為 CLOCK_MONOTONIC(與 vblank 相同)。 例如
clock_gettime(CLOCK_MONOTONIC,
t); deadline_ns = (t.tv_sec * 1000000000L) + t.tv_nsec + ns_until_deadline
無限 DMA Fence¶
在不同的時間,已經提出了 struct dma_fence,它具有無限的時間,直到 dma_fence_wait() 完成。 示例包括
Future fence,在 HWC1 中用於發出訊號,表明緩衝區不再被顯示器使用,並且使用使緩衝區可見的螢幕更新建立。 此 fence 完成的時間完全由使用者空間控制。
代理 fence,用於處理尚未設定 fence 的 &drm_syncobj。 用於非同步延遲命令提交。
使用者空間 fence 或 gpu futex,命令緩衝區內的細粒度鎖定,使用者空間使用它來進行跨引擎或與 CPU 的同步,然後將其作為 DMA fence 匯入,以便整合到現有的 winsys 協議中。
長時間執行的計算命令緩衝區,同時仍然使用傳統的批處理結束 DMA fence 進行記憶體管理,而不是在重新安排計算作業時重新附加的上下文搶佔 DMA fence。
所有這些方案的共同點是使用者空間控制這些 fence 的依賴關係,並控制它們何時觸發。 將無限 fence 與正常的核心 DMA fence 混合使用不起作用,即使包含回退超時以防止惡意使用者空間也是如此
只有核心知道所有 DMA fence 依賴關係,使用者空間不知道由於記憶體管理或排程程式決策而注入的依賴關係。
只有使用者空間知道無限 fence 中的所有依賴關係以及它們何時準確完成,核心沒有可見性。
此外,核心必須能夠為記憶體管理需求阻止使用者空間命令提交,這意味著我們必須支援依賴於 DMA fence 的無限 fence。 如果核心還支援核心中的無限 fence,就像上述任何提案一樣,則存在死鎖的可能性。
無限 Fencing 依賴迴圈¶
這意味著核心可能會意外地透過使用者空間不知道的記憶體管理依賴關係建立死鎖,從而隨機掛起工作負載,直到超時生效。從使用者空間的角度來看,工作負載不包含死鎖。在這種混合 fencing 架構中,沒有一個實體瞭解所有依賴關係。因此,無法從核心內部阻止此類死鎖。
避免依賴迴圈的唯一方法是不允許核心中的無限 fence。這意味著
沒有未來的 fence、代理 fence 或匯入為 DMA fence 的使用者空間 fence,無論是否設定超時。
沒有 DMA fence 用於標記命令提交的批處理緩衝區結束,而使用者空間允許使用使用者空間 fencing 或長時間執行的計算工作負載。 這也意味著在這些情況下,不允許共享緩衝區的隱式 fencing。
可恢復的硬體頁面錯誤的影響¶
現代硬體支援可恢復的頁面錯誤,這對 DMA fence 產生了許多影響。
首先,待處理的頁面錯誤顯然會阻止加速器上正在執行的工作,並且通常需要記憶體分配來解決該錯誤。但是,不允許記憶體分配阻止 DMA fence 的完成,這意味著任何使用可恢復頁面錯誤的工作負載都不能使用 DMA fence 進行同步。必須改用由使用者空間控制的同步 fence。
在 GPU 上,這帶來了一個問題,因為 Linux 上當前的桌面合成器協議依賴於 DMA fence,這意味著如果沒有構建在使用者空間 fence 之上的全新使用者空間堆疊,它們就無法從可恢復的頁面錯誤中受益。具體來說,這意味著不可能進行隱式同步。唯一的例外是頁面錯誤僅用作遷移提示,而從不用作按需填充記憶體請求。目前,這意味著 GPU 上的可恢復頁面錯誤僅限於純計算工作負載。
此外,GPU 通常在 3D 渲染和計算端之間共享資源,例如計算單元或命令提交引擎。如果帶有 DMA fence 的 3D 作業和使用可恢復頁面錯誤的計算工作負載都處於掛起狀態,則它們可能會死鎖
3D 工作負載可能需要等待計算作業完成並首先釋放硬體資源。
計算工作負載可能被卡在頁面錯誤中,因為記憶體分配正在等待 3D 工作負載的 DMA fence 完成。
有幾種方法可以避免此問題,其中驅動程式需要確保
計算工作負載始終可以被搶佔,即使頁面錯誤處於掛起狀態且尚未修復。並非所有硬體都支援此功能。
DMA fence 工作負載和需要頁面錯誤處理的工作負載具有獨立的硬體資源,以保證向前進展。這可以透過例如透過專用引擎和 DMA fence 工作負載的最小計算單元預留來實現。
可以透過僅在 DMA fence 工作負載處於活動狀態時才為其預留硬體資源來進一步改進預留方法。這必須涵蓋從 DMA fence 對其他執行緒可見到透過
dma_fence_signal()完成 fence 的時間。作為最後的手段,如果硬體不提供有用的預留機制,則在需要 DMA fence 的作業或需要頁面錯誤處理的作業之間切換時,必須從 GPU 中清除所有工作負載:這意味著在可以將帶有頁面錯誤處理的計算作業插入排程程式佇列之前,必須完成所有 DMA fence。反之亦然,在 DMA fence 可以在系統的任何位置可見之前,必須搶佔所有計算工作負載,以保證清除所有掛起的 GPU 頁面錯誤。
一個相當理論的選擇是在分配記憶體以修復硬體頁面錯誤時,解開這些依賴關係,無論是透過單獨的記憶體塊還是執行時跟蹤所有 DMA fence 的完整依賴關係圖。這對核心產生了非常廣泛的影響,因為在 CPU 端解決頁面的問題本身可能涉及頁面錯誤。將硬體頁面錯誤處理的影響限制在特定驅動程式中,更可行且更可靠。
請注意,在獨立硬體(如複製引擎或其他 GPU)上執行的工作負載沒有任何影響。這允許我們即使在解決硬體頁面錯誤時,也可以在核心中內部使用 DMA fence,例如,透過使用複製引擎來清除或複製解決頁面錯誤所需的記憶體。
在某些方面,這個頁面錯誤問題是 無限 DMA Fence 討論的一個特殊情況:允許來自計算工作負載的無限 fence 依賴於 DMA fence,但反之則不然。甚至頁面錯誤問題也不是什麼新鮮事,因為使用者空間中的一些其他 CPU 執行緒可能會遇到頁面錯誤,這會阻止使用者空間 fence - 在 GPU 上支援頁面錯誤並沒有帶來任何根本性的新東西。