VM_BIND 鎖定

本文件試圖描述正確進行 VM_BIND 鎖定所需的條件,包括 userptr mmu_notifier 鎖定。它還討論了一些最佳化,以消除最簡單實現中需要的對所有 userptr 對映和外部/共享物件對映的迴圈。此外,還有一個章節描述了實現可恢復頁面錯誤的 VM_BIND 鎖定。

DRM GPUVM 助手集

有一組助手用於實現 VM_BIND 的驅動程式,這組助手實現了大部分(但不是全部)本文件中描述的鎖定。特別是,它目前缺少 userptr 實現。本文件不打算詳細描述 DRM GPUVM 實現,但它包含在 其自身的文件中。強烈建議任何實現 VM_BIND 的驅動程式使用 DRM GPUVM 助手,並在缺少通用功能時對其進行擴充套件。

命名約定

  • gpu_vm:具有元資料的虛擬 GPU 地址空間的抽象。通常每個客戶端(DRM 檔案私有)一個,或者每個執行上下文一個。

  • gpu_vma:gpu_vm 中具有關聯元資料的 GPU 地址範圍的抽象。gpu_vma 的後備儲存可以是 GEM 物件、匿名頁面或頁面快取頁面,這些頁面也對映到程序的 CPU 地址空間中。

  • gpu_vm_bo:抽象了 GEM 物件和 VM 的關聯。GEM 物件維護一個 gpu_vm_bo 列表,其中每個 gpu_vm_bo 維護一個 gpu_vma 列表。

  • userptr gpu_vma 或者僅僅是 userptr:一個 gpu_vma,其後備儲存是上述的匿名頁面或頁面快取頁面。

  • revalidating:重新驗證 gpu_vma 意味著使後備儲存的最新版本駐留,並確保 gpu_vma 的頁表條目指向該後備儲存。

  • dma_fence:類似於結構 completion 的 struct dma_fence,用於跟蹤 GPU 活動。當 GPU 活動完成時,dma_fence 會發出訊號。請參考 dma-buf 文件DMA Fences 部分。

  • dma_resv:一個 struct dma_resv(又名預留物件),用於以 gpu_vm 或 GEM 物件上的多個 dma_fence 的形式跟蹤 GPU 活動。dma_resv 包含 dma_fence 的陣列/列表和一個鎖,該鎖需要在將額外的 dma_fence 新增到 dma_resv 時持有。該鎖的型別允許以任意順序對多個 dma_resv 進行死鎖安全鎖定。請參考 dma-buf 文件Reservation Objects 部分。

  • exec 函式:exec 函式是一個重新驗證所有受影響的 gpu_vma、提交 GPU 命令批處理並將表示 GPU 命令活動的 dma_fence 註冊到所有受影響的 dma_resv 的函式。為了完整起見,雖然本文件未涵蓋,但值得一提的是,exec 函式也可能是某些驅動程式在計算/長時間執行模式下使用的重新驗證工作者。

  • local object:一個僅在單個 VM 中對映的 GEM 物件。本地 GEM 物件共享 gpu_vm 的 dma_resv。

  • external object:又名共享物件:一個可以被多個 gpu_vm 共享並且其後備儲存可以與其他驅動程式共享的 GEM 物件。

鎖和鎖定順序

VM_BIND 的一個好處是,本地 GEM 物件共享 gpu_vm 的 dma_resv 物件,從而共享 dma_resv 鎖。因此,即使有大量的本地 GEM 物件,也只需要一個鎖即可使 exec 序列成為原子操作。

