RDMA——libibverbs 代码分析

Posted xingmuxin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RDMA——libibverbs 代码分析相关的知识,希望对你有一定的参考价值。

下载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这个函数实际上是在找安装了设备驱动的一些设备。然后将这些设备加入链表中,sysfs_dev_list。

    之后遍历这个sysfs_dev_list链表,调用try_drivers,来查看是否有注册驱动,有那么加入到device_list链表中,之后尝试从driver_name_list中的drvier_name遍历去load_driver,然后再查看是否与sysfs_dev_list匹配,sysfs_dev_list中也有,那么加入device_list中,如果sysfs_dev_list中有,但driver没有,那么就将sysfs_dev_list中的对应的这个设备去掉,不放入到device_list中。

    这一段代码后来在高版本中做了修改整理,重点关注注释部分。

/* Remove entries from the sysfs_list that are already preset in the
     * device_list, and remove entries from the device_list that are not
     * present in the sysfs_list.
     */
    list_for_each_safe(device_list, vdev, tmp, entry) 
        struct verbs_sysfs_dev *old_sysfs = NULL;
……
            
        

至此该部分内容分析完毕,后面分析我们采用高版本代码。使用libibverbs-17.

以上是关于RDMA——libibverbs 代码分析的主要内容,如果未能解决你的问题,请参考以下文章

[精华] RDMA技术原理分析主流实现对比和解析

10月29日社区直播Spark Shuffle RPMem扩展: 借助持久内存与RDMA加速Spark 数据分析

避坑指南:关于SPDK问题分析过程

详解RDMA(远程直接内存访问)架构原理

RDMA技术浅析

RDMA技术浅析