基於上一篇文章https://www.cnblogs.com/xingmuxin/p/11057845.html
我們現在從分析libibverbs代碼,跳入到分析內核代碼,代碼位置在./drivers/infiniband/core/uverbs_main.c,在這里,我們主要來探究,infiniband_verbs這個文件目錄的創建。要生成這個目錄,我們需要加載ib_uverbs.ko。下面我們看下這個ko是如何編譯的。
infiniband-$(CONFIG_INFINIBAND_ADDR_TRANS) := rdma_cm.o user_access-$(CONFIG_INFINIBAND_ADDR_TRANS) := rdma_ucm.o obj-$(CONFIG_INFINIBAND) += ib_core.o ib_cm.o iw_cm.o \ $(infiniband-y) obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o $(user_access-y) obj-$(CONFIG_INFINIBAND_USER_ACCESS_UCM) += ib_ucm.o $(user_access-y) ……
CONFIG_INFINIBAND_USER_ACCESS這個編譯選項的解釋為:
Userspace InfiniBand access support. This enables the kernel side of userspace verbs and the userspace communication manager (CM). This allows userspace processes to set up connections and directly access InfiniBand hardware for fast-path operations. You will also need libibverbs, libibcm and a hardware driver library from rdma-core https://github.com/linux-rdma/rdma-core.
也就是說ib_uverbs.ko是為用戶態直接訪問infiniband硬件提供支持的,我們除了需要libibverbs,libibcm外,還需要硬件驅動library。
大體知道了ib_uverbs.ko的作用(這里的u,我理解應該是userspace),我們來看一下它的具體實現。
static int __init ib_uverbs_init(void) { int ret; // 向系統注冊字符設備之前,應首先調用該函數,向系統申請設備號
ret = register_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_NUM_FIXED_MINOR, "infiniband_verbs"); …… ret = alloc_chrdev_region(&dynamic_uverbs_dev, 0, IB_UVERBS_NUM_DYNAMIC_MINOR, "infiniband_verbs"); ……
// 在/sys/class/目錄下創建一個infiniband_verbs目錄 uverbs_class = class_create(THIS_MODULE, "infiniband_verbs"); if (IS_ERR(uverbs_class)) { ret = PTR_ERR(uverbs_class); pr_err("user_verbs: couldn't create class infiniband_verbs\n"); goto out_chrdev; } uverbs_class->devnode = uverbs_devnode; ret = class_create_file(uverbs_class, &class_attr_abi_version.attr); if (ret) { pr_err("user_verbs: couldn't create abi_version attribute\n"); goto out_class; } ret = ib_register_client(&uverbs_client); if (ret) { pr_err("user_verbs: couldn't register client\n"); goto out_class; } return 0; …… }
以上似乎是創建udev的一些固定操作,要在/sys/class目錄下創建對應的文件夾目錄。
接下來,我們再回到libibverbs代碼中,在find_sysfs_devs后,會遍歷sysfs_dev_list,執行try_drivers,try_drivers代碼如下:
static struct ibv_device *try_drivers(struct ibv_sysfs_dev *sysfs_dev) { struct ibv_driver *driver; struct ibv_device *dev; for (driver = head_driver; driver; driver = driver->next) { dev = try_driver(driver, sysfs_dev); if (dev) return dev; } return NULL; }
這里關鍵點是head_driver在沒有賦值前它是空的,我們看下哪里會給head_driver賦值,從代碼看是在調用ibv_register_driver時會給head_driver賦值,這個ibv_register_driver函數在libibverbs代碼中搜索不到調用處,在driver.h文件中,定義一個宏為:
#define PROVIDER_DRIVER(drv) \ static __attribute__((constructor)) void drv##__register_driver(void) \ { \ verbs_register_driver(&drv); \ }
__attribute__((constructor))設置了優先級屬性,constructor
參數讓系統執行main()
函數之前調用函數。
搜索PROVIDER_DRIVER,發現所有rdma驅動,都是使用這個函數來初始化他們的用戶態庫的。我們以mlx5為例。在libmlx5代碼中,mlx5.c(可以參看rdma-core/providers/mlx5.c),就會調用PROVIDER_DRIVER來初始化用戶態驅動。
綜上,我們可以總結出,libibverbs與libmlx5用戶態驅動緊密配合下,發現mlx5驅動設備,而不是通過pcie,而ib_uverbs.ko,mlx5內核態驅動,是提供支持。那么現在的問題是,用戶態驅動都是這樣操作的嗎,普通的設備驅動是如何進行設備發現的呢?
下一節我們來討論這個問題。