使用以下鎖和鎖定順序

  • gpu_vm->lock(可選,一個 rwsem)。保護 gpu_vm 的資料結構,該資料結構跟蹤 gpu_vma。它還可以保護 gpu_vm 的 userptr gpu_vma 列表。如果用 CPU mm 類比,這將對應於 mmap_lock。rwsem 允許幾個讀者同時遍歷 VM 樹,但是該併發的好處很可能因驅動程式而異。

  • userptr_seqlock。在讀取模式下,對於 gpu_vm 的 userptr 列表中的每個 userptr gpu_vma 都會獲取此鎖;在寫入模式下,在 mmu 通知程式失效期間獲取此鎖。這不是真正的 seqlock,但在 mm/mmu_notifier.c 中描述為“類似於 seqcount 的衝突重試讀/寫側‘鎖’。但是,這允許多個寫側同時持有它...”。讀側臨界區由 mmu_interval_read_begin() / mmu_interval_read_retry() 包圍,如果持有寫側,則 mmu_interval_read_begin() 會休眠。核心 mm 在呼叫 mmu 區間失效通知程式時持有寫側。

  • gpu_vm->resv 鎖。保護 gpu_vm 的需要重新繫結的 gpu_vma 列表,以及所有 gpu_vm 的本地 GEM 物件的駐留狀態。此外,它通常保護 gpu_vm 的已逐出和外部 GEM 物件列表。

  • gpu_vm->userptr_notifier_lock。這是一個 rwsem,在 exec 期間以讀取模式獲取,在 mmu 通知程式失效期間以寫入模式獲取。userptr 通知程式鎖是每個 gpu_vm 的。

  • gem_object->gpuva_lock 此鎖保護 GEM 物件的 gpu_vm_bo 列表。這通常與 GEM 物件的 dma_resv 鎖相同,但是某些驅動程式以不同的方式保護此列表,請參見下文。

  • gpu_vm 列表 自旋鎖。對於某些實現,需要它們才能更新 gpu_vm 的已逐出物件和外部物件列表。對於這些實現,在操作列表時會獲取自旋鎖。但是,為了避免與 dma_resv 鎖發生鎖定順序衝突,在遍歷列表時需要特殊的方案。

gpu_vm_bo 和 gpu_vma 的保護和生命週期

GEM 物件的 gpu_vm_bo 列表和 gpu_vm_bo 的 gpu_vma 列表受 gem_object->gpuva_lock 保護,它通常與 GEM 物件的 dma_resv 相同,但是如果驅動程式需要從 dma_fence 訊號臨界區內訪問這些列表,它可以選擇使用單獨的鎖來保護它,該鎖可以從 dma_fence 訊號臨界區內鎖定。然後,此類驅動程式需要額外注意從迴圈內獲取哪些鎖,以避免在遍歷 gpu_vm_bo 和 gpu_vma 列表時發生鎖定順序衝突。

DRM GPUVM 助手集提供了 lockdep 斷言,以確保在相關情況下持有此鎖,並且還提供了一種方法,使其可以知道實際使用了哪個鎖:drm_gem_gpuva_set_lock()

每個 gpu_vm_bo 都持有一個指向底層 GEM 物件的引用計數指標,並且每個 gpu_vma 都持有一個指向 gpu_vm_bo 的引用計數指標。在遍歷 GEM 物件的 gpu_vm_bo 列表和 gpu_vm_bo 的 gpu_vma 列表時,不得釋放 gem_object->gpuva_lock,否則,附加到 gpu_vm_bo 的 gpu_vma 可能會在沒有通知的情況下消失,因為它們不是引用計數的。驅動程式可以實現自己的方案來允許這樣做,但代價是增加了複雜性,但這不在本文件的範圍內。

在 DRM GPUVM 實現中,每個 gpu_vm_bo 和每個 gpu_vma 都持有對 gpu_vm 自身的引用計數。因此,並且為了避免迴圈引用計數,不得從 gpu_vm 的解構函式中完成 gpu_vm 的 gpu_vma 的清理。驅動程式通常實現一個 gpu_vm 關閉函式來進行此清理。gpu_vm 關閉函式將中止使用此 VM 的 gpu 執行,取消對映所有 gpu_vma 並釋放頁表記憶體。

本地物件的重新驗證和驅逐

請注意,在下面給出的所有程式碼示例中,我們使用簡化的虛擬碼。特別是,省略了 dma_resv 死鎖避免演算法以及為 dma_resv fences 保留記憶體。

重新驗證

對於 VM_BIND,當 gpu 使用 gpu_vm 執行時,所有本地物件都需要駐留,並且這些物件需要設定有效的 gpu_vma 指向它們。因此,通常每個 gpu 命令緩衝區提交之前都有一個重新驗證部分

dma_resv_lock(gpu_vm->resv);

// Validation section starts here.
for_each_gpu_vm_bo_on_evict_list(&gpu_vm->evict_list, &gpu_vm_bo) {
        validate_gem_bo(&gpu_vm_bo->gem_bo);

        // The following list iteration needs the Gem object's
        // dma_resv to be held (it protects the gpu_vm_bo's list of
        // gpu_vmas, but since local gem objects share the gpu_vm's
        // dma_resv, it is already held at this point.
        for_each_gpu_vma_of_gpu_vm_bo(&gpu_vm_bo, &gpu_vma)
               move_gpu_vma_to_rebind_list(&gpu_vma, &gpu_vm->rebind_list);
}

