RDMA——libibverbs 代碼分析(1)


下載libibverbs最新代碼,https://downloads.openfabrics.org/verbs/README.html 為1.2.0版本。后面開始逐步分析libibverbs源碼。

1、ibv_get_device_list:

   該函數具體的實現在libibverbs-1.2.0/src/devices.c文件中。

struct ibv_device **__ibv_get_device_list(int *num)
{
……
    pthread_once(&device_list_once, count_devices); //count_devices這個函數在本進程中僅執行一次。具體解析見1.1
……
    l = calloc(num_devices + 1, sizeof (struct ibv_device *)); //分配n個長度為size的連續空間,並將連續空間清零
……

    for (i = 0; i < num_devices; ++i)
        l[i] = device_list[i];
    if (num)
        *num = num_devices;

    return l;
}

1.1 pthread_once:

    在多線程環境中,有些事僅需要執行一次。通常當初始化應用程序時,可以比較容易地將其放在main函數中。但當你寫一個庫時,就不能在main里面初始化了,你可以用靜態初始化,但使用一次初始化(pthread_once)會比較容易些。
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
功能:本函數使用初值為PTHREAD_ONCE_INIT的once_control變量保證init_routine()函數在本進程執行序列中僅執行一次。
    Linux Threads使用互斥鎖和條件變量保證由pthread_once()指定的函數執行且僅執行一次,而once_control表示是否執行過。如果once_control的初值不是PTHREAD_ONCE_INIT(Linux Threads定義為0),pthread_once() 的行為就會不正常。在LinuxThreads中,實際"一次性函數"的執行狀態有三種:NEVER(0)、IN_PROGRESS(1)、DONE (2),如果once初值設為1,則由於所有pthread_once()都必須等待其中一個激發"已執行一次"信號,因此所有pthread_once ()都會陷入永久的等待中;如果設為2,則表示該函數已執行過一次,從而所有pthread_once()都會立即返回0。

    接着繼續分析,重點函數count_devices,count_devices->ibverbs_init(init.c)。在ibverbs_init函數中,會先獲取系統路徑一般為/sys,然后檢查內核ABI版本,對於低於或高於某個ABI版本,ibverbs就不支持了。關於ABI可以參考https://blog.csdn.net/juS3Ve/article/details/82782987,具體說ABI與cpu架構和OS有關。

    接下來會檢查資源限制,如下:

static void check_memlock_limit(void)
{
    struct rlimit rlim;

    if (!geteuid())
        return;

    if (getrlimit(RLIMIT_MEMLOCK, &rlim)) {    //進程可鎖定在內存中的最大數據量,字節為單位。
        fprintf(stderr, PFX "Warning: getrlimit(RLIMIT_MEMLOCK) failed.");
        return;
    }

    if (rlim.rlim_cur <= 32768)
        fprintf(stderr, PFX "Warning: RLIMIT_MEMLOCK is %lu bytes.\n"
            "    This will severely limit memory registrations.\n",
            rlim.rlim_cur);
}

    這里要求進程中可鎖定在內存中的最大數據量,軟限制要大於32K。否則會影響到內存注冊。在使用時,可以在系統上設置ulimit取消限制。

    接下來讀取配置文件調用函數read_config(),該函數會從/sysocnfdir/libibverbs.d目錄下讀取配置文件,在我的系統中配置文件路徑為/etc/libibverbs.d,在這個目錄下內容如下:

linux-MgXfWk:/etc/libibverbs.d # ls
bnxt_re.driver  cxgb4.driver      hns.driver    ipathverbs.driver  mlx5.driver   nes.driver     qedr.driver  vmw_pvrdma.driver
cxgb3.driver    hfi1verbs.driver  i40iw.driver  mlx4.driver        mthca.driver  ocrdma.driver  rxe.driver
linux-MgXfWk:/etc/libibverbs.d # cat cxgb4.driver
driver cxgb4
linux-MgXfWk:/etc/libibverbs.d # cat mlx5.driver
driver mlx5

    從這些文件中讀取driver名字,然后將他們加入到一個鏈表中,鏈表名稱為driver_name_list。

    然后是調用find_sysfs_devs函數,獲取/sys/class/infiniband_verbs目錄下的設備文件,查看內核代碼,在uverbs_main.c中,會創建對應的設備。

那么現在的問題是:

    在find_sysfs_devs函數中,會去查找sys/class/infiniband_verbs目錄下的設備文件,那么這些文件是來自於哪里,是如何創建的?接下來我們來看一下,infiniband的內核代碼,在內核代碼中的路徑為./drivers/infinniband/core/uverbs_main.c,從這個文件看起。

    這個文件里有一個module_init函數,這個是模塊被加載后第一個調用的模塊,對應的初始化函數為ib_uverbs_init。查找編譯文件,可以查看到,這個模塊的名字為ib_uverbs.ko。當我們執行insmod ib_uverbs.ko時,就會調用ib_uverbs_init函數。

static int __init ib_uverbs_init(void)

我們看到這個函數,有__init標志,這個標志的意思是:

__init宏告知編譯器,將變量或函數放在一個特殊的區域,這個區域定義在vmlinux.lds中。__init將函數放在".init.text"這個代碼區中,__initdata將數據放在".init.data"這個數據區中。標記為初始化的函數,表明該函數供在初始化期間使用。在模塊裝載之后,模塊裝載就會將初始化函數扔掉。這樣可以將該函數占用的內存釋放出來。

也就是,這里的初始化函數,會在模塊裝載后,它使用的內存會被釋放。

    首先調用register_chrdev_region,該函數與alloc_chrdev_region函數一樣都是向系統申請設備號,區別在於,前一個函數用於已知起始設備的設備號的情況,而alloc_chrdev_region用於設備號未知,向系統動態申請未被占用的設備號的情況。

    接下來的內容,我們在下一個節展開。


免責聲明!

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



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