<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          LWN:BPF allocator 碰到麻煩了!

          共 4309字,需瀏覽 9分鐘

           ·

          2022-05-14 10:20

          關(guān)注了就能看到更多這么棒的文章哦~

          The BPF allocator runs into trouble

          By Jonathan Corbet
          April 29, 2022
          DeepL assisted translation
          https://lwn.net/Articles/892743/

          5.18 內(nèi)核合并的一個改動是為已經(jīng)加載到內(nèi)核里的 BPF 程序提供一個專門的內(nèi)存分配器(memory allocator)。不過在那之后,這個功能遇到了相當(dāng)多的困難,幾乎可以肯定在 5.18 的最終版本中會被禁用。走到這一步,部分原因在于分配器本身的 bug,但是這項工作也很不幸地引發(fā)了內(nèi)核的內(nèi)存管理子系統(tǒng)中一些更古老、隱藏更深的 bug。

          在目前的內(nèi)核中,BPF 程序的內(nèi)存空間(經(jīng)過 JIT 代碼轉(zhuǎn)換之后)的分配代碼,就是用為 loadable module 分配內(nèi)存的那些代碼來分配的。這看起來很有道理,因為兩種情況都是要申請空間用于在內(nèi)核中運行的可執(zhí)行代碼。但是,這兩種使用場景之間有一個關(guān)鍵區(qū)別。內(nèi)核 module 是相對靜態(tài)的,它們一旦被加載就幾乎不會被刪除。相反,BPF 程序可以頻繁地出現(xiàn)和消失。在系統(tǒng)生命周期中可能有成千上萬次的加載和卸載動作。

          這一差異目前看來影響很大??蓤?zhí)行代碼的內(nèi)存必然要設(shè)置執(zhí)行權(quán)限(execute permission),因此也必須是只讀的。這就要求這部分內(nèi)存在 page table 中有自己的 mapping,也就意味著它必須從內(nèi)核的 direct mapping(可能是 huge-page 方式管理的)中分離出來。這就要把 direct mapping 分解成更小的 page。隨著時間的推移,就會讓 direct map 越來越碎片化,可能會對性能產(chǎn)生明顯的影響。BPF 分配器的主要目標(biāo)是將這些分配動作都采用一組專用的 huge page 中,避免產(chǎn)生這種碎片化。

          不過,在這段代碼合并后不久,就開始涌現(xiàn)出一些 regression 問題報告,以及相關(guān)的擔(dān)憂。這引起了 Linus Torvalds 和其他開發(fā)者的注意,并揭示了一系列的問題。雖然其中一些 bug 是在 BPF 分配器本身,但最具破壞性的問題是在另一個子系統(tǒng)(vmalloc()分配器)中以前做出的一個改動。

          vmalloc() and huge pages

          vmalloc()(及其多種變種函數(shù))與其他的內(nèi)核內(nèi)存分配接口的差異在于,它返回的內(nèi)存虛擬地址連續(xù),但物理上可能是分散的。因此,它適合于分配較大的、并且不需要物理連續(xù)的內(nèi)存區(qū)域。由于 vmalloc() 的開銷較大,而且 32 位系統(tǒng)的可用地址空間不足,人們曾經(jīng)不希望大量使用 vmalloc(),但是隨著時間的推移,人們的態(tài)度已經(jīng)發(fā)生了變化。現(xiàn)在,使用 vmalloc()作為一種避免進行較大 size 分配時因內(nèi)存碎片化而失敗的方法在普遍使用了。近年來,像 kvmalloc() 這樣的函數(shù)也被添加進來,它將在普通分配未能成功時自動退回到 vmalloc()。

          在 2021 年,Nick Piggin 增強了 vmalloc()的功能,如果申請的 size 足夠大,就可以分配 huge page 了。人們可能會想為什么需要這樣做,畢竟 vmalloc() 明確是為了不需要物理連續(xù)的內(nèi)存分配而設(shè)計的。答案就是 huge page 可以通過減少對 CPU 的 translation lookaside buffer (TLB)的壓力來提升性能。內(nèi)核有一些大 size 的分配場景可以從這個改動中受益,所以它被合并到了 5.13 內(nèi)核中。

          哪怕在合入的時候,人們也提出了一些注意事項。內(nèi)核中有些地方會因為收到 vmalloc() 調(diào)用返回的 huge page 而無法正常使用。比如說 PowerPC module loader。所以 Piggin 也添加了一個 flag VM_NO_HUGE_VMAP,它要求只使用 base page。當(dāng)然,vmalloc() 不接受任何 flag,所以要想避免分配到 huge page 的話,只能通過更底層的 __vmalloc_node_range() 函數(shù)來分配了,直到 5.13 周期的后期添加 vmalloc_no_huge() 為止。當(dāng)時 x86 架構(gòu)也沒有啟用 Huge-page 分配,因為沒有人去花時間尋找并解決那里的潛在問題。

          BPF-allocator 系列的第一個 patch 在 vmalloc()中啟用了 x86 架構(gòu)的 huge page 分配;這對 BPF 分配器來說是有必要的,因為需要使用 huge page。這一切看起來都很好,直到開始了更廣泛的測試,就發(fā)現(xiàn)了問題。看來在 x86 上啟用 vmalloc() 中的 huge page 可能不是一個好主意。不過其實這個問題實際上與 x86 架構(gòu)本身并沒有什么關(guān)系。

          當(dāng) vmalloc()(正如它在 5.18 周期開始時的行為)在響應(yīng)一個申請分配 huge page 的請求時,會返回一個 compound page——這是一組連續(xù)的 base page,表現(xiàn)得像一個單一的、更大的 page 而已。這些 page 的組織方式是有差異的,其中關(guān)于它要如何使用的大部分信息,都存儲在第一個 base page ("head")的 page structure 中。接下來的那些("tail")page 的 page structure 大多只包含了一個指向 head page 的指針。務(wù)必注意不要把 tail page 當(dāng)作是獨立頁面,否則會出現(xiàn)問題。

          問題就這么發(fā)生了。事實證明,內(nèi)核里面有很多代碼都是假定可以直接將來自 vmalloc() 的內(nèi)存視為由 base page 組成的;這種代碼會調(diào)整這個 page structure 而沒有注意到它可能正在處理的是 tail page。這導(dǎo)致了系統(tǒng)內(nèi)存 mapping 損壞,一旦檢測到這種 mapping 損壞,內(nèi)核就會出現(xiàn) oops。一個已知會發(fā)生這種情況的案例是 Rick Edgecombe 首先注意到的,即驅(qū)動代碼調(diào)用 vmalloc_to_page() 來獲取 vmalloc() 分配中的某個 page structure(因此,可能在一個 compound page 的中間位置)。事實證明,有相當(dāng)多的驅(qū)動程序使用 vmalloc_to_page()。如果相關(guān)內(nèi)存是由 compound page 組成的,那么每一個驅(qū)動程序幾乎都會碰到問題。

          這個具體問題最終被 Piggin 修復(fù)了?,F(xiàn)在的代碼是將分配的 huge page 分割成 base page(同時保留 huge page mapping),不再使用 tail page。但是在 vmalloc() 子系統(tǒng)中還潛伏著一些其他的問題,隨著越來越多暴露出來,Torvalds 得出結(jié)論:"HUGE_VMALLOC 的設(shè)計有嚴重的錯誤"。他說,從一開始的設(shè)計就有問題,問題現(xiàn)在才出現(xiàn),是因為在 x86 架構(gòu)上啟用該功能導(dǎo)致了更廣泛的測試。

          Resolutions for 5.18

          Piggins 的 fix 被合并到 5.18-rc4 prepatch 中。與此同時,BPF 分配器 patch 的作者 Song Liu 正在努力尋找一套解決方案,從而能安全地使用該分配器,于是就產(chǎn)生了一組由四個 patch 組成的方案。

          1. 刪除了 VM_NO_HUGE_VMAP flag,改用新的 VM_ALLOW_HUGE_VMAP 這個變種。這改變了該 flag 的含義,從而讓 huge page 分配成為一個可以要求使用的功能,而不是要求禁止的。

          2. 讓 alloc_large_system_hash() (用于為大型哈希表來分配空間)來優(yōu)先使用 huge page 分配,因為這里使用是肯定安全的。

          3. 增加了一個名為 module_alloc_huge() 的函數(shù),它也能實現(xiàn) huge-page 的分配。

          4. 使用 module_alloc_huge() 來分配 BPF 分配器所使用的空間。

          如果在 vmalloc()中廣泛使用了 huge page 是唯一的問題,那么這些措施可能已經(jīng)足夠了。然而,Torvalds 也不喜歡他在 BPF 分配器代碼中看到的一些內(nèi)容。其中之一是,它在沒有初始化內(nèi)存的情況下就啟用了所分配的內(nèi)存的可執(zhí)行權(quán)限,這就給內(nèi)核的地址空間里增加了一堆隨機的可執(zhí)行代碼段。他總結(jié)說 "我真的不認為這已經(jīng)準(zhǔn)備好大規(guī)模使用了"。

          因此,他決定只合入 Liu 的第一個 patch,也就是完全禁用 vmalloc()中的 huge page 分配(因為還沒有什么地方使用了新的 opt-in 方式的 flag)。最初他打算到此為止,但后來判定第二個 patch 也可以安全地合入。然后他甚至更進一步添加了一個他自己的 patch,在 kvmalloc() 中實現(xiàn)了 huge page 分配。這樣做的理由是,從該函數(shù)返回的內(nèi)存可能來自一個 slab 分配器,所以拿到這塊內(nèi)存的用戶無論如何都不應(yīng)該對相應(yīng)的 page structure 使用底層操作技巧。

          此后,Liu 在另一組 patch 中修復(fù)了未初始化內(nèi)存的問題。BPF 維護者 Alexei Starovoitov 試圖說明這項工作也應(yīng)該被合入,使 BPF 分配器在 5.18 版本中可用。不過 Torvalds 仍然未被說服,所以這項工作似乎更有可能是 5.19(甚至可能是更晚)才會合入了。也就是說 BPF 用戶可能只需要再等一個發(fā)布周期就可以使用專門的內(nèi)存分配器了。

          從這個小插曲中可以得出一些結(jié)論。調(diào)整底層的內(nèi)存管理特性是很棘手的,可能會在出人意料的地方產(chǎn)生問題。更為流行的架構(gòu)所帶來的廣泛測試有很大的價值,它會發(fā)現(xiàn)那些在用戶基數(shù)較小的架構(gòu)上可能被隱藏起來的錯誤。但是,也許最重要的是,它說明了在內(nèi)存管理子系統(tǒng)之外就不應(yīng)該去訪問 page structure。將這種底層的細節(jié)暴露在整個內(nèi)核中,總是會導(dǎo)致這種意外發(fā)生。讓內(nèi)核的其他部分不再使用 page structure(這才剛剛開始)將是一個漫長而困難的任務(wù),但還是很值得去承受這些辛苦的。

          全文完
          LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。

          歡迎分享、轉(zhuǎn)載及基于現(xiàn)有協(xié)議再創(chuàng)作~

          長按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開源社區(qū)的各種新近言論~



          瀏覽 13
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久伊人大香蕉精品视频 | 青青草亚洲 | 人人射影院 | 午夜福利黄色 | 国产精品直接观看 |