for_each_gpu_vma_on_rebind_list(&gpu vm->rebind_list, &gpu_vma) {
        rebind_gpu_vma(&gpu_vma);
        remove_gpu_vma_from_rebind_list(&gpu_vma);
}
// Validation section ends here, and job submission starts.

add_dependencies(&gpu_job, &gpu_vm->resv);
job_dma_fence = gpu_submit(&gpu_job));

add_dma_fence(job_dma_fence, &gpu_vm->resv);
dma_resv_unlock(gpu_vm->resv);

擁有單獨的 gpu_vm 重新繫結列表的原因是,可能存在未對映緩衝區物件的 userptr gpu_vma,也需要重新繫結。

驅逐

然後,驅逐這些本地物件之一將類似於以下內容

obj = get_object_from_lru();

dma_resv_lock(obj->resv);
for_each_gpu_vm_bo_of_obj(obj, &gpu_vm_bo);
        add_gpu_vm_bo_to_evict_list(&gpu_vm_bo, &gpu_vm->evict_list);

add_dependencies(&eviction_job, &obj->resv);
job_dma_fence = gpu_submit(&eviction_job);
add_dma_fence(&obj->resv, job_dma_fence);

dma_resv_unlock(&obj->resv);
put_object(obj);

請注意,由於該物件是 gpu_vm 本地的,因此它將共享 gpu_vm 的 dma_resv 鎖,例如 obj->resv == gpu_vm->resv。標記為驅逐的 gpu_vm_bo 會放置在 gpu_vm 的驅逐列表上,該列表受 gpu_vm->resv 保護。在驅逐期間,所有本地物件都會鎖定其 dma_resv,並且由於上述等式,也會鎖定保護 gpu_vm 的驅逐列表的 gpu_vm 的 dma_resv。

對於 VM_BIND,無需在驅逐之前取消繫結 gpu_vma,因為驅動程式必須確保驅逐 blit 或 copy 將等待 GPU 空閒或依賴於所有先前的 GPU 活動。此外,GPU 隨後嘗試透過 gpu_vma 訪問釋放的記憶體的任何嘗試都將以新的 exec 函式開頭,其中包含重新驗證部分,該部分將確保所有 gpu_vma 都已重新繫結。在重新驗證時持有物件 dma_resv 的驅逐程式碼將確保新的 exec 函式不會與驅逐競爭。

驅動程式可以以這樣一種方式實現:在每個 exec 函式上,僅選擇 vma 的子集進行重新繫結。在這種情況下,所有選擇用於重新繫結的 vma 都必須在提交 exec 函式工作負載之前取消繫結。

使用外部緩衝區物件進行鎖定

由於外部緩衝區物件可以被多個 gpu_vm 共享,因此它們無法與其單個 gpu_vm 共享其預留物件。相反,它們需要擁有自己的預留物件。因此,使用一個或多個 gpu_vma 繫結到 gpu_vm 的外部物件將放置在每個 gpu_vm 的列表上,該列表受 gpu_vm 的 dma_resv 鎖或 gpu_vm 列表自旋鎖之一保護。鎖定 gpu_vm 的預留物件後,可以安全地遍歷外部物件列表並鎖定所有外部物件的 dma_resv。但是,如果改為使用列表自旋鎖,則需要使用更精細的迭代方案。

在驅逐時,需要將外部物件繫結到的所有 gpu_vm 的 gpu_vm_bo 放置在其 gpu_vm 的驅逐列表上。但是,在驅逐外部物件時,通常不會持有該物件繫結到的 gpu_vm 的 dma_resv。只能保證持有該物件的私有 dma_resv。如果在驅逐時有 ww_acquire 上下文可用,我們可以獲取這些 dma_resv,但這可能會導致昂貴的 ww_mutex 回滾。一個簡單的選擇是僅使用一個 evicted bool 標記被驅逐的 gem 物件的 gpu_vm_bo,該 bool 在下次需要遍歷相應的 gpu_vm 驅逐列表之前進行檢查。例如,在遍歷外部物件列表並鎖定它們時。那時,將同時持有 gpu_vm 的 dma_resv 和該物件的 dma_resv,然後可以將標記為已驅逐的 gpu_vm_bo 新增到 gpu_vm 的已驅逐 gpu_vm_bo 列表中。evicted bool 受該物件的 dma_resv 正式保護。

