拆分頁表鎖

最初,mm->page_table_lock 自旋鎖保護了 mm_struct 的所有頁表。但是,由於鎖上的高競爭,這種方法導致多執行緒應用程式的頁面錯誤可伸縮性較差。為了提高可伸縮性,引入了拆分頁表鎖。

使用拆分頁表鎖,我們有單獨的每表鎖來序列化對錶的訪問。目前,我們對 PTE 和 PMD 表使用拆分鎖。對更高級別表的訪問受 mm->page_table_lock 保護。

有一些輔助函式可以鎖定/解鎖表和其他訪問器函式

  • pte_offset_map_lock()

    對映 PTE 並獲取 PTE 表鎖,返回指向 PTE 的指標以及指向其 PTE 表鎖的指標,如果不存在 PTE 表則返回 NULL;

  • pte_offset_map_ro_nolock()

    對映 PTE,返回指向 PTE 的指標以及指向其 PTE 表鎖的指標(未獲取),如果不存在 PTE 表則返回 NULL;

  • pte_offset_map_rw_nolock()

    對映 PTE,返回指向 PTE 的指標以及指向其 PTE 表鎖的指標(未獲取)及其 pmd 條目的值,如果不存在 PTE 表則返回 NULL;

  • pte_offset_map()

    對映 PTE,返回指向 PTE 的指標,如果不存在 PTE 表則返回 NULL;

  • pte_unmap()

    取消對映 PTE 表;

  • pte_unmap_unlock()

    解鎖並取消對映 PTE 表;

  • pte_alloc_map_lock()

    如果需要,分配 PTE 表並獲取其鎖,返回指向 PTE 的指標以及指向其鎖的指標,如果分配失敗則返回 NULL;

  • pmd_lock()

    獲取 PMD 表鎖,返回指向獲取的鎖的指標;

  • pmd_lockptr()

    返回指向 PMD 表鎖的指標;

如果 CONFIG_SPLIT_PTLOCK_CPUS(通常為 4)小於或等於 NR_CPUS,則編譯時啟用 PTE 表的拆分頁表鎖。如果停用拆分鎖,則所有表都由 mm->page_table_lock 保護。

如果 PTE 表啟用了 PMD 表的拆分頁表鎖,並且該架構支援它(見下文)。

Hugetlb 和拆分頁表鎖

Hugetlb 可以支援多種頁面大小。我們僅對 PMD 級別使用拆分鎖,而不對 PUD 使用。

Hugetlb 特定輔助函式

  • huge_pte_lock()

    獲取 PMD_SIZE 頁面的 pmd 拆分鎖,否則獲取 mm->page_table_lock;

  • huge_pte_lockptr()

    返回指向表鎖的指標;

架構對拆分頁表鎖的支援

無需特別啟用 PTE 拆分頁表鎖:所有必需的操作都由 pagetable_pte_ctor() 和 pagetable_dtor() 完成,必須在 PTE 表分配/釋放時呼叫它們。

確保架構不使用 slab 分配器進行頁表分配:slab 對其頁面使用 page->slab_cache。此欄位與 page->ptl 共享儲存。

如果您有超過兩個頁表級別,PMD 拆分鎖才有意義。

啟用 PMD 拆分鎖需要在 PMD 表分配時呼叫 pagetable_pmd_ctor(),在釋放時呼叫 pagetable_dtor()。

分配通常發生在 pmd_alloc_one() 中,釋放在 pmd_free() 和 pmd_free_tlb() 中,但請確保您涵蓋所有 PMD 表分配/釋放路徑:即 X86_PAE 在 pgd_alloc() 上預分配少量 PMD。

完成所有設定後,您可以設定 CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK。

注意:pagetable_pte_ctor() 和 pagetable_pmd_ctor() 可能會失敗 - 必須正確處理。

page->ptl

page->ptl 用於訪問拆分頁表鎖,其中“page”是包含該表的頁面的 struct page。它與 page->private(以及聯合中的其他幾個欄位)共享儲存。

為了避免增加 struct page 的大小並獲得最佳效能,我們使用了一個技巧

  • 如果 spinlock_t 適合 long 型別,我們使用 page->ptr 作為自旋鎖,這樣我們可以避免間接訪問並節省一個快取行。

  • 如果 spinlock_t 的大小大於 long 的大小,我們使用 page->ptl 作為指向 spinlock_t 的指標並動態分配它。這允許使用啟用 DEBUG_SPINLOCK 或 DEBUG_LOCK_ALLOC 的拆分鎖,但會為間接訪問增加一個快取行;

spinlock_t 在 PTE 表的 pagetable_pte_ctor() 中分配,在 PMD 表的 pagetable_pmd_ctor() 中分配。

請不要直接訪問 page->ptl - 使用適當的輔助函式。