廢話不多說,我們直接看文件~XD
還有一類 cpu 快取問題,目前需要一套完全不同的介面來正確處理。
這問題就是處理器 D-cache 中的虛擬別名(virtual aliasing)。
你的 cpu 架構是否容易在其 D-cache 中出現虛擬別名?
如果你的 D-cache 是虛擬索引的,且快取列大於 PAGE_SIZE(頁大小),
並且不能防止同一實體位址同時存在多個快取列,就會遇到這個問題。
如果你的 D-cache 有這個問題,首先正確定義 asm/shmparam.h SHMLBA,
它基本上應該是你的 D-cache 的大小(或者如果大小是可變的,則是最大的可能值)。
這樣將迫使 SYSv IPC 層只允許使用者程序在這個值的倍數的位址上對共享記憶體進行 mmap。
.. note::
這並不能解決共享 mmaps 的問題,請查看 sparc64 解決這個問題的一個方法
(特別是 SPARC_FLAG_MMAPSHARED)。
接下來,你必須解決所有其他情況下的 D-cache 別名問題。
請記住,對於一個給定的使用者定址空間映射的分頁,
總至少還有一個從 PAGE_OFFSET 開始的核心空間的線性映射。
因此,一旦第一個使用者將一個給定的實體分頁映射到它的定址空間,
就意味著 D-cache 的別名問題有可能存在,因為核心已經將這個分頁映射到它的虛擬定址空間。
``void copy_user_page(void *to, void *from, unsigned long addr, struct page *page)``
``void clear_user_page(void *to, unsigned long addr, struct page *page)``
這兩個介面在使用者匿名分頁或 COW 分頁中儲存資料。
它使得不同架構能有效地利用這兩個介面避免使用者空間和核心之間的 D-cache 別名問題。
例如,架構可以如此實作,
在複製過程中把 “from” 和 “to” 暫時映射到核心的虛擬位址。
選擇這兩個頁面的虛擬位址方式使得,核心的 load/store 指令發生在,
和使用者映射分頁具有相同 “顏色” 的虛擬位址上。
例如,Sparc64 就使用這種技術。
“addr” 參數代表使用者最終要映射這個分頁的虛擬位址,
“page” 參數給出了一個指向目標 struct page 的指標。
如果架構並沒有 D-cache 別名問題,
這兩個介面可以簡單地直接使用 memcpy/memset 而不做其他事情。
``void flush_dcache_page(struct page *page)``
這個介面的使用時機:
a. 核心確實寫到一個在分頁快取(page cache)中的分頁 和/或是 high memory
b. 核心要讀取一個分頁快取的分頁,且該分頁在使用者空間是可能存在 共享/可寫 的映射。
注意:{get,pin}_user_pages{_fast} 已經在任何使用者定址空間的分頁
呼叫了 flush_dcache_page,所以驅動程式的程式碼幾乎不需考慮這個狀況。
.. note::
這個介面只需要運作在有可能被映射到使用者程序定址空間的分頁快取分頁。
因此,VFS 層程式碼處理分頁快取中 vfs 符號連結(symlinks)的根本不需要使用這個介面。
“核心寫入分頁快取的分頁” 這句話的意思是,具體來說,
內核執行 store 指令,弄髒(dirty)了在該分頁的分頁->虛擬映射處的資料。
在這介面中,通過更新快取的手段處理 D-cache 的別名問題是很重要的,
如此才可確保這些核心儲存對該分頁的使用者空間映射是可見的。
推論的情況也同樣重要,如果有使用者對這個文件有共享+可寫的映射,
我們必須確保核心對這些分頁的讀取會看到使用者所做的最新的儲存指令。
如果架構並沒有 D-cache 別名問題,這個介面可以簡單地定義為該架構上的 nop。
在 page->flags (PG_arch_1) 中有一個位元是 “architecture private”。
核心保證,對於分頁快取的分頁,當這樣的分頁第一次進入分頁快取時,
這個位元將被清除。
這使得這些介面可以有更有效率的實作。
如果目前沒有使用者程序映射這個分頁,
它允許我們“延遲”(也許是無限期)實際的更新過程。
請看 sparc64 的 flush_dcache_page 和 update_mmu_cache 實現,
以了解如何做到這一點。
概念是首先在 flush_dcache_page() 時,如果 page_file_mapping() 回傳了一個映射,
且 mapping_mapped 在該映射上回傳 %false,只需標記 architecture private 旗標位元。
其後,在 update_mmu_cache() 中,會對這個旗標位元進行檢查,
如果設置了,就進行更新,並清除該旗標位元。
.. important::
很重要的是,如果更新被延遲的話,實際的更新會發生在
和 cpu 儲存到的分頁,使該分頁變髒的同一個 CPU 上。
同樣,請參閱 sparc64 關於如何處理這個問題的示例。
``void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
unsigned long user_vaddr, void *dst, void *src, int len)``
``void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
unsigned long user_vaddr, void *dst, void *src, int len)``
當核心需要複製任意的數據到任意的使用者分頁時(比如ptrace()),
它將使用這兩個介面。
任何必要的快取更新或其他需要的快取一致性操作都應該在這裡作用。
如果處理器的 icache 沒有對 cpu 存儲進行窺探(snoop),
那麽很可能會需要為 copy_to_user_page() 更新 icache。
``void flush_anon_page(struct vm_area_struct *vma, struct page *page,
unsigned long vmaddr)``
當核心需要存取一個匿名分頁的內容時,它會呼叫這個函數(目前只有 get_user_pages())。
注意:flush_dcache_page() 故意對匿名分頁不起作用。
預設的實作方法是nop(對於所有具有快取一致性的架構都應該如此)。
對於不一致性的架構,它應該更新 vmaddr 處分頁的快取。
``void flush_icache_range(unsigned long start, unsigned long end)``
當核心儲存到它將執行的位址中時(例如在載入模組時),這個函數被呼叫。
如果 icache 不對這筆儲存進行窺探 (snoop),那麽將需要對其進行更新。
``void flush_icache_page(struct vm_area_struct *vma, struct page *page)``
flush_icache_page 的所有功能都可以在 flush_dcache_page
和 update_mmu_cache 中實現。希望在未來能夠完全移除這個介面。
今天閱讀的內容,是目前閱讀起來最難理解的一個部分,只大致上瞭解這些介面是為了在不同的情境下,正確解決 D-cache aliasing 的問題,但提到解決的方法都相當晦澀難懂,而且英文原文讀起來也是相當繞口,感覺需要對核心分頁管理有很深入的瞭解,才能字字句句理解的透徹。
不免俗的記錄一下,自己需要再更理解的部分:
for a given page mapped into some user address space, there is always at least one more mapping, that of the kernel in its linear mapping starting at PAGE_OFFSET
為真?今天先完成這部分的部分翻譯,接下來的幾天,再把其餘翻譯,以及試圖理解內容的而查詢各種資料補上,預祝各位中秋節快樂,我們明天見!