提要
我們知道,一般的SSD控制器裡面分為前端,核心,後端,如上圖是希捷收購的SandForce SF3700控制器的架構,三星不少SSD主控也是類似,三個部分分別用三個CPU管理。功能分別為:
所以,跟NVMe相關的是前端部分,從本文開始,我們正式進入NVMe內容,開發一個NVMe SSD控制器的前端協議邏輯。
前戲
記得去年阿呆家小呆呆還沒出生,有人忽悠阿呆說胎教很管用。阿呆開始每天背唐詩宋詞,晚上睡前隔著妹子肚皮背給娃聽。不知道他有沒有聽進去,阿呆卻發現原來詩詞意境這麼美,夜裡躺床上聽到雨滴落在別人家的雨篷上,滴滴答答,整個世界在春雨聲中安靜了,從耳朵到內心。我就想起老年的陸遊進京面見宋孝宗,在客店裡聽了一夜的春雨,折騰了大半輩子,他已經不相信有人會派他去收復河山了,反而閒情逸緻寫了首詩,果然宋孝宗找他也沒什麼事。不過那時候的人重視文化,陸遊寫的這首詩很快傳遍了臨安,傳進了皇宮,宋孝宗反覆誦讀,讚嘆不已。所以我建議各位讀者晚上回家少刷朋友圈,看看詩詞,這樣睡得更香,第二天看ssdfans就有精神。
臨安春雨初霽
年代: 宋 作者: 陸遊
世味年來薄似紗,誰令騎馬客京華。
小樓一夜聽春雨,深巷明朝賣杏花。
矮紙斜行閒作草,晴窗細乳戲分茶。
素衣莫起風塵嘆,猶及清明可到家。
最近老是貼代碼,有點枯燥,蛋蛋沒有工作後休息的這幾周ssdfans公眾號訂閱人數增量看來不能達標了,希望大家多看看精華文章,分享一下蛋蛋的精品。不過別擔心,往後不會貼那麼多代碼了(這是真的嗎?)。
不知上次阿呆提醒之後,你有沒有掃一下《蛋蛋讀NVMe之x》系列文章,尤其是最後一篇。從本篇起我們要進入NVMe相關的內容了,冒出個名詞你得有心理準備,比如namespace,這個是《蛋蛋讀NVMe之六》裡面提到的。說牛逼一點,我們要開始做一件激動人心的事:開發一個NVMe Controller了!儘管這個過程比較枯燥,但是只要你耐心看完,就一定有收穫。畢竟理論聯繫實踐才是王道。
NVMEState
今天的任務不重,只是看看NVMEState這個NVMe設備的數據結構。治大國如烹小鮮,NVMe協議很龐雜,但只要我們讀透NVMe的數據結構這把刀,後面看代碼就如庖丁解牛般遊刃有餘。
typedef
struct NVMEState {
PCIDevice dev;
int mmio_index;
void
*bar0;
int bar0_size;
uint8_t nvectors;
/* Space for NVME Ctrl Space except doorbells */
uint8_t *cntrl_reg;
/* Masks for NVME Ctrl Registers */
uint8_t *rw_mask;
/* RW/RO mask */
uint8_t *rwc_mask;
/* RW1C mask */
uint8_t *rws_mask;
/* RW1S mask */
uint8_t *used_mask;
/* Used/Resv mask */
struct nvme_features feature;
NVMEIOCQueue cq[NVME_MAX_QS_ALLOCATED];
NVMEIOSQueue sq[NVME_MAX_QS_ALLOCATED];
DiskInfo *disk;
uint32_t ns_size;
uint32_t num_namespaces;
uint32_t instance;
time_t start_time;
/* Used to store the AQA,ASQ,ACQ between resets */
struct AQState aqstate;
/* TODO
* These pointers have been defined since
* present code uses the older defined strucutres
* which has been replaced by pointers.
* Once each and every reference is replaced by
* offset from cntrl_reg, remove these pointers
* becasue bit field structures are not portable
* especially when the memory locations of the bit fields
* have importance
*/
NVMECtrlCap *ctrlcap;
NVMEVersion *ctrlv;
NVMECtrlConf *cconf;
/* Ctrl configuration */
NVMECtrlStatus *cstatus;
/* Ctrl status */
NVMEAQA *admqattrs;
/* Admin queues attributes. */
QEMUTimer *sq_processing_timer;
int64_t sq_processing_timer_target;
/* Used for PIN based and MSI interrupts */
uint32_t intr_vect;
/* Page Size used by the hardware */
uint32_t page_size;
/* Pointer to Identify Controller Strucutre */
NVMEIdentifyController *idtfy_ctrl;
/* Pointer to Firmware slot info log page */
NVMEFwSlotInfoLog fw_slot_log;
uint8_t last_fw_slot;
uint8_t temp_warn_issued;
QEMUTimer *async_event_timer;
uint16_t async_cid[ASYNC_EVENT_REQ_LIMIT +
1];
uint16_t outstanding_asyncs;
QSIMPLEQ_HEAD(async_queue, AsyncEvent) async_queue;
/* Masks for async event requests */
uint8_t err_sts_mask;
/* error status event mask */
uint8_t smart_mask;
/* smart/health status event mask */
} NVMEState;
一個個來看。
PCIDevice dev
int mmio_index
void
*bar0
int bar0_size
uint8_t nvectors
uint8_t *cntrl_reg
顧名思義,這就是NVMe的控制寄存器指針了。後面四個小兄弟是寄存器的MASK,啥意思,就是每個bit加了個限制。
uint8_t *rw_mask;
/* RW/RO mask */ 可讀寫或只讀
uint8_t *rwc_mask;
/* RW1C mask */ 寫1清零
uint8_t *rws_mask;
/* RW1S mask */ 寫1置1
uint8_t *used_mask;
/* Used/Resv mask */ 是否在用
struct nvme_features {
uint32_t arbitration;
uint32_t power_management;
uint32_t LBA_range_type;
/* uses memory buffer */
uint32_t temperature_threshold;
uint32_t error_recovery;
uint32_t volatile_write_cache;
uint32_t number_of_queues;
uint32_t interrupt_coalescing;
uint32_t interrupt_vector_configuration;
uint32_t write_atomicity;
uint32_t asynchronous_event_configuration;
uint32_t software_progress_marker;
};
NVMEIOSQueue sq[NVME_MAX_QS_ALLOCATED];
uint32_t num_namespaces;
namespace大小和個數。不知道namespace為何物的請查看精華文章,查看《蛋蛋讀NVMe之六》。
NVMEVersion *ctrlv;
NVMECtrlConf *cconf;
/* Ctrl configuration */
NVMECtrlStatus *cstatus;
/* Ctrl status */
NVMEAQA *admqattrs;
/* Admin queues attributes. */
這是NVMe設備5個寄存器的指針,設備初始化的時候把寄存器內存地址賦給它們。不過注釋裡也說了,這種指針表示法可移植性差。以後會直接用控制寄存器的偏移來訪問。
int64_t sq_processing_timer_target;
sq_processing_timer是處理SQ所用的timer,裡面註冊了回調函數,sq_processing_timer_target是觸發timer的時間,當時間到了target之後,timer裡面註冊的回調函數就會被調用。timer的用途很廣,比如讓NVMe控制器定期處理SQ隊列裡的新命令,就可以每次檢查完把target設置為一定時間以後再次觸發,這樣無窮無盡循環下去。
uint32_t intr_vect;
NVMe設備中斷向量。
uint32_t page_size;
NVMe控制器和Host數據交互的頁大小。
NVMe控制器Identify Controller的指針。裡面就是設備相關的一些參數,比如vendor id, subsystem vendor id等,具體定義請參考NVM Express 1.0b Chapter 5.11 Identify command。
NVMe控制器Firmware slot info log的指針。裡面是固件相關的一些參數,比如固件版本。last_fw_slot是上次固件的slot值。
QEMUTimer *async_event_timer;
uint16_t async_cid[ASYNC_EVENT_REQ_LIMIT +
1];
uint16_t outstanding_asyncs;
QSIMPLEQ_HEAD(async_queue, AsyncEvent) async_queue;
這又是一個timer,搞蝦米用的呢?顧名思義,NVMe Admin命令有一個叫Asynchronous Event Request,這個timer就是為這類異步請求設置的。Host有時候給NVMe控制器打招呼:小弟,有個人幫大哥盯著,出現了就報告。NVMe小弟趕快記下來,就是這個異步事件,有空了就留心一下。初始化註冊回調函數,事件發生了就會調用回調函數。不相信?請看《蛋蛋讀NVMe之一》,阿呆幫你把圖貼過來。async_cid是異步請求的command id。outstanding_asyncs是NVMe控制器還沒完成的異步事件數。async_queue就是異步事件隊列。
uint8_t err_sts_mask;
/* error status event mask */
uint8_t smart_mask;
/* smart/health status event mask */
當NVMe出現了error或者smart事件,對應的mask置1.
你是不是還很疑惑PCI的bar和mmio到底是怎麼用的?下期為你解惑。
引用
https://github.com/nvmeqemu
微信搜索ssdfans,掃描或長按下面二維碼圖片關注ssdfans微信公眾號,每天看一篇SSD好文!
你想在SSD領域積累更多人脈、學習核心技術、掌握最新動態?我們建了一個微信群,方便SSD Fans們討論SSD,快閃記憶體和存儲相關的話題。群友既包括各大公司SSD、存儲方面的技術精英,行業大牛,也有很多SSD愛好者。歡迎各位SSD行業人士、使用者和愛好者加入,謝絕獵頭,廣告(可聯繫contact@ssdfans.com通過公眾平臺發布)。 加入後請修改暱稱為:姓名(或暱稱)-公司(或領域)-職業例如:張三-Marvell-SSD FW QA蛋蛋-SSD Controller-FW開發微信加nanoarch為好友拉你入群。轉載請註明來自SSD技術學習網,本文地址:http://www.ssdfans.com/?p=2092除非註明,SSD Fans網站文章均為原創,歡迎轉載,轉載必須註明出處,作者和連結,並保留二維碼圖片!