NVM Express(NVMe),或稱非易失性內存主機控制器接口規范(英語:Non Volatile Memory Host Controller Interface Specification,縮寫:NVMHCIS),是一個邏輯設備接口規范。它是與AHCI類似的、基于設備邏輯接口的總線傳輸協議規范(相當于通訊協議中的應用層),用于訪問通過PCI Express(PCIe)總線附加的非易失性存儲器介質(例如采用閃存的固態硬盤驅動器),雖然理論上不一定要求 PCIe 總線協議。
1. 綜述
NVMe over PCIe協議,定義了NVMe協議的使用范圍、指令集、寄存器配置規范等。
1.1 名詞解釋
Namespace是一定數量邏輯塊(LB)的集合,屬性在Identify Controller中的數據結構中定義。
Fused Operations可以理解為聚合操作,只能聚合兩條命令,并且這兩條命令在隊列中應保持相鄰順序。協議中只有NVM指令才有聚合操作。還需要保證聚合操作的兩條命令讀寫的原子性,參考Compare and Write例子。
除了聚合操作(Fused Operations),每一條SQ中的命令都是獨立的,不必考慮RAW等數據相關問題,即使考慮,也是host應該解決的問題。
控制器需要支持寫單元的原子性。但有時也能通過host配置Write Atomicity feature,減小原子性單元的大小,提高性能。
數據的額外信息,相當于提供校驗功能??蛇x的方式。
用來選擇下一次執行的命令的SQ的機制,三種仲裁方式:
NVMe定義的最小的讀寫單元,2KB、4KB……,用LBA來標識塊地址,LBA range則表示物理上連續的邏輯塊集合。
由SQ(提交隊列)與CQ(完成隊列)組成,host通過SQ提交命令,NVMe Controller通過CQ提交完成命令。
NVM子系統包括控制器、NVM存儲介質以及控制器與NVM之間的接口。
整體來看,NVMe SSD可以分為三部分,host端的驅動(NVMe官網以及linux、Windows已經集成了相應的驅動)、PCIe+NVMe實現的控制器以及FTL+NAND Flash的存儲介質。
NVMe控制器實質上為DMA + multi Queue,DMA負責數據搬運(指令+用戶數據),多隊列負責發揮閃存的并行能力。
NVMe over PCIe,通過利用PCIe總線實現數據交互的功能,實現對物理層的抽象功能。
PCIe總線分為三層,物理層,數據鏈路層,處理層(類似于計算機網絡的分層結構),通過包來轉發數據。NVMe協議定義的內容相當于PCIe的上一層應用層,處于應用層。PCIe給NVMe提供了底層的抽象。
NVMe SSD相當于一個PCIe的端設備(EP)。
在協議中主要定義了PC header、PCI Capabilities和PCI Express Extended Capabilities三部分內容。
具體在host內存中會占有4KB,結構如下:
PCI header有兩種類型,type0表示設備,type1表示橋。NVMe 控制器屬于EP,所以定義為type0的類型。共64KB,如下圖:
這里配置了PCI Capbilities,包括電源管理、中斷管理(MSI、MSI-X)、PCIe Capbilities。
這里配置有關錯誤恢復等高級功能。
3. NVMe寄存器配置
3.1 寄存器定義
NVMe寄存器主要分為兩部分,一部分定義了Controller整體屬性,一部分用來存放每組隊列的頭尾DB寄存器。
判斷隊列滿可以有多種方法,協議中規定的是頭指針比尾指針大一,所以隊列滿時,空余一個元素。
1. 隊列大小有16bit,最小隊列大小為2個元素(因為滿隊列的定義方式,所以最小為2個元素),對于I/O隊列,最大隊列大小為64k;對于Admin隊列,最大隊列為4k;
2. QID來標識唯一ID,16bit,由host分配;
3. host可以修改隊列優先級(如果支持的話),共四級,U、H、M、L;
RR仲裁,Admin SQ與I/O SQ優先級相同,控制器每次可以選擇一個隊列中的多個命令(Arbitration Burst setting)。
有3個嚴格的優先權,Priority1 > Priority2 > Priority3,在這三個優先級隊列中,高優先級的隊列中如果有命令,則優先執行(非搶占式)。
Vendor Specific。
NVMe把Host的內存分為頁的集合,頁的大小在CC寄存器中配置,PRP是一個64位的內存物理地址指針,結構如下:
最后兩位為0,指四字節對齊;(n:2)位表示頁內內偏移。
舉個例子,內存頁大小位4KB,則(11:2)表示頁內偏移。
PRP尋址有兩種方式,直接用PRP指針尋址,通過PRP List尋址。當使用PRP List尋址時,偏移必須為0h,每一個PRP條目表示一個內存頁,如下:
Admin命令的數據地址只能采取PRP的方式,I/O命令的數據地址既可以采取PRP的方式,又可以采取SGL的方式。Host在命令中會告訴Controller采用何種方式。具體來說,如果命令當中DW0[15:14]是0,就是PRP的方式,否則就是SGL的方式。
命令的Dword6~Dword9只定義了PRP1、PRP2兩個數據指針,通過PRP條目可以指向PRP List。如下圖:
在上面的例子中,PRP1直接指向內存頁,PRP2指向PRP List存在的地址,在PRP List中存有數據的真正的地址。
更詳細的說:
協議中PRP Entry是一個指向物理內存頁的指針。PRP被用作NVMe Controller和PC內存之間進行數據傳輸。PRPEntry是固定大小的(8B)。
首先,明確兩個概念,PRP Entry 為PRP指針,PRP List為PRP列表指針,示意圖如下:
SGL是另外一種索引內存的數據結構。SGL由若干個SGL段組成,SGL段又由若干個SGL描述符組成,所以SGL描述符是SGL數據結構的基本單位。
目前定義的SGL描述符有6種:
在上面SGL例子中,共有3個SGL段,用到了4種SGL描述符。Host需要往SSD中讀取13KB的數據,其中真正只需要11KB數據,這11KB的數據需要放到3個大小不同的內存中,分別是:3KB,4KB和4KB。
無論是PRP還是SGL,本質都是描述內存中的一段數據空間,這段數據空間在物理上可能連續的,也可能是不連續的。Host在命令中設置好PRP或者SGL,告訴Controller數據源在內存的什么位置,或者從閃存上讀取的數據應該放到內存的什么位置。
SGL和PRP本質的區別在于,一段數據空間,對PRP來說,它只能映射到一個個物理頁,而對SGL來說,它可以映射到任意大小的連續物理空間,具有更大的靈活性,也能夠描述更大的數據空間。如下圖:
命令由host提交到內存中的SQ隊列中,更新TDBxSQ后,NVMe控制器通過DMA的方式將SQ中的命令(怎么取,如何取,取多少,因設計而異)取到控制器緩沖區,執行命令;執行完成后,根據執行狀態,組裝完成命令,仍然通過DMA的方式將完成命令寫入內存CQ的隊列中;NVMe控制器通過MSI-X中斷方式通知host已完成命令;最后,host處理CQ命令,更新控制器中HDBxCQ,標識著命令真正完成。
命令分為Admin指令與NVM指令(I/O指令)。
Admin指令只能提交到Admin Controller中,主要負責管理NVMe控制器,也包含對NVM的一些控制指令。
NVM 指令只能提交到I/O Controller中,主要負責完成數據的傳輸。
在1.0e版本中,Admin指令有15條(3條與NVM相關),NVM指令有6條;在1.3d版本中,Admin指令有15條(3條與NVM相關),NVM指令有11條。
命令均為64字節,具有相同的格式,某些字段根據命令的不同有不同的定義。
Dword0 | CID、傳輸方式、聚合操作、操作碼 |
1 | NID(命名空間ID) |
2 | 保留 |
3 | 保留 |
4、5 | 元數據指針(MPTR) |
6-9 | 數據指針(DPTR) |
10-15 | 根據命令指定 |
完成命令同樣具有相同的格式,某些字段根據命令的不同有不同的定義。
Dword0 | 根據命令指定 |
1 | 保留 |
2 | SQID、SQ頭指針 |
3 | 狀態域、P位、CID |
Admin指令與NVM指令根據放置的的隊列組(Queue Pair)來區分,Admin指令在Admin CQ與SQ里,NVM指令在I/O CQ與SQ里。
通過Dword0中的8位操作碼定義不同指令,注意并不是絕對的順序增加(eg,沒有03h)。每一種指令都對應有其完成命令,通過SQID(提交隊列ID)+CID(命令ID)唯一標識完成的命令。
操作碼 | 指令 | 作用 |
00h | 刪除I/O SQ, | 釋放SQ空間 |
01h | 創建 I/O SQ, | 保存host分配給SQ的地址、隊列優先權、隊列大小 |
02h | 獲取日志, | 返回所選日志頁于緩沖區 |
04h | 刪除 I/O CQ, | 釋放CQ空間 |
05h | 創建 I/O CQ, | 保存host分配給CQ的地址、中斷向量、隊列大小等 |
06h | Identify | 返回關于controller與namespace能力和狀態的數據結構(2k字節) |
08h | 撤銷, | 用來撤銷之前完成的指令,best-effort |
09h | 設置features | 根據FID設置相應的features |
0Ah | 獲取 features, | 根據FID返回隊列數量、仲裁信息等 |
0Ch | 異步事件請求, | Controller向host報告運行信息(error or health) |
10h | 固件激活, | 驗證下載的鏡像,提交到Firmware Slot(1-7)中 |
11h | 固件鏡像下載, | 下載固件鏡像 |
Note:
NVMe控制器讀寫的最小單元是LB,層次圖如下:
NVM指令與Admin指令結構完全相同,也是通過Dword0中的8位操作碼來定義不同指令。
操作碼 | 指令 | 作用 |
00h | Flush | 將數據(和元數據)提交到NVM中,所有命令都要執行 |
01h | Write | 將數據(和元數據)寫入NVM中 |
02h | Read | 讀NVM中的數據(和元數據) |
04h | Wirte Uncorrectable | 標記無效數據塊 |
05h | Compare | 比較從NVM端讀出的數據和比較數據緩沖區的數據 |
09h | Dataset Management | 標識一定范圍數據的特點,eg,頻繁讀、頻繁寫(提升性能) |
控制器從功能上可以分為三類,I/O、Admin和Discovery。
在實現過程中,Admin 控制器只有一個,負責管理控制器及其他控制功能。控制器只是抽象的概念,應用于具體的實現中,可能是一個具體的模塊,也可能多個模塊。
控制器主要的作用是實現對NVMe定義命令的翻譯,從而實現數據傳輸、狀態控制等功能。
1. host將命令(1條或者多條)寫入提前分配好的SQ中;
2. 更新對應SQ的DB寄存器;
3. NVMe控制器取SQ中命令(通過HDB和TDB可以判斷是否有未完成命令);
4. NVMe控制器執行命令;
5. NVMe 控制器在命令完成后,將完成命令(可能執行成功,也可能失敗,但都會返回完成命令)寫入host內存SQ對應的CQ中;
6. NVMe 控制器根據實現的中斷方式,提醒host命令已完成;
7. host響應中斷,處理完成命令;
8. host 更新對應CQ的DB寄存器。
6.2 重啟(Reset)
Controller重啟可能發生在PCIe總線重啟、PCI重啟、控制器CC.EN從1到0重啟。當重啟發生時:
重啟后,host操作:
隊列水平的重啟,即,刪除該隊列,再重新創建一個新隊列。刪除隊列的時候,host應該保證隊列處于idle狀態(所有命令均已完成——接收到了完成命令),否則的話,可能會導致CQ接收不到提交命令的完成命令。
在Controller完成SQ命令后,根據執行狀態,將結果組裝成完成命令寫入CQ中,Controller通過中斷機制通知Host處理完成命令。
NVMe協議中支持的中斷方式有4種,pin-based、Single MSI、Multi-message MSI和MSI-X,協議推薦采用MSI-X中斷方式,能夠支持更多的中斷向量(2K)。
MSI-X允許每一個CQ發送自己的中斷信息(相比于發一條中斷信息提醒全部CQ隊列有很大的優勢)。在產生MSI-X中斷信息前,需要檢查該中斷在相應寄存器種不被屏蔽。
Controller的初始化過程:
Controller 關機
正常關機:
突然關機:
6.6 NVMe與PCIe交互實例(分析包結構)
以Host發出read命令為例。
分析該包,Host需要從起始LBA 0x20E0448(SLBA)上讀取128個DWORD (512字節)的數據,讀到哪里去呢?PRP1給出內存地址是0x14ACCB000。這個命令放在編號為3的SQ里 (SQID=3),CQ編號也是3 (CQID=3)
上圖中,上層是NVMe層,下層是PCIe傳輸層的TLP。Host想往SQ Tail DB中寫入的值是5。PCIe是通過一個Memory Write TLP來實現Host寫CQ的Tail DB的。該Tail DB寄存器映射在Host的內存地址為F7C11018,由于NVMe 的寄存器映射到了Host內存中,所以可以根據這個地址寫入寄存器值。
PCIe是通過發一個Memory Read TLP到Host的SQ中取指的??梢钥吹?,PCIe需要往Host內存中讀取16個DWORD的數據(一個NVMe指令大?。?,
SSD是通過Memory write TLP 把Host命令所需的128個DWORD數據寫入到Host命令所要求的內存中去。SSD每次寫入32個DWORD,一共寫了4次。
SSD是通過Memory write TLP 把16個字節的命令完成狀態信息寫入到Host的CQ中。
上圖使用的是MSI-X中斷方式。這種方式將中斷信息和正常的數據信息一樣,PCIe打包把中斷信息告知Host。SSD還是通過Memory Write TLP把中斷信息告知Host,這個中斷信息長度是1DWORD。
完成:
Host還是通過Memory Write TLP更新SSD端的CQ Head DB。
該過程完整的包流程如下:
7. NVMe features
1. 將固件下載到Controller中(使用 Firmware Image Download命令);
2. Host提交Firmware Activate命令(也可以激活之前版本的Controller鏡像);
3. Controller reset;
4. reset完成后,Host重新初始化Controller,包括Host重新分配I/O隊列,與reset步驟相同。
元數據的使用并沒有強制規定,最經常的使用方法是用做端到端數據的保護信息。有兩種傳輸元數據的方式,一種可以作為LB數據塊的一部分,如下圖:
另一種可以單獨作為一個邏輯塊傳輸,如下圖:
端到端,一端指主機的內存空間,一端指閃存空間(NVM)。數據傳輸的兩個環節如下圖:
數據在PCIe上傳輸的時候,由于信道噪聲的存在(說白了就是存在干擾),可能導致數據出錯;另外,Controller閃存之間,數據也可能發生錯誤。采用元數據進行數據的保護是最常用的一種手段。
充當保護數據角色的元數據結構如下:
其中,Guard為16bit的CRC校驗碼,Application Tag與LBAT相關,Reference Tag將用戶數據和地址(LBA)相關聯。下圖為以512bytes的數據塊為例:
那么按照排列組合,共有四種保護情況(1帶2帶、1不帶2不帶、1帶2不帶、1不帶2帶)。但由于協議中控制保護信息的只有兩個字段(1. 是否采用保護 2. PRACT位),只有三種情況,如下圖(是以寫命令為例,讀命令相同):
原文鏈接:
https://zhuanlan.zhihu.com/p/347599423
作者:Fappy
在過去的2016年,幾乎全球所有的存儲大廠都推出了基于NVMe協議的固態硬盤產品,一時間NVMe協議幾乎成為了行業最為熱門的詞匯。
那么,NVMe協議到底是什么?它和傳統的AHCI協議又有哪些異同?下面,我們一起來看。
NVMe協議的定義及特點
NVMe,全稱為Non-Volatile Memory Express,我們拆開翻譯,Non-Volatile Memory中文譯名為非易失性存儲器。
熟悉存儲的都知道,存儲器根據斷電后是否能夠存儲數據為標準分為易失性和非易失性,我們常用的優盤、閃存卡等存儲產品就是非易失性存儲器,當然固態硬盤產品也是非易失性存儲器了。而此處的Express,就是類似于PCIe中那個e,指的是通道或是規范。
NVMe是基于非易失性存儲器的傳輸規范
一般常識性的理解,NVMe是一種基于非易失性存儲器的傳輸規范,NVMe規范由包含90多家公司在內的工作小組所定制,Intel是主要領頭人,小組成員包括美光、戴爾、三星、Marvell、NetAPP、EMC、IDT等公司。
此規范目的在于充分利用PCI-E通道的低延時以及并行性,還有當代處理器、平臺與應用的并行性,在可控制的存儲成本下,極大的提升固態硬盤的讀寫性能,降低由于AHCI接口帶來的高延時,徹底解放SATA時代固態硬盤的極致性能。
就存儲整個流程來說,NVMe不僅僅是邏輯上的協議接口,還是一種指令標準,一種指定協議,它的出現徹底顛覆了存儲行業長期以來以ATA為核心底層的存儲邏輯,掀起了一場實至名歸的存儲革命。
NVMe協議和SATA的異同
SATA是一種物理接口類型,執行的AHCI協議標準,是目前最為廉價和常見的固態硬盤接口,缺點便是有著6Gbps的極限讀寫限制,無法滿足專業領域對于無延時、極致讀寫的要求。
NVMe協議和SATA的異同
PCIe實際上是通道協議,在物理表現上就是主板上那些PCIe接口。這些通道協議,屬于總線協議,能夠直接連接CPU,因而幾乎沒有延時,成為NVMe標準的絕佳伴侶。而在AHCI標準時代,受制于協議,幾乎無法發揮PCIe的實際性能,同時根據傳輸速度不同,PCIe還可分為X2/X4/X8。
M.2接口,在固態硬盤領域,更多的是用于和傳統的SATA固態硬盤進行區分的名詞。根據主控執行的協議不同,M.2接口又分為NVMe協議以及AHCI協議的固態硬盤。根據協議不同,M.2固態硬盤在性能上也會有著相當的差異。
小結:
基于NVMe協議的PCIe固態硬盤,無論是在性能和實用性上,都遠超傳統基于AHCI的SATA接口固態硬盤,可以說是固態硬盤行業發展的未來。
而傳統的SATA接口固態硬盤,則會在制造成本降低的大背景下,成為普通裝機首選。