2023年10月11日 星期三

解析 NVM Express - 透過Linux OS 解析M.2 NVMe SSD

在之前,我撰寫了三篇有關NVMe的文章 ,分別是"原理NVM Express - NVMe Submission Queue & Completion Queue (SQ & CQ)"、"原理NVM Express - Admin Command Set"和"原理NVM Express - NVM Command Set",只要有了這三篇的基本知識,我們就有足夠的能力可以去解析我們主機板上任何一個M.2 NVMe device的一些資訊和如何操作它。接下來這篇文章會使用x86系統並安裝Ubuntu OS,然後隨意安裝市面上的一款M.2 NVMe SSD到主機板上,透過Linux command line的方式來解析此M.2 NVMe SSD的能力和如何透過I/O command去讀寫namespace,並且提供一些在Linux中非常實用的CLI。


■ Parsing PCIe Part

首先透過lspci來展列所有系統上的pcie device(如圖1所示),bus 01/ dev 00 /func 0(01:00.0)Non-Volatile memory controller為我們的目標device

Figure 1. List of PCIe device

接下來我們可以透過lspci的指令來dump device的PCIe configuration space(如圖2),offset 10h-17h的地方為bar0,bar0所指向的memory address為存放NVMe register的空間,可以看到圖中其值為0x00000000a0500004。接下來可以查看這個device的capability list,PCIe的capability list從offset 34h開始,然後如資料結構的linked list的方法一路往下串聯,如圖3所示,[7:0]為Capability ID,主要表示當前所指到的是哪一個Capability ,[15:8]為Next Capability Pointer,主要表示下一個Capability的offset是多少。由圖2中可以看到34h的值為0x80,意思就是第一個capability list在offset 80h的地方,然後80h的值為Capability ID 0x10,0x10代表是PCI Express Capability List Register,然後下一個capability在offset d0h的位置。offset d0h的Capability ID值為0x11,0x11為MSI-X Capability,以此類推。

Figure 2. PCIe Configuration Space


Figure 3. PCIe Capability List Register

如果上述lspci所dump的binary方式不好看的話,其實有另外一種方式可以讓開發者可以更快地去得知每個register的狀態,指令為lspci -s 01:00.0 -vvvv(如圖4)。
Figure 4. PCIe Configuration Space in more verbose way

■ Dump NVMe Registers

如上面所提到的bar0為存放NVMe register的地方,接下來我們用Linux tool "devmem2"來dump NVMe registers(如圖5所示)。
Figure 5. PCIe Capability List Register

首先Offset 00h-07h的位置為Controller Capabilities register,其值為0x30400303FF,底下介紹此controller所support的能力有哪些:
  • [15:0] Maximum Queue Entries Supported的值為0x3FF,代表這個controller可以支援queue的深度為何,此controller最多可以處理1024筆command。
  • [16] Contiguous Queues Required值為1,表示host在create I/O SQ 和 CQ的時候,必須提供物理連續的memory。
  • [17] Arbitration Mechanism Supported為1,表示此controller處理command的時候會以Weighted Round Robin的仲裁方式。
  • [31:24] Timeout為0x40,表示建議host等待command完成的最大時間,單位為500ms,所以為0x40 x 500 = 32s。
  • [36] NVM Subsystem Reset Supported為1,表示support NSSR reset。
  • [44:37] Command Sets Supported為1,表示support NVM command set。

Offset 08h-0Bh為Version register,其值為0x10400,表示此controller相容於1.4的NVMe spec。

Offset 14h-17h為Controller Configuration,其值為0x460001,bit0為Enable bit,為1表示現在controller為應該要可以處理command的狀態,但還是得等到CSTS.RDY bit為1後host才可以開始提交command。bit [19:16] I/O Submission Queue Entry Size,表示SQ一個entry為多少bytes,0x6代表2^6 = 64 bytes per command entry。bit [23:20] I/O Completion Queue Entry Size表示CQ一個entry為多少bytes,0x4代表2^4= 16 bytes per completion entry。

