塊設備層bdev編程簡介


介紹

塊設備是支持以固定大小的塊讀取和寫入數據的存儲設備。這些塊通常為512或4096字節。設備可以是軟件中的邏輯構造,或者對應於諸如NVMe SSD的物理設備。

塊設備層包含單個通用庫lib/bdev,以及實現各種類型的塊設備的許多可選模塊(作為單獨的庫)。通用庫的公共頭文件是bdev.h,它是與任何類型的塊設備交互所需的全部API。

 

下面將介紹如何使用該API與bdev進行交互。有關實現bdev模塊的指南,請參閱編寫自定義塊設備模塊

除了為所有塊設備提供通用抽象之外,bdev層還提供了許多有用的功能:

  • 響應隊列滿或內存不足的情況自動排隊I / O請求
  • 支持熱移除,即使在I / O流量發生時也是如此。
  • I / O統計信息,如帶寬和延遲
  • 設備重置支持,和I / O超時跟蹤

 

基本原語

bdev API的用戶與許多基本對象進行交互。

struct spdk_bdev,本指南將其稱為bdev,表示通用塊設備。struct spdk_bdev_desc,此前稱為描述符,表示給定塊設備的句柄。描述符用於建立和跟蹤使用底層塊設備的權限,非常類似於UNIX系統上的文件描述符。對塊設備的請求是異步的,由spdk_bdev_io對象表示請求必須在關聯的I / O channel上提交。消息傳遞和並發中描述了I / O channel的動機和設計

Bdev可以是分層的,這樣一些bdev通過將請求路由到其他bdev來服務I / O. 這可用於實現緩存,RAID,邏輯卷管理等。的BDEV該路線I / O到其他的BDEV通常被稱為虛擬的BDEV,或vbdevs的簡稱。

 

初始化庫

bdev層依賴於頭文件include/spdk/thread.h抽象的通用消息傳遞基礎結構。有關完整說明,請參閱消息傳遞和並發最重要的是,只能通過調用spdk_allocate_thread()從已經分配了SPDK的線程調用bdev庫

從分配的線程中,可以通過調用spdk_bdev_initialize()來初始化bdev庫,這是一個異步操作。在調用完成回調之前,不能調用其他bdev庫函數。同樣,要拆除bdev庫,請調用spdk_bdev_finish()

 

發現塊設備

所有塊設備都有一個簡單的字符串名稱。在任何時候,都可以通過調用spdk_bdev_get_by_name()來獲取指向設備對象的指針,或者可以使用spdk_bdev_first()spdk_bdev_next()及其變體來迭代整個bdev集

一些塊設備也可以給出別名,也是字符串名稱。別名的行為類似於符號鏈接 - 它們可以與實名互換使用以查找塊設備。

 

准備使用塊設備

為了將I / O請求發送到塊設備,必須首先通過調用spdk_bdev_open()來打開它這將返回一個描述符。多個用戶可能同時打開bdev,並且用戶之間的讀寫協調必須由bdev層之外的某些更高級別的機制來處理。如果虛擬bdev模塊聲明了bdev,則打開具有寫入權限的bdev可能會失敗虛擬bdev模塊實現RAID或邏輯卷管理之類的邏輯,並將其I / O轉發到較低級別的bdev,因此它們將這些較低級別的bdev標記為聲稱可防止外部用戶發出寫入。

打開塊設備時,可以提供可選的回調和上下文,如果刪除了為塊設備提供服務的底層存儲,則將調用該回調和上下文。例如,當NVMe SSD熱插拔時,將在物理NVMe SSD支持的bdev的每個打開描述符上調用remove回調。回調可以被認為是關閉打開描述符的請求,因此可以釋放其他內存。當存在開放描述符時,不能拆除bdev,因此強烈建議提供回調。

當用戶完成描述符時,他們可以通過調用spdk_bdev_close()來釋放它

描述符可以同時傳遞給多個線程並從中使用。但是,對於每個線程,必須通過調用spdk_bdev_get_io_channel()獲得單獨的I / O channel這將分配必要的每線程資源,以便在不接受鎖定的情況下向bdev提交I / O請求。要釋放channel,請調用spdk_put_io_channel()在銷毀所有相關 channel之前,不能關閉描述符。

SPDK 的I/O 路徑采用無鎖化機制。當多個thread操作同意SPDK 用戶態block device (bdev) 時,SPDK會提供一個I/O channel的概念 (即thread和device的一個mapping關系)。不同的thread 操作同一個device應該擁有不同的I/O channel,每個I/O channel在I/O路徑上使用自己獨立的資源就可以避免資源競爭,從而去除鎖的機制。詳見SPDK進程間的高效通信。

 