exec 函式變為

dma_resv_lock(gpu_vm->resv);

// External object list is protected by the gpu_vm->resv lock.
for_each_gpu_vm_bo_on_extobj_list(gpu_vm, &gpu_vm_bo) {
        dma_resv_lock(gpu_vm_bo.gem_obj->resv);
        if (gpu_vm_bo_marked_evicted(&gpu_vm_bo))
                add_gpu_vm_bo_to_evict_list(&gpu_vm_bo, &gpu_vm->evict_list);
}

for_each_gpu_vm_bo_on_evict_list(&gpu_vm->evict_list, &gpu_vm_bo) {
        validate_gem_bo(&gpu_vm_bo->gem_bo);

        for_each_gpu_vma_of_gpu_vm_bo(&gpu_vm_bo, &gpu_vma)
               move_gpu_vma_to_rebind_list(&gpu_vma, &gpu_vm->rebind_list);
}

for_each_gpu_vma_on_rebind_list(&gpu vm->rebind_list, &gpu_vma) {
        rebind_gpu_vma(&gpu_vma);
        remove_gpu_vma_from_rebind_list(&gpu_vma);
}

add_dependencies(&gpu_job, &gpu_vm->resv);
job_dma_fence = gpu_submit(&gpu_job));

add_dma_fence(job_dma_fence, &gpu_vm->resv);
for_each_external_obj(gpu_vm, &obj)
       add_dma_fence(job_dma_fence, &obj->resv);
dma_resv_unlock_all_resv_locks();

相應的共享物件感知驅逐將如下所示

obj = get_object_from_lru();

dma_resv_lock(obj->resv);
for_each_gpu_vm_bo_of_obj(obj, &gpu_vm_bo)
        if (object_is_vm_local(obj))
             add_gpu_vm_bo_to_evict_list(&gpu_vm_bo, &gpu_vm->evict_list);
        else
             mark_gpu_vm_bo_evicted(&gpu_vm_bo);

add_dependencies(&eviction_job, &obj->resv);
job_dma_fence = gpu_submit(&eviction_job);
add_dma_fence(&obj->resv, job_dma_fence);

dma_resv_unlock(&obj->resv);
put_object(obj);

在未持有 dma_resv 鎖的情況下訪問 gpu_vm 的列表

某些驅動程式在訪問 gpu_vm 的驅逐列表和外部物件列表時將持有 gpu_vm 的 dma_resv 鎖。但是,有些驅動程式需要在未持有 dma_resv 鎖的情況下訪問這些列表,例如由於來自 dma_fence 訊號臨界區內的非同步狀態更新。在這種情況下,可以使用自旋鎖來保護對列表的操作。但是,由於在遍歷列表時需要為每個列表項獲取更高級別的休眠鎖,因此需要將已遍歷的項臨時移動到私有列表,並在處理每個項時釋放自旋鎖

由於額外的鎖定和原子操作,可以避免在 dma_resv 鎖外部訪問 gpu_vm 的列表的驅動程式可能還希望避免使用此迭代方案。特別是,如果驅動程式預計會有大量的列表項。對於預計列表項數量較少、列表迭代不經常發生或者與每次迭代相關的額外成本非常高的列表,與此類迭代相關的原子操作開銷很可能可以忽略不計。請注意,如果使用此方案,則必須確保此列表迭代受外部級別的鎖或訊號量保護,因為在迭代時會暫時從列表中刪除列表項,並且還值得一提的是,本地列表 still_in_list 也應被視為受 gpu_vm->list_lock 保護,因此列表項也可能在與列表迭代同時從本地列表中刪除。

請參考 DRM GPUVM 鎖定部分及其內部的 get_next_vm_bo_from_list() 函式。

userptr gpu_vma