Offset 1Ch-1Fh為Controller Status,bit [0] 用來表示controller現在可以開始接收command,主要會在host下CC.EN的時候才會轉變為1。

Offset 24h-27h為Admin Queue Attributes,其值為0x001F001F,表示Admin Queue的max entry為何,此controller的Admin SQ & CQ都為32個entries。

Offset 28h-2Fh為Admin Submission Queue Base Address,其值為0x3F204B000,表示host提交admin command的memory位置為何。

Offset 30h-37h為Admin Completion Queue Base Address,其值為0x3F204C000,表示controller回覆command completion的memory位置為何。

■ Dump Admin SQ(ASQ) base address

由於上述我們已得知Admin SQ(ASQ) base address位置且也知道ASQ一個entry為64 bytes,我們可以透過devmem2來dump ASQ的memory(如圖6),由圖中可以得知host提交到第一個entry的command為Opcode 02h,也就是Get Log Page command,其中NSID欄位為broadcast(0xFFFFFFFF),Log Page ID欄位為0x2,詳細的格式可以參考"原理NVM Express - Admin Command Set"這篇文章。
Figure 6. Admin Submission Queue Memory Space

■ Dump Admin CQ(ACQ) base address

得知ASQ後,我們一樣可以透過ACQ base address來得知controller回覆了哪些completion,每一個entry為16 bytes,如圖7所示,controller所回覆到第一個entry的Command ID為0x18的command,然後status為Successful Completion(0x0000),詳細的格式可以參考"原理NVM Express - NVMe Submission Queue & Completion Queue (SQ & CQ)"這篇文章。

Figure 7. Admin Completion Queue Memory Space

■ NVME CLI

在host OS之中,除了NVMe driver會提交command之外,還有一個tool提供開發者可以手動提交command,那就是NVMe CLI,由於我的系統是Ubuntu,可以透過sudo apt-get install nvme-cli來安裝。安裝完之後可以透過nvme --help得到此CLI的使用說明(如圖8)。
Figure 8. NVME CLI

在使用nvme cli之前,我們可以透過ls /dev/nvme*來得知nvme的裝置名稱,如圖9所示,/dev/nvme0為controller,/dev/nvme0n1為namespace,如之前所撰寫的文章"原理NVM Express - NVM Command Set"有提到,namespace為LBA的集合,當namespace attach到controller之後,我們才能透過controller下I/O cmd來讀寫namespace,此範例為nvme0n1 attach到nvme0的情況。

Figure 9. List NVMe device

在之前所撰寫"原理NVM Express - Admin Command Set"文章裡有提到兩個很重要的data structure,分別是Identify Controller Data Structure (ICDS)和Identify Namespace Data Structure (INDS),ICDS主要是描述controller的一些資訊及能力,INDS主要是為了回報給host此namespace的一些資訊及能力。

ICDS可以使用nvme id-ctrl /dev/nvme0來對controller提交指令,其詳細內容如下,主要挑幾個欄位來解析:
  • mdts欄位為6,表示Maximum Data Transfer Size為4096 x (2^6) = 262,144 bytes。
  • oacs欄位為 0x17,可以得知此controller support Security SendSecurity ReceiveFormat NVMFirmware Commit and Firmware Image DownloadDevice Self-test command。
  • npss欄位為4,表示power state有PS1~PS4。
  • oncs欄位為0x5f,表示support CompareWrite UncorrectableDataset ManagementWrite Zeroes command

root@i:~# nvme id-ctrl /dev/nvme0

