pcid對內核頁表切換的優化

前一段時間碰到一個pcid相關的bug,於是就把內核對pcid的支持代碼看了一遍。年齡大了看過的東西時間一長就忘了,把要點記下來既方便自己又有利他人。我看的是公司發行版的內核uek4基於upstream的v4.1.12。upstream的pcid支持同舊版本相比用了一套完全不同的更優化的方式,所以另開一篇文章來寫。

不同的進程有不同的地址空間用mm_struct表示,進程調度時內核線程借用用戶進程的地址空間並使cpu進入LAZY模式,當mm地址空間有更新的時候,cpu會發送tlb shutdown給相關cpu通知他們進行刷新。對於LAZY模式的cpu刷新是沒有意義的,因爲內核線程永遠不會訪問用戶空間,所以leave_mm被調用,使得當前cpu脫離用戶進程mm,並綁定到init_mm,這樣cpu不會再收到tlb shutdown。

PTI的引入使得一個進程有兩個頁表,內核一次分配8K的頁,4k用於內核pgd,另外4k用於用戶pgd。用戶頁表映射了用戶態地址,gdt,idt,tss,內核entry/exit代碼等在頁表切換之前就需要的內核地址空間,這部分空間會打上global標記,而其他的普通內核地址空間在內核頁表中被去掉了global標記。

這樣每次系統調用都會發生頁表切換,幾乎所有tlb表項會被刷新,代價很大。下面是一個對比數據,每秒的系統調用數從5.2M降到了2.2M!
    no kaiser: 5.2M
    kaiser+  pcid: 3.0M
    kaiser+nopcid: 2.2M

比較新一點的cpu都支持pcid,pcid可以在頁表切換的時候避免cpu刷新tlb。4.1.12用asid=0標記內核頁表,asid=0x80標記用戶頁表,於是用戶態系統調用進入內核態和退出內核態不用再刷新頁表,直到調度器調度另一個進程,內核頁表被刷新。之前的用戶頁表得以保留。內核同時設定一個標記使得新進程返回用戶態時刷新一次用戶頁表,這個標記是一次性的。

這樣優化以後性能從2.2M上升到了3.0M。但是這種設計在TLB中只緩存一個進程的表項,在no kaiser時pcid沒有發揮任何效果,於是upstream給出了一套新的設計。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章