userptr gpu_vma 是一個 gpu_vma,它不是將緩衝區物件對映到 GPU 虛擬地址範圍,而是直接對映 CPU mm 的匿名或檔案頁面快取頁面範圍。一個非常簡單的方法是在繫結時使用 pin_user_pages() 鎖定頁面,並在取消繫結時取消鎖定它們,但是這會建立一個拒絕服務向量,因為單個使用者空間程序將能夠鎖定所有系統記憶體,這是不可取的。(對於特殊用例並假設正確的核算,鎖定可能仍然是理想的功能,儘管如此)。在一般情況下,我們需要做的是獲取對所需頁面的引用,確保在 CPU mm 取消對映頁面之前使用 MMU 通知程式通知我們,如果這些頁面未以只讀方式對映到 GPU,則將它們標記為髒頁,然後刪除引用。當我們收到 MMU 通知程式通知 CPU mm 將要刪除頁面時,我們需要透過等待 MMU 通知程式中的 VM 空閒來停止 GPU 對頁面的訪問,並確保在 GPU 下次嘗試訪問 CPU mm 範圍中當前存在的任何內容之前,我們從 GPU 頁表中取消對映舊頁面並重復獲取新頁面引用的過程。(請參見下面的 通知程式示例)。請注意,當核心 mm 決定清洗頁面時,我們會收到這樣的取消對映 MMU 通知,並且可以在下次 GPU 訪問之前再次將頁面標記為髒頁。我們還收到類似的 NUMA 核算的 MMU 通知,GPU 驅動程式實際上不需要關心,但是到目前為止,事實證明很難排除某些通知。

pin_user_pages() 文件中描述了使用 MMU 通知程式進行裝置 DMA(和其他方法)。

現在,不幸的是,不能在 dma_resv 鎖下使用使用 get_user_pages() 獲取 struct page 引用的方法,因為這會違反 dma_resv 鎖與在解析 CPU 頁面錯誤時獲取的 mmap_lock 的鎖定順序。這意味著 gpu_vm 的 userptr gpu_vma 列表需要由外部鎖保護,在我們的示例中,它是 gpu_vm->lock

userptr gpu_vma 的 MMU 區間 seqlock 的使用方式如下

// Exclusive locking mode here is strictly needed only if there are
// invalidated userptr gpu_vmas present, to avoid concurrent userptr
// revalidations of the same userptr gpu_vma.
down_write(&gpu_vm->lock);
retry:

// Note: mmu_interval_read_begin() blocks until there is no
// invalidation notifier running anymore.
seq = mmu_interval_read_begin(&gpu_vma->userptr_interval);
if (seq != gpu_vma->saved_seq) {
        obtain_new_page_pointers(&gpu_vma);
        dma_resv_lock(&gpu_vm->resv);
        add_gpu_vma_to_revalidate_list(&gpu_vma, &gpu_vm);
        dma_resv_unlock(&gpu_vm->resv);
        gpu_vma->saved_seq = seq;
}

// The usual revalidation goes here.

// Final userptr sequence validation may not happen before the
// submission dma_fence is added to the gpu_vm's resv, from the POW
// of the MMU invalidation notifier. Hence the
// userptr_notifier_lock that will make them appear atomic.

add_dependencies(&gpu_job, &gpu_vm->resv);
down_read(&gpu_vm->userptr_notifier_lock);
if (mmu_interval_read_retry(&gpu_vma->userptr_interval, gpu_vma->saved_seq)) {
       up_read(&gpu_vm->userptr_notifier_lock);
       goto retry;
}

job_dma_fence = gpu_submit(&gpu_job));

add_dma_fence(job_dma_fence, &gpu_vm->resv);

for_each_external_obj(gpu_vm, &obj)
       add_dma_fence(job_dma_fence, &obj->resv);

dma_resv_unlock_all_resv_locks();
up_read(&gpu_vm->userptr_notifier_lock);
up_write(&gpu_vm->lock);

mmu_interval_read_begin()mmu_interval_read_retry() 之間的程式碼標記了我們所謂的 userptr_seqlock 的讀側臨界區。實際上,gpu_vm 的 userptr gpu_vma 列表會被迴圈訪問,並且會檢查其所有的 userptr gpu_vma,儘管我們在此處僅顯示一個。

可能會從回收上下文中呼叫 userptr gpu_vma MMU 失效通知程式,並且同樣為了避免鎖定順序衝突,我們不能從中獲取任何 dma_resv 鎖或 gpu_vm->lock。

