避坑指南關于SPDK問題分析過程

      網友投稿 1319 2025-03-31

      【前言】

      這是一次充滿曲折與反轉的問題分析,資料很少,代碼很多,經驗很少,概念很多,當內核態,用戶態,DIF,LBA,大頁內存,SGL,RDMA,NVME和SSD一起迎面而來的時候,問題是單點的意外,還是群體的無奈?

      為了加深記憶,也為了分享出來給人以啟示,特記錄這次問題分析過程。

      【現象】

      同事L在項目中需要使用NVMF寫盤,發現寫盤失敗,瘋狂打印錯誤碼:

      圖片中雖然截取的比較少,但實際是瘋狂的一直打印。

      故障現象簡要描述一下就是:

      通過NVMF寫盤失敗,瘋狂打印錯誤碼15;

      作為對照,通過本地寫盤,一切正常。

      注:這里的盤,都是指SSD盤。目前實驗室使用的型號是公司V3版本(HWE3xxx)。

      【分析】

      在這里把涉及到的一些基本縮略語都記錄一下:

      習慣了縮略語作為名詞后,總是容易忽略其背后更多的含義,問題的分析,需要對這些有更深的理解,最初對這些理解不深,對數據處理流程不清晰,起步很艱難。

      分析步驟(一)

      在下發IO時,通過變換IO的大小,隊列深度,發現數據量較小時,則幾乎沒有問題,直接下發1M大小IO時,則必現。

      因此,可以明顯的推測出IO的大小與問題的出現緊密相關。

      直接運行業務來驗證問題,過于笨重了,而且非常麻煩,將問題直接簡化為,一個服務端和一個請求端,發現均能穩定復現,他們分別是:

      1. 運行SPDK自帶的app,nvmf_tgt程序,這個就是NVMF的服務端了;

      進入spdk目錄后,配置好2M大頁;

      配置好nvmf.conf 配置文件,假設文件放在/opt/yy目錄下;配置文件參考附錄;

      運行./app/nvmf_tgt/nvmf_tgt -c /opt/yy/nvmf.conf;

      2. 可以使用兩種模式的請求端,

      一種是SPDK自帶的perf程序,路徑是./examples/nvme/perf/perf,會配置必要的參數; 注意:系統也自帶一個perf,不是系統自帶的那一個; Perf是一個測試工具,會隨機產生數據大量寫入,可以驗證問題修復性,但不利于問題最初的分析;

      一種是自已改造nvme目錄下的helloworld程序(初始版本,由同事C提供,后來經過了一些改良,后續稱為DEMO程序); 代碼見附錄;

      因為都是運行在用戶態,所以開啟調試還是很方便的。兩端同時開啟調試模式,進行單步跟蹤,發現錯誤碼是在異步模式下輪循得到,如圖

      函數名稱已經告知,是處理完成的結果;

      調用是來自于這里,383行:

      在303行下斷點,根據棧信息(沒有有效信息,略)看,錯誤碼可能來自于SPDK的某個異步調用,也可能來自于設備,查遍SPDK代碼,發現根本沒有15這個錯誤碼的設置,基本推導為是由SSD返回的。

      根據最初的信息可知,IO的數據量大小會影響問題出現,IO數據量較小時不會出現,那么分界點在哪里呢?

      采用二分法在DEMO程序上嘗試,發現LBA的個數為15時,是分界點。

      那么,怎么用起來呢?

      單步跟蹤,有一個參數進入視野,命名空間(NVME的協議規范吧,一塊SSD下有一個控制,有若干個命名空間)的sectors_per_max_io參數。

      修改這個參數,可以控制最后寫盤時的大小,在DEMO程序上試驗,問題消失。

      但是當IO大小與深度較大,要么出現內存不足錯誤碼,要么錯誤依然出現,另外多盤場景下非常容易再現。

      給出有條件解決辦法1:

      (1) 修改如上位置;

      (2) 業務下發時要求對IO的大小和下發的盤數進行限定;

      實際使用時,因為必需多盤,要改造成單盤,非常困難,不是理想的解決方案。

      另外還發現不同版本的盤,最小適配值不一樣,最安全值是7,但是后來主要選取一塊15為安全線的盤來分析問題使用。

      分析步驟(二)

      為了快速解決問題,開始嘗試廣泛求助,這么明顯的問題,別人有沒有遇到?

      在遍訪hi3ms和搜遍google,以及請教相關可以找到的同事,嘿,還真沒有第二例!

      而且更為奇怪的是,在Intel的基線報告中明明就有較大的IO數據量的NVMF測試,還有正常的結果。

      怎么在這里就有問題呢?

      不同點:

      Intel肯定使用Intel的盤;

      這兒用的是公司的盤;

      難道是因為這個?

      硬件上,理論上沒有這么大差異吧。

      經過一番探索發現,當把硬盤格式化為不帶DIF時,NVMF也是正常的,如果格式化為帶DIF的,即512+8格式時,問題就會出現;

      SO,Intel為啥沒有問題,基本已經確定,他們用的是不帶DIF格式,同時發現不帶DIF,時延會快一點點,這很好理解。

      有一個疑惑,始終沒有答案,為什么本地寫沒有出現,而NVMF寫會出現呢?

      這是需要回答的最重要的問題。

      作為基礎,需要先簡單了解一下NVME的寫盤。

      這個過程是異步的;

      寫盤前,程序將數據按照隊列(比如SGL)準備好,然后通知SSD,程序就完事了;

      然后是SSD會到機器中把數據取出寫入盤中,處理完成后,然后通知程序,程序檢查結果隊列。

      可以看出,當前說的寫盤,主要是指將數據按照隊列準備好就完成了,后面一段是由SSD設備來處理的。

      有了這個基礎,可以較快理解本地寫盤了,調用SPDK API后,由SPDK準備隊列,然后提交,真正把數據存起來的事情是SSD里控制器做的。。。

      但是NVMF寫盤呢?畢竟中間有段網絡,是怎么處理的。。。

      為了便于分析,所以選擇改造DEMO,主要是perf比較復雜,隨機的LBA和大數據量對分析有較大干擾。

      在DEMO程序中,指定在0號LBA開始提交數據,而且每次提交17塊數據(總長度17*520=8840)。

      那為啥數據塊指定17呢?

      因為15及以下是不會出現問題的,根據前面的分析,這塊SSD的正常分界線是15,而16是2的4次方,在計算機中2的N次方過于特殊,因此選擇普通的17。

      其次,保證其它地方完全一樣,僅在初始化時,形成兩種模式,一種是本地寫,一種是NVMF寫;

      如圖,手動直接改變紅框里的參數,由tr_rdma和tr_pcie,可以在兩種模式中切換;

      這樣的目的是,可以形成完全的對比,對齊所有能對齊的條件,分析在NVMF的哪個環節出現問題。

      在初步單步跟蹤了一下調用過程,可以梳理出本地寫與NVMF寫的基本處理流程:

      本地寫:

      在請求端,申請了一塊連續的內存1M大小,塊大小以4K大小對齊;

      將其中的17個塊(也就是1M大小只用了17*520字節)通過調用SPDK的API進行寫盤;

      SPDK的API會調用以PCIE模式接口(系統初始化時,注冊的回調函數,在初始化入口時,上面圖中紅框的參數決定了會走向PCIE對應接口);

      準備數據隊列,提交SSD寫盤請求,返回;

      輪循處理完成的接口,獲取到寫盤成功通知;

      NVMF寫:

      請求端側:

      (1)在請求端,申請了一塊連續的內存1M大小,塊大小以4K大小對齊;

      (2)將其中的17個塊(也就是1M大小只用了17*520字節)通過調用SPDK的API進行寫盤;

      (3) SPDK的API會調用以RDMA模式接口(同上,初始化時,注冊了RDMA的回調函數,上圖中紅框的參數決定了,這里的調用走向RDMA對應接口);

      (4)準備數據隊列,通過RDMA網絡傳送到服務端,返回;

      服務端側:

      (5) 服務端的RDMA在輪循(poll)中收到數據到來的通知;

      (6)組裝數據結構,便于內部API調用;

      (7)數據一路調用bdev,spdk,nvme的api,地址被轉換為物理地址,最后調用pcie的數據接口提交;

      (8)然后按規范按下提交門鈴,返回;

      兩側異步(提交請求后,只能異步等待結果打?。┐蛴〗Y果:

      (9)請求端輪循處理完成的接口,如果錯誤會出現打?。?/p>

      通過debug可以看到錯誤碼是15

      (10)服務端輪循處理完成的接口,如果錯誤,會出現打印:

      反復對本地和NVMF下發數據(上面0開始,17塊數據),逐個流程與參數對比(雙屏提供了較大的便利),確實發現不少異同點:

      (1)本地寫的過程與NVMF寫的請求端過程,幾乎一樣,不同的是本地寫的數據提交是到SSD,NVMF請求端的寫調用RDMA的接口;

      (2) NVMF服務端有很長的調用棧(有30層深),而本地寫根本不存在這個過程;

      (3)NVMF服務端在經過系列調用后,最后走到了像本地寫盤一樣的函數調用,nvme_transport_qpair_submit_request;

      似乎是個顯然的結論,NVME OVER RDMA實際是,數據經過了RDMA傳輸后,還是NVME OVER PCIE;

      (4)本地寫時,只有1個SGL,這個SGL里面只有1個SGE,NVMF的請求端在調用RDMA前,也是只有1個SGL,這個SGL里也只有1個SGE;

      (5) NVMF服務端的在寫盤前,只有1個SGL,但是這個SGL里有2個SGE;

      整個過程,用圖來描述如下:

      如圖:

      這是一個重要的發現,基本可以解釋為什么解決辦法1部分場合是有效的(15的安全線內數據大小小于8k,保證1個SGL里只有1個SGE),但無法解釋有一些場合失敗。

      捋一下,就清楚多了:

      RDMA在NVMF的請求端拿到的數據是1個SGL內含1個SGE,經過RDMA后,從NVMF服務端拿到的數據是1個SGL內含2個SGE。

      至此,似乎基本“鎖定”了肇事者了,就是RDMA了!

      但是,在翻閱RDMA的資料,SSD的資料后,發現1個SGL里,1個SGE,2個SGE根本是自由的,自由的。。。

      避坑指南:關于SPDK問題分析過程

      雖然,RDMA在接收數據后,將1個SGE分成2個SGE,有引起問題的嫌疑,但是從資料介紹看,似乎不能直接構成問題。

      為了驗證1個SGL里多個SGE是不是問題,又開始改造DEMO了,構造了寫數據前,將數據分為多個SGE了,如圖:

      先試了試NVMF,發現可以復現,和前面的NVMF沒有什么兩樣,

      接下來試了試本地,發現沒有問題,也就是說,疑問沒有消除。

      分析步驟(三)

      山重水復疑無路,只好推倒,從頭再來分析,一次偶然的NVMF下發中發現,2個SGE的地址中,第2個SGE的地址在前,第1個SGE的地址在后,然后密切關注,即便在DEMO程序中,這個地址的先后也有一定的隨機,多數時候是順序的,少數時候是顛倒的,但是無論怎樣,1個SGE與另1個SGE中是不連續,也就是SGE1與SGE2之間有空洞。

      馬上構造相同的形態,

      寫本地,發現重現了!

      這是一個“重要發現”!本地也能重現!

      幾乎可以順利成章的推論出,是否NVMF不是關鍵!那么也就排除了RDMA的嫌疑了!

      寫盤時,如果多個SGE的數據區完全連續,則沒有問題,如果多個SGE的數據區不連續,則會出現問題。

      那么,很容易推導出問題所在點,當前用的這個SSD不支持不連續的SGE!難道是SSD?!

      然后。。。(此處略去一段文字不表。。。)

      。。。

      。。。

      是的,SSD沒有問題,有問題的是那個8192的長度,正確的應該是8320!

      8320是什么,8192是什么?

      8192是512 * 16;

      8320是520 * 16;

      看看,之前一直不理解那個刷屏的錯誤提示,什么叫“DATA SGL LENGTH INVALID”,這個含糊不清的提示,也有很多可能,既可能是SGL里的SGE個數不對,也可能是SGE里的長度不對,還可能是里面的長度字段讀寫不對,還可能是寄存器出錯,還可能內存被踩。。。

      但是,真相就是,SGE里的數據長度沒有和BLOCK的基本大小520對齊!現在用的格式是帶DIF區的,512+8=520!

      那個提示是告訴你,數據塊沒有對齊,SGE里的長度無效!

      當各個點針對性的改好了這個基本參數時,

      DEMO的本地正常了,

      DEMO的NVMF也正常了,

      似乎真相大白了。。。

      然而,還沒高興幾分鐘,使用perf下發1M的IO時,問題又復現了!

      分析步驟(四)

      細心的跟蹤后發現,雖然問題復現了,但是沒有以前刷屏那么多了,而且通過單步發現,只要SGE數據的地址是以FF000結尾的,就會出現問題。

      回溯這個地址,可以看到,來源于RDMA在收到數據后就出現了,偶爾會出現FF000結尾的,所以可以解釋錯誤刷屏沒有那么密集了。

      看起來,還是RDMA有問題啊~

      繼續分析可以發現,這些地址,實際也不是RDMA臨時分配的,而是從緩沖隊列里獲取的。

      基本可以認為,緩沖隊列中有很多可供選擇,偶爾會拿到FF000結尾的這種來做緩沖,只要這種地址就會出現問題。

      那么,為什么這種地址就會出現問題呢?

      還記得前面有一個步驟嗎?設置2M大頁內存,SPDK是基于DPDK的,DPDK內存隊列是要求大頁內存的,最常用的是2M大頁。

      這些緩沖就是從DPDK那些大頁里獲取的,而FF000就是靠近2M邊界的,一般的緩沖使用也沒有啥問題,但是SSD不接受跨大頁的空間,因此在準備提交隊列時,如果遇到要跨大頁的,將這個SGE做切分,1分為2,以FF000結尾的地址上只能存4096字節,因此一個SGE里4096,余下的放在下一個SGE里,而4096又不是520的對齊倍數,所以出問題了。

      針對性的解決辦法是,在獲取地址前,加一個判斷,如果是這種地址就跳過。

      修改!

      驗證!

      屏住呼吸。。。

      但是,再一次出乎意料,用perf在大IO下測試依然有問題!

      不氣餒,再戰!

      打開日志(因為是異步,而且是大數據量測試,所以只好在關鍵地方增加日志,記錄下這些地址分配細節,主要地點,一個是提交請求時,見上面的文件和代碼行,就不貼代碼了,一個是入RDMA收到數據最開始拿到的地方,還有一個是完成時的結果),繼續分析。

      一下就看到,還有一種地址分配異常,也會形成SGE中長度問題,如圖:

      再一次在獲取地址的位置進行修改屏蔽之,將兩種要跳過的直接合一。

      如圖(471~475,另外在nvmf_request_get_buffers函數中需要配置進行跳過處理):

      修改!

      驗證!

      各用例測試通過!

      問題消失!

      提供第2個解決辦法,按如上代碼,可以徹底解決問題。

      雖然問題解決了,跳過一些特殊地址,有一些浪費,

      但是總感覺這種改法太土了!可以消除問題,但是隱隱感覺不爽!

      分析步驟(五)

      有沒有其它方法?

      帶著疑問繼續挖。

      既然RDMA只是使用緩沖的隊列,那就有一個地方是分配這種緩沖隊列的,分配出來卻不用,明顯有點浪費,那至少可以做到,分配的時候就不要分配這種數據吧。

      一路回溯,終于找到申請的地方,但是甚是復雜,容后慢慢消化吧。

      發現有段文字描述很長,和地址的分配很相關,

      帶著這些信息再來單步查看分配緩沖過程,大致推測修改過程中的一個參數,就可以影響到后面的處理流程了。

      紅框1為代碼默認參數,修改為紅框2的,紅框2兩個參數的含義為單生產者單消費者,DEMO程序中完全匹配這個模式。

      修改!

      驗證!

      RDMA在獲取SGE地址時,是單向增長的。

      問題消失!

      一個參數消除掉問題,對比起來,舒適多了!

      【小結】

      (1)問題最后的解決辦法就是: NVMF的配置文件中需要顯性設置IOUnitSize的大小,與所用的Block大小成整數倍對齊,當前使用520的Block,建議設置為8320;修改創建內存池參數;最后圖中的一個參數即可。

      (2) 過程非常曲折,但是只要不放棄,跟著代碼,再翻閱資料,大膽假設,小心求證,不斷迭代,終能找到問題所在;如果對相關概念與處理過程熟悉,會大幅度節約時間;

      (3)最后安利一下,VSC,配上Remote – SSH,可以直接在呈現Linux機器上的代碼,進行可視化調試,在代碼里任意穿梭,哪里疑惑點哪里,對本次分析問題有極大的幫助;

      附錄:

      Nvmf的配置文件如下

      [Global][Nvmf][Transport] ??Type?RDMA ??InCapsuleDataSize?16384??IOUnitSize?8192[Nvme] ??TransportID?"trtype:PCIe?traddr:0000:04:00.0"?Nvme0 ??TransportID?"trtype:PCIe?traddr:0000:05:00.0"?Nvme1 ??TransportID?"trtype:PCIe?traddr:0000:82:00.0"?Nvme2[Subsystem1] ??NQN?nqn.2020-05.io.spdk:cnode1 ??Listen?RDMA?192.168.80.4:5678 ??SN?SPDK001 ??MN?SPDK_Controller1 ??AllowAnyHost?Yes ??Namespace?Nvme0n1?1[Subsystem2] ??NQN?nqn.2020-05.io.spdk:cnode2 ??Listen?RDMA?192.168.80.4:5678 ??SN?SPDK002 ??MN?SPDK_Controller1 ??AllowAnyHost?Yes ??Namespace?Nvme1n1?1[Subsystem3] ??NQN?nqn.2020-05.io.spdk:cnode3 ??Listen?RDMA?192.168.80.4:5678 ??SN?SPDK003 ??MN?SPDK_Controller1 ??AllowAnyHost?Yes ???????????Namespace?Nvme2n1?1

      API

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:WPS邊框和底紋怎么設置
      下一篇:Vim常用按鍵大全
      相關文章
      噜噜噜亚洲色成人网站∨| 日产国产精品亚洲系列| 亚洲婷婷国产精品电影人久久| 亚洲女子高潮不断爆白浆| 亚洲不卡在线观看| 亚洲一区二区三区在线| 国产99在线|亚洲| 国产精品久久亚洲不卡动漫| 亚洲av无码电影网| 亚洲一区二区三区高清不卡 | 亚洲娇小性xxxx色| 亚洲一级毛片免费在线观看| 亚洲大香人伊一本线| 久久精品国产亚洲AV久| 波多野结衣亚洲一级| 亚洲综合一区无码精品| 亚洲色成人四虎在线观看| 亚洲国产成人精品无码区花野真一 | 亚洲精品无码久久久久sm| 亚洲人成无码网站| 日本亚洲成高清一区二区三区| 国产精品亚洲成在人线| 亚洲AV无码码潮喷在线观看| 亚洲国产一区在线| 亚洲第一永久在线观看| 亚洲sss综合天堂久久久| 中文字幕在线日亚洲9| 亚洲GV天堂无码男同在线观看| 国产成人亚洲综合在线| 亚洲AV伊人久久青青草原| 久久乐国产精品亚洲综合| 久久91亚洲人成电影网站| 亚洲精品无码不卡| 亚洲一级高清在线中文字幕| 亚洲欧美黑人猛交群| 亚洲XX00视频| 色噜噜AV亚洲色一区二区| 亚洲va久久久噜噜噜久久| 亚洲视频在线观看免费视频| 亚洲五月丁香综合视频| 国产精品国产亚洲区艳妇糸列短篇|