Linux中nvme驅動詳解
NVMe離不開PCIe,NVMe SSD是PCIe的endpoint。PCIe是x86平臺上一種流行的bus匯流排,由於其Plug and Play的特性,目前很多外設都通過PCI Bus與Host通訊,甚至不少CPU的整合外設都通過PCI Bus連線,如APIC等。
NVMe SSD在PCIe介面上使用新的標準協議NVMe,由大廠Intel推出並交由nvmexpress組織推廣,現在被全球大部分儲存企業採納
1.NVMe Command
NVMe Host(Server)和NVMe Controller(SSD)通過NVMe Command進行資訊互動。NVMe Spec中定義了NVMe Command的格式,佔用64位元組。
NVMe Command分為Admin Command和IO Command兩大類,前者主要是用於配置,後者用於資料傳輸。
NVMe Command是Host與SSD Controller交流的基本單元,應用的I/O請求也要轉化成NVMe Command。
詳見《NVM_Express_Revision》
2.PCI匯流排
在系統啟動時,BIOS會列舉整個PCI的匯流排,之後將掃描到的裝置通過ACPI tables傳給作業系統。當作業系統載入時,PCI Bus驅動則會根據此資訊讀取各個PCI裝置的Header Config空間,從class code暫存器獲得一個特徵值。
class code是PCI bus用來選擇哪個驅動載入裝置的唯一根據。NVMe Spec定義的class code是010802h。NVMe SSD內部的Controller PCIe Header中class code都會設定成010802h。
所以,需要在驅動中指定class code為010802h,將010802h放入pci_driver nvme_driver的id_table。之後當nvme_driver註冊到PCI Bus後,PCI Bus就知道這個驅動是給class code=010802h的裝置使用的。nvme_driver中有一個probe函式,nvme_probe(),這個函式才是真正載入裝置的處理函式。
#define PCI_CLASS_STORAGE_EXPRESS 0x010802
staticconststruct pci_device_id nvme_id_table[] = {
…….
{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS,
……
};
3.單獨編譯NVME驅動
在老版本的原始碼中,可以在原始碼路徑drivers/block中,增加Makefile內容如下,進行編譯:
obj-$(CONFIG_BLK_DEV_NVME) += nvme.o
nvme-objs := nvme-core.o nvme-scsi.o
PWD := $(shell pwd)
default:
make -C /usr/src/kernels/3.10.0-327.x86_64/ M=$(PWD) modules
clean:
rm –rf *.o *.ko
然後直接make 即可生成nvme.ko檔案。
關於Makefile可以參考如下:
KERNELVER ?= $(shell uname -r)
KERNROOT = /lib/modules/$(KERNELVER)/build
nvme:
$(MAKE) -C $(KERNROOT) M=`pwd`/drivers/block
clean:
$(MAKE) -C $(KERNROOT) M=`pwd`/drivers/block clean
主要就兩個檔案:nvme-core.c和nvme-scsi.c。
不過,最新的程式碼位於drivers/nvme/host中,主要是core.c和pci.c。
4.註冊和初始化
我們知道首先是驅動需要註冊到PCI匯流排。那麼nvme_driver是如何註冊的呢?
當驅動被載入時就會呼叫nvme_init(drivers/nvme/host/pci.c)函式。在這個函式中,呼叫了kernel的函式pci_register_driver,註冊nvme_driver,其結構體如下。
staticstruct pci_driver nvme_driver = {
.name = "nvme",
.id_table = nvme_id_table,
.probe = nvme_probe,
.remove = nvme_remove,
.shutdown = nvme_shutdown,
.driver = {
.pm = &nvme_dev_pm_ops,
},
.sriov_configure = nvme_pci_sriov_configure,
.err_handler = &nvme_err_handler,
};
這樣PCI bus上就多了一個pci_driver nvme_driver。當讀到一個裝置的class code是010802h時,就會呼叫這個nvme_driver結構體的probe函式, 也就是說當裝置和驅動匹配了之後,驅動的probe函式就會被呼叫,來實現驅動的載入。
Probe函式主要完成四個工作:
1.對映裝置的bar空間到記憶體虛擬地址空間
2.設定admin queue;
3.新增nvme namespace裝置;
4.新增nvme Controller,提供ioctl介面。
PCIe的Header空間和BAR空間是PCIe的關鍵特性。Header空間是PCIe裝置的通有屬性,所有的PCIe Spec功能和規範都在這裡實現;BAR空間則是裝置差異化的具體體現,BAR空間的定義決定了這個裝置是網絡卡,SSD還是虛擬裝置。BAR空間是Host和PCIe裝置進行資訊互動的重要介質,BAR空間的資料實際儲存在PCIe裝置上。Host這邊給PCIe裝置分配的地址資源,並不佔用Host的記憶體資源。當讀寫BAR空間時,都需要通過PCIe介面(通過PCI TLP訊息)進行實際的資料傳輸。
接著來看下nvme_driver結構體中的.probe函式nvme_probe。
staticint nvme_probe(struct pci_dev *pdev, conststruct pci_device_id *id)
{
int node, result = -ENOMEM;
struct nvme_dev *dev;
unsignedlong quirks = id->driver_data;
node = dev_to_node(&pdev->dev);
if (node == NUMA_NO_NODE)
set_dev_node(&pdev->dev, first_memory_node);
dev = kzalloc_node(sizeof(*dev), GFP_KERNEL, node);
if (!dev)
return -ENOMEM;
dev->queues = kcalloc_node(num_possible_cpus() + 1,
sizeof(struct nvme_queue), GFP_KERNEL, node);
if (!dev->queues)
goto free;
dev->dev = get_device(&pdev->dev);
pci_set_drvdata(pdev, dev);
result = nvme_dev_map(dev);
if (result)
goto put_pci;
INIT_WORK(&dev->ctrl.reset_work, nvme_reset_work);
INIT_WORK(&dev->remove_work, nvme_remove_dead_ctrl_work);
mutex_init(&dev->shutdown_lock);
init_completion(&dev->ioq_wait);
result = nvme_setup_prp_pools(dev);
if (result)
goto unmap;
quirks |= check_vendor_combination_bug(pdev);
result = nvme_init_ctrl(&dev->ctrl, &pdev->dev, &nvme_pci_ctrl_ops,
quirks);
if (result)
goto release_pools;
dev_info(dev->ctrl.device, "pci function %s\n", dev_name(&pdev->dev));
nvme_reset_ctrl(&dev->ctrl);
return0;
release_pools:
nvme_release_prp_pools(dev);
unmap:
nvme_dev_unmap(dev);
put_pci:
put_device(dev->dev);
free:
kfree(dev->queues);
kfree(dev);
return result;
}
nvme_probe函式會通過nvme_dev_map函式(層層呼叫之後)對映裝置的bar空間到核心的虛擬地址空間當中, pci協議裡規定了pci裝置的配置空間裡有6個32位的bar暫存器,代表了pci裝置上的一段記憶體空間,可以通過writel, readl這類函式直接讀寫暫存器。
並分配裝置資料結構nvme_dev,佇列nvme_queue等,結構體如下。
structnvme_dev {
struct nvme_queue *queues;
struct blk_mq_tag_set tagset;
struct blk_mq_tag_set admin_tagset;
u32 __iomem *dbs;
struct device *dev;
struct dma_pool *prp_page_pool;
struct dma_pool *prp_small_pool;
unsigned online_queues;
unsigned max_qid;
int q_depth;
u32 db_stride;
void __iomem *bar;
unsignedlong bar_mapped_size;
struct work_struct remove_work;
struct mutex shutdown_lock;
bool subsystem;
void __iomem *cmb;
pci_bus_addr_t cmb_bus_addr;
u64 cmb_size;
u32 cmbsz;
u32 cmbloc;
struct nvme_ctrl ctrl;
struct completion ioq_wait;
/* shadow doorbell buffer support: */
u32 *dbbuf_dbs;
dma_addr_t dbbuf_dbs_dma_addr;
u32 *dbbuf_eis;
dma_addr_t dbbuf_eis_dma_addr;
/* host memory buffer support: */
u64 host_mem_size;
u32 nr_host_mem_descs;
dma_addr_t host_mem_descs_dma;
struct nvme_host_mem_buf_desc *host_mem_descs;
void **host_mem_desc_bufs;
};
每個裝置至少兩個佇列,一個是admin管理命令,一個是給I/O命令,這個佇列概念和之前介紹塊驅動中的磁碟佇列一個道理,只是那個驅動比較基礎,所以命令和IO並不區分佇列,具體結構體如下。
structnvme_queue {
struct device *q_dmadev;
struct nvme_dev *dev;
spinlock_t q_lock;
struct nvme_command *sq_cmds;
struct nvme_command __iomem *sq_cmds_io;
volatilestruct nvme_completion *cqes;
struct blk_mq_tags **tags;
dma_addr_t sq_dma_addr;
dma_addr_t cq_dma_addr;
u32 __iomem *q_db;
u16 q_depth;
s16 cq_vector;
u16 sq_tail;
u16 cq_head;
u16 qid;
u8 cq_phase;
u8 cqe_seen;
u32 *dbbuf_sq_db;
u32 *dbbuf_cq_db;
u32 *dbbuf_sq_ei;
u32 *dbbuf_cq_ei;
};
繼續說nvme_probe函式,nvme_setup_prp_pools,主要是建立dma pool,後面可以通過dma函式從dma pool中獲得memory。主要是為了給4k和128k的不同IO來做優化。
nvme_init_ctrl函式會建立NVMe控制器結構體,這樣在後後續probe階段時候用初始化過的結構,其傳入的操作函式集是nvme_pci_ctrl_ops。
staticconststruct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.name = "pcie",
相關推薦
Linux中nvme驅動詳解
NVMe離不開PCIe,NVMe SSD是PCIe的endpoint。PCIe是x86平臺上一種流行的bus匯流排,由於其Plug and Play的特性,目前很多外設都通過PCI Bus與Host通訊,甚至不少CPU的整合外設都通過PCI Bus連線,如APIC等。 NV
linux中expect命令詳解
linux運維expect介紹expect 是由Don Libes基於Tcl(Tool Command Language )語言開發的,主要應用於自動化交互式操作的場景,借助Expect處理交互的命令,可以將交互過程如:ssh登錄,ftp登錄等寫在一個腳本上,使之自動化完成。尤其適用於需要對多臺服務器執行相同
linux中at命令詳解
at一次性計劃任務 at詳解 系統命令 at命令: 一:簡介: 計劃任務,在特定的時間執行某項工作,在特定的時間執行一次,需要安裝at服務,apt-get install at 二:時間定義: at允許使用一套相當復雜的指定時間的方法。● 能夠接受在當天的hh:mm(小時:分鐘)式的時間指定。假如
Linux中seq命令詳解
seq命令可以輸出連續的數字,或固定間隔的數,或者是輸出指定格式的數字 例子: [[email protected] Desktop]$ seq 1 5 1 2 3 4 5 [[email protected] Desktop]$ seq 1 2 5 1 3
linux中apache服務詳解4(企業級)ssl
ssl加密 yum install mod_ssl -y 他是一個模組 yum install crypto-utils -y 加密 genkey www.westos.com
linux中apache服務詳解3(企業級)cgi
對於cgi表單 mkdir -p /var/www/html/cgi semaneger fcontent -a -t httpd_sys_script_exec_t '/var/www/html/cgi(/.*)?' restorecon -Rvvf /va
linux中apache服務詳解1(企業級)(http\cgi\php\ssl)
curl -I www.jd.com 檢視網站用的哪些服務 curl -I www.taobao.com firewall-config runtime 當前允許的狀態 permanent 永久允許的 html超文字標記語言 yum install httpd htt
linux中fork() 函式詳解
fork入門知識 一個程序,包括程式碼、資料和分配給程序的資源。fork()函式通過系統呼叫建立一個與原來程序幾乎完全相同的程序,也就是兩個程序可以做完全相同的事,但如果初始引數或者傳入的變數不同,兩個程序也可以做不同的事。 一個程序呼叫fork()函式後,系統先給新的程序分配資源,例如儲存資料和程式碼的
輸入子系統------鍵盤按鍵驅動程式 13.Linux鍵盤按鍵驅動 (詳解)
由上一節的輸入子系統的框架分析可知,其分三層:裝置驅動層,核心層,事件驅動層 我們在為某種裝置的編寫驅動層,只需要關心裝置驅動層,即如何驅動裝置並獲得硬體資料(如按下的按鍵資料),然後呼叫核心層提供的介面,核心層就會自動把資料提交給事件處理層。在輸入子系統中,事件驅動是標準的,適用於所有輸入類的。
linux中btt工具詳解
在之前的文章中介紹瞭如何使用blktrace 以及其工作原理和架構。我們知道blktrace 跟蹤塊裝置的統計資訊,每個CPU會有一個檔案儲存,然後通過blkparse可以將這些檔案整合成一個檔案來顯示。 不過blkparse顯示的檔案過於龐大,而通
Linux中dd命令詳解
一、dd命令 dd:用指定大小的塊拷貝一個檔案,並在拷貝的同時進行指定的轉換。 注意:指定數字的地方若以下列字元結尾,則乘以相應的數字:b=512;c=1;k=1024;w=2 引數註釋: if=檔名:輸入檔名,預設為標準輸入。即指定原始檔。< i
6410 實現 linux 串列埠驅動詳解
為了實現串列埠通訊,需要在嵌入式linux下編寫相應的驅動程式。在嵌入式系統中,串列埠被看做終端裝置tty。終端裝置是unix體系中一個非常重要的物件,內容非常複雜,它是整個unix人機互動的基礎,其地位並不亞於檔案系統在作業系統中的作用。筆者muge0913在此對uar
nvme 驅動詳解 之1
按照老的套路,在分析一個driver時,我們首先看這個driver相關的kconfig及Makefile檔案,察看相關的原始碼檔案. 在開始閱讀一個driver,通常都是從module_initor syscall_init函式看起。 下面讓我們開始nvme的旅程吧。 首
Linux塊裝置驅動詳解(一)
請求佇列跟蹤等候的塊I/O請求,它儲存用於描述這個裝置能夠支援的請求的型別資訊、它們的最大大小、多少不同的段可進入一個請求、硬體扇區大小、對齊要求等引數,其結果是:如果請求佇列被配置正確了,它不會交給該裝置一個不能處理的請求。 請求佇列還實現一個插入介面,這個介面允許使用多個I/O排程器,I/
很好的linux下GPIO驅動詳解文章
打算跟著友善之臂的《mini2440 linux移植開發指南》來做個LED驅動,雖然LED的原理簡單得不能再簡單了,但是要把kernel中針對於s3c24**的GPIO的一些資料結構,還有函式搞清楚也不是那麼輕鬆的事,所以本文主要簡單地說明下LED驅動中的相關資料結構以及
Linux中ps命令詳解
1. 執行(正在執行或在執行佇列中等待) 2. 中斷(休眠中, 受阻, 在等待某個條件的形成或接受到訊號) 3. 不可中斷(收到訊號不喚醒和不可執行, 程序必須等待直到有中斷髮生) 4. 僵死(程序已終止, 但程序描述符存在, 直到父程序呼叫wait4()系統呼叫後釋放) 5. 停止(程序收到
Linux中make命令詳解
原文地址:https://www.computerhope.com/unix/umake.htm About make make is a utility(實用的) for building and maintaining groups of programs
Linux中vi命令詳解
最近vi用的多,很多技巧不知道,備註一份, vi編輯器是所有Unix及Linux系統下標準的編輯器,它的強大不遜色於任何最新的文字編輯器,這裡只是簡單地介紹一下它的用法和一小部分指令。由於 對Unix及Linux系統的任何版本,vi編輯器是完全相同的,因此您可以在其他任
Linux中TFTP使用詳解
FTP協議簡介 TFTP是用來下載遠端檔案的最簡單網路協議,它其於UDP協議而實現。 linux伺服器端tftp-server的配置 1、安裝tftp伺服器需要安裝xinetd(守護tftp)、tftp和tftp-server 3個軟體1)如果能上網,通過yum安裝
linux中awk命令詳解
簡介awk是一個強大的文字分析工具,相對於grep的查詢,sed的編輯,awk在其對資料分析並生成報告時,顯得尤為強大。簡單來說awk就是把檔案逐行的讀入,以空格為預設分隔符將每行切片,切開的部分再進行各種分析處理。awk有3個不同版本: awk、nawk和gawk,未作特別