bool gpu_vma_userptr_invalidate(userptr_interval, cur_seq)
{
        // Make sure the exec function either sees the new sequence
        // and backs off or we wait for the dma-fence:

        down_write(&gpu_vm->userptr_notifier_lock);
        mmu_interval_set_seq(userptr_interval, cur_seq);
        up_write(&gpu_vm->userptr_notifier_lock);

        // At this point, the exec function can't succeed in
        // submitting a new job, because cur_seq is an invalid
        // sequence number and will always cause a retry. When all
        // invalidation callbacks, the mmu notifier core will flip
        // the sequence number to a valid one. However we need to
        // stop gpu access to the old pages here.

        dma_resv_wait_timeout(&gpu_vm->resv, DMA_RESV_USAGE_BOOKKEEP,
                              false, MAX_SCHEDULE_TIMEOUT);
        return true;
}

當此失效通知程式返回時,GPU 不能再訪問 userptr gpu_vma 的舊頁面,並且需要在新的 GPU 提交成功之前重做頁面繫結。

高效的 userptr gpu_vma exec_function 迭代

如果 gpu_vm 的 userptr gpu_vma 列表變得很大,則在每個 exec 函式上迭代整個 userptr 列表以檢查每個 userptr gpu_vma 的已儲存序列號是否過時效率低下。一種解決方案是將所有失效的 userptr gpu_vma 放置在單獨的 gpu_vm 列表上,並且僅在每個 exec 函式上檢查此列表上存在的 gpu_vma。然後,此列表非常適合於 自旋鎖迭代部分中描述的自旋鎖鎖定方案,因為在 mmu 通知程式中,我們在其中將失效的 gpu_vma 新增到列表,無法獲取任何外部鎖,例如 gpu_vm->lockgpu_vm->resv 鎖。請注意,如該部分中所述,在迭代時仍然需要獲取 gpu_vm->lock 以確保列表完整。

如果像這樣使用失效的 userptr 列表,則 exec 函式中的重試檢查會簡單地變成檢查失效列表是否為空。

繫結和取消繫結時的鎖定

在繫結時,假設一個 GEM 物件支援的 gpu_vma,每個 gpu_vma 需要與一個 gpu_vm_bo 相關聯,並且該 gpu_vm_bo 依次需要新增到 GEM 物件的 gpu_vm_bo 列表,並且可能新增到 gpu_vm 的外部物件列表。這被稱為連結 gpu_vma,並且通常需要持有 gpu_vm->lockgem_object->gpuva_lock。取消連結 gpu_vma 時應持有相同的鎖,這可確保在 gpu_vm->resv 或 GEM 物件的 dma_resv 下迭代 gpu_vma` 時,只要我們迭代的鎖未釋放,gpu_vma 就會保持活動狀態。對於 userptr gpu_vma,同樣需要在 vma 銷燬期間持有外部 ``gpu_vm->lock,因為否則在如上一節所述迭代失效的 userptr 列表時,沒有任何東西可以使那些 userptr gpu_vma 保持活動狀態。

可恢復頁面錯誤頁表更新的鎖定

對於可恢復頁面錯誤的鎖定,我們需要確保兩件事

  • 當我們返回頁面以供系統/分配器重用時,不應再有任何 GPU 對映,並且任何 GPU TLB 都必須已重新整理。

  • gpu_vma 的取消對映和對映不得競爭。

由於 GPU pte 的取消對映(或清零)通常發生在難以甚至不可能獲取任何外部級別鎖定的地方,因此我們必須引入一個在對映和取消對映時都持有的新鎖,或者檢視我們在取消對映時確實持有的鎖,並確保在對映時也持有它們。對於 userptr gpu_vma,在 mmu 失效通知程式中以寫入模式持有 userptr_seqlock,在此處進行清零。因此,如果在對映期間以讀取模式持有 userptr_seqlock 以及 gpu_vm->userptr_notifier_lock,則它不會與清零競爭。對於 GEM 物件支援的 gpu_vma,清零將在 GEM 物件的 dma_resv 下進行,並確保在為指向 GEM 物件的任何 gpu_vma 填充頁表時也持有 dma_resv,這將同樣確保我們沒有競爭。

如果在釋放這些鎖的情況下在 dma-fence 下非同步執行對映的任何部分,則清零需要在開始修改頁表之前等待該 dma-fence 在相關鎖下發出訊號。

由於以釋放頁表記憶體的方式修改頁表結構也可能需要外部級別的鎖,因此 GPU pte 的清零通常僅專注於將頁表或頁面目錄條目清零並重新整理 TLB,而頁表記憶體的釋放會延遲到取消繫結或重新繫結時。