NVME Identify Controller:
vid      : 0x1987
ssvid  : 0x1987
sn       : TPBF2201280010102442
mn     : T-FORCE TM8FPL500G
fr         : EJFM32.0
rab      : 4
ieee    : 6479a7
cmic    : 0
mdts    : 6
cntlid  : 0
ver     : 10400
rtd3r   : 7a120
rtd3e   : 4c4b40
oaes    : 0x200
ctratt  : 0x2
oacs    : 0x17
acl     : 3
aerl    : 3
frmw    : 0x12
lpa     : 0x1e
elpe    : 62
npss    : 4
avscc   : 0x1
apsta   : 0x1
wctemp  : 357
cctemp  : 361
mtfa    : 100
hmpre   : 16384
hmmin   : 16384
tnvmcap : 0
unvmcap : 0
rpmbs   : 0
edstt   : 30
dsto    : 0
fwug    : 4
kas     : 0
hctma   : 0x1
mntmt   : 273
mxtmt   : 356
sanicap : 0xa0000002
hmminds : 1024
hmmaxd  : 16
sqes    : 0x66
cqes    : 0x44
maxcmd  : 256
nn      : 1
oncs    : 0x5f
fuses   : 0
fna     : 0
vwc     : 0x7
awun    : 255
awupf   : 0
nvscc   : 1
acwu    : 0
sgls    : 0
subnqn  : nqn.2020-11.com.phison:nvme:PS5019:TPBF2201280010102442
ioccsz  : 0
iorcsz  : 0
icdoff  : 0
ctrattr : 0
msdbd   : 0
ps    0 : mp:5.00W operational enlat:0 exlat:0 rrt:0 rrl:0
          rwt:0 rwl:0 idle_power:- active_power:-
ps    1 : mp:2.40W operational enlat:0 exlat:0 rrt:1 rrl:1
          rwt:1 rwl:1 idle_power:- active_power:-
ps    2 : mp:1.92W operational enlat:0 exlat:0 rrt:2 rrl:2
          rwt:2 rwl:2 idle_power:- active_power:-
ps    3 : mp:0.0700W non-operational enlat:1000 exlat:1000 rrt:3 rrl:3
          rwt:3 rwl:3 idle_power:- active_power:-
ps    4 : mp:0.0050W non-operational enlat:10000 exlat:40000 rrt:4 rrl:4
          rwt:4 rwl:4 idle_power:- active_power:-


INDS可以透過nvme id-ns /dev/nvme0 -n 0x1來對controller提交指令,其詳細內容如下,主要挑幾個欄位來解析:
  • nsze欄位為0x3a386030,其值單位為sector,一個sector為512 bytes,所以其容量為0x3a386030 x 512 = 500,107,862,016 bytes = 500 GB
  • nlbaf欄位為1,表示額外support 1種 LBA format,然後format資訊紀錄在最下面"lbaf  1"的位置,透過lbads:12可以得知此format的LBA為4096 bytes per sector。
  • flbas欄位為0,表示目前使用的是LBA format 0。

root@i:~# nvme id-ns /dev/nvme0 -n 0x1
NVME Identify Namespace 1:
nsze    : 0x3a386030
ncap    : 0x3a386030
nuse    : 0x3a386030
nsfeat  : 0
nlbaf   : 1
flbas   : 0
mc      : 0
dpc     : 0
dps     : 0
nmic    : 0
rescap  : 0
fpi     : 0
nawun   : 0
nawupf  : 0
nacwu   : 0
nabsn   : 0
nabo    : 0
nabspf  : 0
noiob   : 0
nvmcap  : 0
nguid   : 00000000000000016479a7d9d02028bb
eui64   : 6479a75dd02028bb
lbaf  0 : ms:0   lbads:9  rp:0x1 (in use)
lbaf  1 : ms:0   lbads:12 rp:0  


然後透過"nvme smart-log /dev/nvme0 -o normal"來取得SMART / Health Information Log Page,其內容如下:

root@i:~# nvme smart-log /dev/nvme0 -o normal
Smart Log for NVME device:nvme0 namespace-id:ffffffff
critical_warning                      : 0
temperature                            : 36 C
available_spare                       : 100%
available_spare_threshold   : 5%
percentage_used                   : 0%
data_units_read                      : 21,8955
data_units_written                 : 19,7982
host_read_commands          : 155,2872
host_write_commands         : 96,1765
controller_busy_time             : 26
power_cycles                            : 19
power_on_hours                      : 1013
unsafe_shutdowns                  : 5
media_errors                             : 0
num_err_log_entries               : 0
Warning Temperature Time            : 0
Critical Composite Temperature Time    : 0
Thermal Management T1 Trans Count    : 0
Thermal Management T2 Trans Count   : 0
Thermal Management T1 Total Time       : 0
Thermal Management T2 Total Time      : 0