發送I / O

一旦一個描述符和一個信道已經獲得,I / O可以通過調用各種I / O功能提交諸如發送spdk_bdev_read() 這些調用都將回調作為參數,稍后將使用spdk_bdev_io對象的句柄調用該參數響應完成,用戶必須調用spdk_bdev_free_io()來釋放資源。在此回調中,用戶還可以使用函數spdk_bdev_io_get_nvme_status()spdk_bdev_io_get_scsi_status()以他們選擇的格式獲取錯誤信息。

通過調用spdk_bdev_read()spdk_bdev_write()等函數來執行I / O提交這些函數將一個指向內存區域的指針或一個描述將被傳輸到塊設備的內存的分散集合列表作為參數。必須通過spdk_dma_malloc()或其變體分配此內存有關內存必須來自特殊分配池的完整說明,請參閱用戶空間驅動程序的內存管理在可能的情況下,內存中的數據將使用直接內存訪問直接傳輸到塊設備這意味着它不會被復制。

所有I / O提交功能都是異步和非阻塞的。它們不會因任何原因阻塞或停止線程。但是,I / O提交功能可能會以兩種方式之一失敗。首先,它們可能會立即失敗並返回錯誤代碼。在這種情況下,將不會調用提供的回調。其次,它們可能異步失敗。在這種情況下,關聯的spdk_bdev_io將傳遞給回調,它將報告錯誤信息。

某些I / O請求類型是可選的,給定的bdev可能不支持。要查詢bdev以獲取其支持的I / O請求類型,請調用spdk_bdev_io_type_supported()

 

重置塊設備

為了處理意外的故障情況,bdev庫提供了一種通過調用spdk_bdev_reset()來執行設備重置的機制這會將消息傳遞給bdev存在I / O channel的每個其他線程,暫停它,然后將重置請求轉發到底層bdev模塊並等待完成。完成后,I / O channel將恢復,重置將完成。bdev模塊中的特定行為是特定於模塊的。例如,NVMe設備將刪除所有隊列對,執行NVMe重置,然后重新創建隊列對並繼續。最重要的是,無論設備類型如何,塊設備的所有未完成的I / O都將在重置完成之前完成。

 

 

從RPC到Bdev

# ./scripts/rpc.py -h

 

例如:./scripts/rpc.py -p 5261 construct_nvme_bdev -b "nvme0" -t "PCIe" -a "0000.04:00:0"

 

scripts/rpc.py

if __name__ == "__main__":
p = subparsers.add_parser('construct_nvme_bdev',
help='Add bdev with nvme backend')
p.add_argument('-b', '--name', help="Name of the bdev", required=True)
p.add_argument('-t', '--trtype',
help='NVMe-oF target trtype: e.g., rdma, pcie', required=True)
p.add_argument('-a', '--traddr',
help='NVMe-oF target address: e.g., an ip address or BDF', required=True)
p.add_argument('-f', '--adrfam',
help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
p.add_argument('-s', '--trsvcid',
help='NVMe-oF target trsvcid: e.g., a port number')
p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
p.set_defaults(func=construct_nvme_bdev)

 

scripts/rpc/bdev.py

def construct_nvme_bdev(args):
params = {'name': args.name,
'trtype': args.trtype,
'traddr': args.traddr}
if args.adrfam:
params['adrfam'] = args.adrfam
if args.trsvcid:
params['trsvcid'] = args.trsvcid
if args.subnqn:
params['subnqn'] = args.subnqn
args.client.call('construct_nvme_bdev', params)

 

Souce Insight
---- construct_nvme_bdev Matches (13 in 8 files) ----
Bdev.py (c:\workspace\spdk20180208\scripts\rpc):def construct_nvme_bdev(args):
Bdev.py (c:\workspace\spdk20180208\scripts\rpc): args.client.call('construct_nvme_bdev', params)
Bdev_nvme_rpc.c (c:\workspace\spdk20180208\lib\bdev\nvme):SPDK_RPC_REGISTER("construct_nvme_bdev", spdk_rpc_construct_nvme_bdev)

 

bdev_nvme_rpc.c

SPDK_RPC_REGISTER("construct_nvme_bdev", spdk_rpc_construct_nvme_bdev)

static void
spdk_rpc_construct_nvme_bdev(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)

{ ......

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM