spdk nvme盤probe的流程詳細分析

示好發表於2020-12-11
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, /* qpair ID */ 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流程就結束了   

相關文章