■ NVME CLI - I/O Command

而nvme cli不只可以提交admin command,且也可以提交I/O command,如以下圖10範例,透過nvme cli對namespace nvme0n1下read command,SLBA(-s)從0x0開始,然後NLB(-c)為15,也就是16個sector,16個sector相當於8192 bytes(-z),"-d"則是將讀出的資料寫為任意檔名的檔案,由圖中可以看出目前的資料是沒有規則的。

Figure 10. Read Command

接下來如圖11,我們透過write command來覆寫剛剛的SLBA和NLB所指定的LBA range,一樣指定namespace nvme0n1,SLBA 0x0、NLB 15和data size 8192 bytes,write command的傳入參數"-d"則是我們自己帶入想寫入的資料,所以我自己產生了一個全為0x5a的8k.bin的檔案,帶入write command之中,當回報Success代表8192 bytes的0x5a pattern已全數寫入nand之中,接著我們再透過read command將資料讀出來,則時候可以發現所指定的LBA range,資料已經全部變成0x5a。

Figure 11. Write Command

由於Maximum Data Transfer Size(MDTS)為 262,144 bytes,因此我們可以透過command來測試此上限,如圖12所示,當我們上限設為262,144的時候,command可以正常執行,當超過262,144的時候,則會回報Invalid argument
Figure 12. MDTS limitation




11 則留言:

  1. 您好
    感謝大大分享好文,不過在文章中
    [44:37] Command Sets Supported為1,表示support NVM command set。=> 是否有誤正確因該為[39:37]

    回覆刪除
    回覆
    1. 我看了一下nvme spec 1.4c , 是bit [44:37]沒錯,請問你是看哪一個部分?

      刪除
  2. 大大您好
    另外想請問dumpMem.sh 這一個執行檔,可以從哪裡調用,我安裝devmem2之後,好像也是不能像圖上這樣發送commands。

    回覆刪除
    回覆
    1. dumpMem.sh是我自己寫的shell script,只是用devmem2 去dump一段區間的memory而已,你需要可以提供信箱,我寄給你

      刪除
    2. 我的信箱如下,謝謝您的回覆。
      josh.lin70@gmail.com

      刪除
  3. 大大你好, 請問你知道有甚麼tool可以手動切換D0~D3 State和查看L0~L1的當前狀態嗎?謝謝

    回覆刪除
    回覆
    1. D state的話可以用lspci -s xx:xx.x -vvvv 找出Power Management的capability offset,以下面
      為範例就是offset e0h的位置,然後+4(e4h)就是Power Management Control/Status Register,在bit[1:0]寫入你要的power state就可以了,寫入的方法用setpci -s xx:xx.x e4.b=3

      至於L state好像沒辦法直接控制,好像也沒有register status可以看
      範例:
      Capabilities: [e0] Power Management version 3
      Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
      Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-

      刪除
    2. 感謝您的回覆, 我在來試試看

      刪除
    3. 大大~請教一下, nvme cli可以設定CSTS的bit嗎?我查了一下查不到.

      刪除
    4. CSTS想要寫的話可能要透過devmem2, bar0的位置就是指到nvme registers,上面例子是0xa0500000,CSTS在offset 0x1C-0x1F,所以指令就是$devmem2 0xa050001C w "data in hex"

      刪除
    5. 謝謝您的回覆, 另外可以跟您索取dumpMem.sh嗎?devmem2讀取起來沒有您寫的sh方便Orz
      如果可以的話麻煩您寄到wa741963@gmail.com
      謝謝

      刪除

解析 NVM Express - 透過Linux OS 解析M.2 NVMe SSD

在之前,我撰寫了三篇有關NVMe的文章 ,分別是" 原理NVM Express - NVMe Submission Queue & Completion Queue (SQ & CQ) "、" 原理NVM Express - Admi...