一、中文講解
這兩個函數是Linux內核模塊中對于Mellanox InfiniBand 驅動程序初始化和清理的函數。
mlx4_ib_init()函數是模塊初始化函數,使用__init宏標注,表示該函數只在模塊加載時運行一次。
函數執行的步驟如下:
1. 通過alloc_ordered_workqueue創建一個有序的工作隊列wq。這個隊列用于將工作項按順序排隊執行,WQ_MEM_RECLAIM表示如果系統內存緊張,允許回收。
? ?如果工作隊列創建失敗,返回-ENOMEM錯誤碼,表示內存不足。
2. 在編譯時,如果定義了CONFIG_MLX4_IB_DEBUG_FS,則會調用mlx4_ib_register_debugfs函數注冊調試文件系統(debugfs),這有助于查詢和操作驅動程序的內部信息。
3. 調用mlx4_ib_mcg_init函數初始化多播組(multi-cast group)的一些資源,如果初始化失敗,則跳轉到標簽clean_wq進行清理。
4. 執行init_dev_assign函數,這個函數雖然沒有代碼顯示,但從名稱上看,它可能用于初始化設備或分配相應資源。
5. 注冊InfiniBand接口到Mellanox的InfiniBand核心驅動,使用的函數為mlx4_register_interface。如果注冊失敗,會跳轉到標簽clean_mcg進行清理。
6. 如果所有初始化步驟都成功,函數返回0,表示初始化成功。
如果在任何一步發生錯誤,函數會執行清理操作:
- clean_mcg: 清除通過mlx4_ib_mcg_init初始化的多播組資源。
- clean_wq: 銷毀之前創建的工作隊列wq。
mlx4_ib_cleanup()函數是模塊清理函數,使用__exit宏標注,表示這個函數在模塊卸載時調用。
函數的步驟如下:
1. 調用mlx4_unregister_interface來注銷之前注冊的Mellanox InfiniBand接口。
2. 如果CONFIG_MLX4_IB_DEBUG_FS被定義,則調用mlx4_ib_unregister_debugfs注銷調試文件系統。
3. 調用mlx4_ib_mcg_destroy銷毀在模塊初始化時創建的多播組資源。
4. 銷毀之前創建的工作隊列wq。
5. 釋放dev_num_str_bitmap,這個變量可能用于跟蹤設備編號的分配,但代碼段中沒有展示它的分配過程。
module_init(mlx4_ib_init);和module_exit(mlx4_ib_cleanup);是宏定義,它們告訴內核模塊初始化和退出時應該調用哪些函數。
在 Linux 驅動開發中,mlx4_ib_interface 是一個 mlx4_interface 類型的結構體,它實現了 InfiniBand (IB) 協議棧接口的特定函數。這個結構定義了幾個回調函數,這些函數是由底層的 Mellanox 網絡設備驅動(通常是 mlx4_core 驅動)在特定事件發生時被調用的。
這些回調函數包括:
- add - 當新設備被添加時調用(例如,當一個新的網絡適配器被系統探測到時)。
- remove - 用于當設備被移除時做清理工作。
- event - 當設備關聯的事件發生時被調用,例如鏈接狀態改變、硬件錯誤等。
- protocol - 指定這個接口支持哪種協議(在此示例中是 MLX4_PROT_IB_IPV6,代表 InfiniBand 協議的 IPv6 over IB 版本)。
- flags - 提供額外的信息標志,本例中使用了 MLX4_INTFF_BONDING 標志,表示支持網絡接口綁定(bonding)功能。
這個結構體被注冊到低層的 mlx4_core 驅動,它是 Mellanox 網絡設備的一個核心驅動,提供了基礎硬件抽象層的功能。注冊后,它成為 mlx4_core 驅動和高層驅動(本例中的 mlx4_ib)之間溝通的橋梁。
mlx4_ib_interface 被注冊的地方是在 mlx4_ib_init() 函數,這通常是作為模塊初始化的一部分。當 mlx4_ib_init() 函數成功執行后,mlx4_ib 成為 mlx4_core 驅動感知到的一個客戶端。
以下是 mlx4_ib_interface 回調函數被調用的時機:
- mlx4_ib_add - ?當注冊 mlx4_ib_interface 到 mlx4_core 后,如果 mlx4_core 驅動發現一個新的設備,mlx4_core 將調用 mlx4_ib 的 add 回調函數。
- mlx4_ib_remove - 當一個設備需要被移除,可能是因為硬件被物理移除或是模塊正在被卸載,remove 回調函數將被調用。
- mlx4_ib_event - 當 mlx4_core 檢測到某個事件發生并需要通知 mlx4_ib 模塊時,event 回調函數會被調用。
在 mlx4_core 的代碼中,會在適當的位置調用 mlx4_ib_interface 結構體中指定的函數,以便在上述事件發生時,正確地通知 InfiniBand 協議棧進行相關操作。這種設計允許 mlx4_core 驅動與多個協議棧(如以太網和 InfiniBand)獨立協作,同時保持模塊間的松耦合。
在 Linux 內核中,mlx4_core 驅動發現新設備的過程通常涉及 Linux 的 PCI 子系統。mlx4_core` 驅動是 PCI 驅動的一個實現,它注冊了一系列的回調函數來處理特定于 PCI 設備的事件。下面是一個簡化的描述,解釋了驅動程序如何發現新的 PCI 設備:
1. PCI 設備枚舉: 當系統啟動時,或當新的設備被添加到系統中時(如通過熱插拔),PCI 子系統將識別并枚舉所有的 PCI 設備。它創建代表這些設備的數據結構,并讀取它們的配置空間來獲取諸如供應商ID、設備ID、類別碼等信息。
2. 驅動注冊: 一個 PCI 驅動程序,比如 mlx4_core,會在加載時調用 pci_register_driver() 函數來注冊自己。這個注冊過程包括提供一個 pci_driver 類型的結構體,該結構體包含對應的供應商ID和設備ID,以及針對新設備的回調函數,如 .probe。
static struct pci_driver mlx4_pci_driver = {.name = DRV_NAME,.id_table = mlx4_pci_table,.probe = mlx4_probe,.remove = mlx4_remove,// ... 其他回調函數};
? ? 這里的 mlx4_pci_table 包含了 mlx4_core 驅動支持的設備對應的供應商ID和設備ID列表。mlx4_probe 和 mlx4_remove 是當設備被探測到或移除時要調用的函數。
3. 設備和驅動匹配: 一旦驅動注冊了自己,PCI 子系統就會遍歷所有的 PCI 設備,嘗試找出設備ID和供應商ID與驅動中 id_table 匹配的設備。對于每個匹配的設備,PCI 子系統會調用對應驅動的 .probe 回調函數。
4. 設備初始化 (mlx4_probe): 用于 mlx4_core 驅動的 probe 函數通常名為 mlx4_probe。當一個與 mlx4_pci_table 匹配的 Mellanox 設備被探測到時,mlx4_probe 會被調用。在 mlx4_probe 函數內部,驅動程序會執行必要的初始化步驟,如申請資源、設置設備、注冊網絡設備接口,如果有 InfiniBand 部分,也會在這個時候注冊 mlx4_ib_interface 接口。
5. 接口注冊: 注冊過程可能涉及調用 mlx4_register_interface() 函數,這是 mlx4_core 提供的接口,允許常見的 Mellanox InfiniBand 和以太網功能擴展。這個注冊函數會存儲一個指向 mlx4_interface 結構體的指針,使得在交互式事件中,mlx4_core 驅動能夠調用必要的回調函數。
通過上述流程,mlx4_core 驅動能夠發現新的設備并與相關的協議接口(如 mlx4_ib_interface)進行通信,完成設備初始化和事件通知。
二、中文注釋
這段代碼是一個Linux內核模塊的初始化和清除函數,在kernel-4.9\drivers\infiniband\hw\mlx4\main.c文件中,涉及到Mellanox技術的InfiniBand驅動的mlx4子系統。
// Linux內核模塊的初始化函數
static int __init mlx4_ib_init(void)
{int err;// 為名為"mlx4_ib"的workqueue申請有序工作隊列,WQ_MEM_RECLAIM標志表示該工作隊列支持內存回收wq = alloc_ordered_workqueue("mlx4_ib", WQ_MEM_RECLAIM);// 如果工作隊列創建失敗,則返回內存不足的錯誤代碼if (!wq)return -ENOMEM;// 如果定義了CONFIG_MLX4_IB_DEBUG_FS編譯選項,注冊調試文件系統#ifdef CONFIG_MLX4_IB_DEBUG_FSmlx4_ib_register_debugfs();#endif// 初始化多播組。如果初始化失敗,則跳轉到clean_wq標簽進行清理工作。err = mlx4_ib_mcg_init();// 如果初始化多播組出錯,跳到clean_wq的清理流程if (err)goto clean_wq;// 初始化設備分配相關工作。init_dev_assign();// 注冊接口到mlx4核心。如果注冊失敗,則跳轉到clean_mcg標簽進行多播組清理工作err = mlx4_register_interface(&mlx4_ib_interface);if (err)goto clean_mcg;// 初始化成功,返回0return 0;clean_mcg:// 清理多播組mlx4_ib_mcg_destroy();clean_wq:// 銷毀之前創建的工作隊列destroy_workqueue(wq);// 返回錯誤代碼return err;
}// Linux內核模塊的清除函數,用于在模塊卸載時被調用
static void __exit mlx4_ib_cleanup(void)
{// 注銷mlx4接口mlx4_unregister_interface(&mlx4_ib_interface);// 如果定義了CONFIG_MLX4_IB_DEBUG_FS編譯選項,注銷調試文件系統#ifdef CONFIG_MLX4_IB_DEBUG_FSmlx4_ib_unregister_debugfs();#endif// 清理多播組mlx4_ib_mcg_destroy();// 銷毀工作隊列destroy_workqueue(wq);// 釋放之前分配的設備號位圖存儲空間kfree(dev_num_str_bitmap);
}// 指定模塊加載時調用的初始化函數
module_init(mlx4_ib_init);
// 指定模塊卸載時調用的清除函數
module_exit(mlx4_ib_cleanup);
這些函數是驅動模塊的生命周期鉤子,module_init用于表明模塊加載時應當調用的函數,而module_exit用于指定模塊卸載時的清理函數。__init和__exit宏在內核模塊編譯時有特殊含義,它們用于優化模塊的初始化和退出代碼。__init標記的代碼在模塊加載后不再需要,可以被丟棄;__exit標記的代碼在構建內核為非模塊支持時丟棄。
三、Linux內核"接口(interface)"機制
在Linux內核中,通過一種稱為"接口(interface)"的機制允許不同的驅動程序之間相互溝通。對于Mellanox設備,這可能涉及到mlx4_core模塊和mlx4_ib模塊之間的交互,分別對應于以太網(Ethernet)和InfiniBand子系統。
在這個上下文中,一個mlx4_core的實例在被PCI子系統探測到后會進行初始化,并且注冊到mlx4_core模塊的內部數據結構中。這個注冊過程中,mlx4_core會公布一系列對于外部模塊(如mlx4_ib InfiniBand實現)可以調用的回調函數。
這些回調函數定義在mlx4_interface結構中,如:
static struct mlx4_interface mlx4_ib_interface = {.add = mlx4_ib_add,.remove = mlx4_ib_remove,.event = mlx4_ib_event,.protocol = MLX4_PROT_IB_IPV6,.flags = MLX4_INTFF_BONDING
};
核心思想是,這個結構會被注冊到mlx4_core模塊中。當mlx4_core模塊中的一個設備完成基礎初始化后,它會通知所有注冊了的mlx4_interface實例。通過這種方式,當設備被添加(add)或被移除(remove)時,能夠反過來觸發mlx4_ib_add或mlx4_ib_remove函數的調用。
在添加(add)事件發生時,Ethernet驅動調用mlx4_ib_add,這個函數會負責為InfiniBand功能初始化必要的資源,如設置隊列對、分配內存等。相應地,當移除(remove)事件發生時,會調用mlx4_ib_remove以清理InfiniBand相關的資源。
這種機制不僅僅限于設備的添加和移除,也可用于處理其他類型的事件,例如設備狀態改變、錯誤處理等。最終,這允許不同的驅動模塊能夠同步設備的狀態,并在多協議硬件上提供一致的服務。
在Linux內核中,"接口"是指允許不同組件或驅動程序之間溝通和交互的一種機制。這種機制通常包括一系列的函數指針、數據結構和協議,定義了組件之間的通信方式。以下是一些Linux內核中用于不同驅動程序之間相互溝通的接口機制:
1. 平臺設備和驅動注冊: 平臺驅動和設備使用平臺設備結構struct platform_device和平臺驅動結構struct platform_driver,這兩者都包含指向描述它們如何交互的數據結構的指針。設備通常注冊它們自己的資源,如內存區域、DMA和中斷。當平臺設備注冊時,核心代碼會為匹配的驅動程序調用probe()函數,此時可以建立接口。
2. 設備模型(Device Model): Linux內核的設備模型是一種抽象層,允許內核代碼以統一的方式來處理硬件設備。它定義了設備、驅動程序和總線(bus)之間的關系,并通過結構體和方法來描述它們的關系。例如,每個設備結構struct device可以關聯一組用于設備操作的方法。
3. 文件操作接口:字符和塊設備驅動程序使用文件操作結構`truct file_operations,該結構包含指向不同操作的函數指針(如open(), read(), write(), ioctl(), 等)。這樣,用戶空間應用可以通過標準的系統調用來與內核中的設備文件進行交互。
4. 網絡層接口:網絡設備使用網絡設備結構struct net_device,通過這種結構,它們可以注冊自己給網絡子系統,并提供一系列操作函數,以進行網絡數據包的發送和接收。
5. 總線類型: 內核定義了多種總線類型,比如PCI, USB, I2C等。每種總線類型都有自己的一套方法和協議,用于發現設備、匹配驅動程序、配置資源等。
6. 內核模塊與符號導出: 如果驅動程序或內核模塊需要讓其它部分的內核代碼使用它們提供的功能,它們可以通過EXPORT_SYMBOL或EXPORT_SYMBOL_GPL宏來導出符號(函數、變量的名字)。
7. 回調函數: 驅動程序與硬件通信時通常需要中斷處理,內核提供注冊中斷處理函數的接口,允許驅動編寫自己的中斷服務例程。
通常,在專業的環境下,這些接口的使用都遵循嚴格的編程接口(API)和編程約定(ABI),確保了內核的模塊和組件可以正確地與彼此通信。