struct spdk_nvme_probe_ctx {
struct spdk_nvme_transport_id trid;
void *cb_ctx;
spdk_nvme_probe_cb probe_cb;
spdk_nvme_attach_cb attach_cb;
spdk_nvme_remove_cb remove_cb;
TAILQ_HEAD(, spdk_nvme_ctrlr) init_ctrlrs;
};
rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); 20.02版本的呼叫 if (spdk_nvme_probe(NULL, probe_ctx, probe_cb, attach_cb, remove_cb)) 20.10中已刪除 probe_cb
spdk_nvme_trid_populate_transport 初始化trid 設定pcie型別SPDK_NVME_TRANSPORT_NAME_PCIE
probe_ctx = spdk_nvme_probe_async(trid, cb_ctx, probe_cb, attach_cb, remove_cb); 開始進行probe_ctx的初始化
nvme_driver_init 初始化g_spdk_nvme_driver的全域性變數,其中包括鎖,driver佇列,熱插拔fd的connect獲取,uuid的初始化 只允許一個程式去做,加鎖,避免引起混亂
probe_ctx = calloc(1, sizeof(*probe_ctx)); 建立上下文 型別為:struct spdk_nvme_probe_ctx
nvme_probe_ctx_init(probe_ctx, trid, cb_ctx, probe_cb, attach_cb, remove_cb); 變數的賦值,初始化probe_ctx->init_ctrlrs的佇列,用來存放需要初始化的nvme 控制器
rc = nvme_probe_internal(probe_ctx, false);
nvme_transport_ctrlr_scan(probe_ctx, direct_connect);
nvme_pcie_ctrlr_scan
enum_ctx.probe_ctx = probe_ctx;
bool enum_ctx.has_pci_addr ? 判斷probe_ctx->trid.traddr中內容是否為空, 不為空,說明有特殊指定pci,則呼叫spdk_pci_device_attach,為空說明沒有特殊指定pci 則呼叫spdk_pci_enumerate
spdk_pci_enumerate(spdk_pci_nvme_get_driver(), pcie_nvme_enum_cb, &enum_ctx); 本example傳入的引數為NULL,則直接執行enumerate流程
cleanup_pci_devices 清除pci裝置,狀態dev->internal.removed為true的,從g_pci_devices中移除,在熱插拔連結串列g_pci_hotplugged_devices中遍歷,移除該佇列並插入到g_pci_devices的尾部
TAILQ_FOREACH(dev, &g_pci_devices, internal.tailq) { 開始遍歷g_pci_devices,過程中需要加g_pci_mutex鎖,防止列表資訊變化。 實際上此時g_pci_devices為空,直接退出
}
scan_pci_bus 重新把所有的bus上的裝置掃一遍,主要是pci bus
driver->cb_fn = enum_cb;
driver->cb_arg = enum_ctx; 把pcie_nvme_enum_cb和arg傳入作為nvme driver的回撥
rte_bus_probe 呼叫pci_probe 把pci裝置進行繫結
----------------------------------------------------------------------------------------------------------一個個匹配,所以以下流程對於滿足條件的pci裝置會走多次
rte_pci_probe_one_driver device和驅動匹配上,如果不是匹配的,則退出。
rte_pci_map_device 進行pci地址對映,以在使用者空間訪問
ret = dr->probe(dr, dev); 呼叫pci_device_init函式,作為driver的probe。 pci_env_init函式中進行的probe函式的指定
pci_device_init
rc = driver->cb_fn(driver->cb_arg, dev); 主要進行addr等基礎資訊的賦值傳遞,同時執行一開始傳入的回撥函式
pcie_nvme_enum_cb
nvme_get_ctrlr_by_trid_unsafe 去g_nvme_attached_ctrlrs和g_spdk_nvme_driver->shared_attached_ctrlrs兩個連結串列中搜尋ctrlr,用來進行判斷是否已建立
nvme_ctrlr_probe(&trid, enum_ctx->probe_ctx, pci_dev); 有一個條件,使用者傳入過pci,則只建立傳入的pci的ctrlr,否則全部建立
spdk_nvme_ctrlr_get_default_ctrlr_opts 獲取預設的ctrlr的opts引數
probe_ctx->probe_cb(probe_ctx->cb_ctx, trid, &opts) 呼叫傳入的probe_cb,列印了"Attaching to %s\n", trid->traddr
ctrlr = nvme_transport_ctrlr_construct(trid, &opts, devhandle); 開始建立ctrlr
spdk_pci_device_claim(pci_dev) 先claim pci裝置,保證唯一性
pctrlr = spdk_zmalloc(sizeof(struct nvme_pcie_ctrlr), 64, NULL, 建立nvme的ctrlr
SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE);
rc = nvme_ctrlr_construct(&pctrlr->ctrlr); 初始化spdk_nvme_ctrlr的所有資訊,初始化ctrlr->ctrlr_lock、active_procs等資源
rc = nvme_pcie_ctrlr_allocate_bars(pctrlr);
rc = spdk_pci_device_map_bar(pctrlr->devhandle, 0, &addr, &phys_addr, &size); 把之前對映好的地址讀取過來,儲存在引數中
pctrlr->regs = (volatile struct spdk_nvme_registers *)addr; 關聯pctrlr的regs和實際的pci的addr
pctrlr->regs_size = size; 賦值暫存器的size
nvme_pcie_ctrlr_map_cmb(pctrlr); 把addr、phys_addr、size、offset進行傳遞
spdk_pci_device_cfg_read16(pci_dev, &cmd_reg, 4); 讀取中斷管理的fd,寫入404,使中斷失效
cmd_reg |= 0x404;
spdk_pci_device_cfg_write16(pci_dev, cmd_reg, 4);
nvme_ctrlr_get_cap(&pctrlr->ctrlr, &cap)
nvme_ctrlr_get_vs(&pctrlr->ctrlr, &vs)
nvme_ctrlr_init_cap(&pctrlr->ctrlr, &cap, &vs); 通過暫存器讀取的方式獲取cap和vs資訊,初始化cap。主要資訊是page_size、io_queue_size、io_queue_requests
rc = nvme_pcie_ctrlr_construct_admin_qpair(&pctrlr->ctrlr, pctrlr->ctrlr.opts.admin_queue_size); 建立管理佇列qpair
pqpair = spdk_zmalloc(sizeof(*pqpair), 64, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE); 使用的是大頁記憶體
rc = nvme_qpair_init(ctrlr->adminq, 0, ctrlr, SPDK_NVME_QPRIO_URGENT, num_entries); 初始化qpair的下屬佇列等,0號qpair就是管理佇列
nvme_pcie_qpair_construct(ctrlr->adminq, NULL); 建立其他qpair所需的資訊max_completions_cap、tracker等,tracker是一次性分配該qpair的所有數量,因為記憶體對齊和邊界的要求。
然後將tracker裝入到qpair中的tracker陣列指標中進行儲存,並插入free_tr中進行記錄
nvme_pcie_qpair_reset(qpair); qpair中的佇列資訊清零
rc = nvme_ctrlr_add_process(&pctrlr->ctrlr, pci_dev); 建立程式資訊,用於多程式下的ctrlr管理
ctrlr->remove_cb = probe_ctx->remove_cb; remove_cb的傳入
ctrlr->cb_ctx = probe_ctx->cb_ctx;
nvme_qpair_set_state(ctrlr->adminq, NVME_QPAIR_ENABLED); 設定qpair的狀態
TAILQ_INSERT_TAIL(&probe_ctx->init_ctrlrs, ctrlr, tailq); 插入控制器到probe_ctx->init_ctrlrs中,用於後續的狀態初始化
TAILQ_INSERT_TAIL(&g_pci_hotplugged_devices, dev, internal.tailq); 插入到熱插拔的佇列中
--------------------------------------------------------------------------------------------------------------------------------
cleanup_pci_devices(); 把新probe的控制器給放入g_pci_devices中管理(此前為空)
nvme_init_controllers(probe_ctx);
rc = spdk_nvme_probe_poll_async(probe_ctx);
------------------------------------------------------------TAILQ_FOREACH_SAFE(ctrlr, &probe_ctx->init_ctrlrs, tailq, ctrlr_tmp) { 對每一個在init_ctrlrs佇列中的ctrlr
nvme_ctrlr_poll_internal(ctrlr, probe_ctx); 對每個在probe_ctx->init_ctrlrs的ctrlr執行,直到該佇列為空,設定g_spdk_nvme_driver->initialized = true;返回0
rc = nvme_ctrlr_process_init(ctrlr); 配置ctrlr的暫存器狀態 cc.en,identify 、 construct namespace、 identify namespace 等,直到狀態為NVME_CTRLR_STATE_READY才算成功
TAILQ_REMOVE(&probe_ctx->init_ctrlrs, ctrlr, tailq); 移除初始化佇列
TAILQ_INSERT_TAIL(&g_spdk_nvme_driver->shared_attached_ctrlrs, ctrlr, tailq); 插入到g_spdk_nvme_driver->shared_attached_ctrlrs佇列
nvme_ctrlr_proc_get_ref(ctrlr); 移除inactive的proc,給當前的proc進行active_proc->ref++
probe_ctx->attach_cb(probe_ctx->cb_ctx, &ctrlr->trid, ctrlr, &ctrlr->opts); 如果attach_cb有效,則進行attach_cb流程
------------------------------------------------------------ 迴圈執行
------------------------------------------------------------------------------------------------ 到此為止example的spdk_nvme_probe流程就結束了