文章目录
快速回忆
通过本文可以看到内核中驱动变化如何通过udev通知到用户态udevd守护进程,并进一步根据rdma-core安装时候配置到udevd的rules进行匹配驱动或者subsystems并进一步触发通过udev机制让systemd启动rdma-ndd机制,并且也通过rdma-ndd机制再次使用udev机制通过netlink机制创建monitor观测infiniband事件的add和move事件并最终更新到sysfs文件。从而介绍了udev机制启动用户态其他systemd管理的守护进程,以及介绍了守护进程通过udev机制和netlink机制建立了驱动变化触发用户态守护进程变化,进而触发相关动作(比如更新文件),最终更新完文件,相当于通过sysfs这个对外接口暴露给用户。
/sys/class/infiniband/mlx5_0/node_desc #查看驱动是否成功创建infiniband设备
背景
rdma-ndd是rdma-core项目中的一个系统守护进程,全称为“RDMA device Node Description update daemon”,主要负责在RDMA驱动事件变化时通知用户态程序。它通过systemd进行管理,主要功能是监测hostname和内核udev的add、remove事件,并使用poll机制结合文件描述符(fd)进行处理。rdma-ndd通过udev监控netlink事件,特别是Infiniband设备的变化,并在检测到事件后更新sysfs中的node_desc文件,如/sys/class/infiniband/mlx5_0/node_desc。本文还介绍了修改hostname的实验,ndd会检测到/proc/sys/kernel/hostname的变化并更新相关文件。另外介绍了rdma-ndd所在的rdma-core rpm文件包含多个配置文件、udev规则和服务文件,支持RDMA设备的模块加载和命名管理。通过本文能够看到RDMA驱动事件变化后如何通知用户态程序
rdma-ndd简要分析
什么是rdma-ndd
rdma-ndd是rdma-core中的一个系统守护进程,全称是 “RDMA device Node Description update daemon”,即 RDMA 设备节点描述更新守护进程。虽然不是太重要,但是能够反映,RDMA驱动事件变化后如何通知用户态程序
systemd-+-ModemManager---2*[{ModemManager}]
|-rdma-ndd
是systemd管理
rdma-ndd的代码:
项目仓库:
https://github.com/linux-rdma/rdma-core
具体代码路径:
rdma-ndd的实现主要分布在以下文件中:
主程序源文件:https://github.com/linux-rdma/rdma-core/tree/master/rdma-ndd
ndd的主要流程
可以看到ndd的主要流程就是观测hostname和内核udev的add和remove进行处理。使用poll的方式结合fd来监测。
ndd如何关注udev的事件?以及如何创建monitor?
udev只关注了netlink事件并且只关注infiniband的变化,使用了一个udev的monitor,并且将monitor的fd返回后用poll进行处理。
ndd在观测poll之后如何处理?
udev主要处理来自驱动中add和move的事情:
ndd观测了哪些文件变化?
ndd更新的文件:
udev检测到hostname或者netlink来的事件后会更新sysfs中的node_desc文件:/sys/class/infiniband/mlx5_0/node_desc
下面做了一个实验修改hostname之后,infiniband的sysfs中node_desc就变化了:
hostname命令修改后,会更新/proc/sys/kernel/hostname
,然后ndd检测该文件
udev何时创建ndd守护进程?如何创建
udev 规则中使用 ENV{SYSTEMD_WANTS}
设置环境变量,通知 systemd 启动特定服务。
在rdma-core的rpm中指定的udev的rdma-ndd.rules中,就会指定systemd何时创建rdma-ndd守护进程。
rdma-code的文件
[root@localhost ~]# rpm -ql rdma-core-37.2-1.0.1.an8.x86_64
/etc/modprobe.d
/etc/modprobe.d/mlx4.conf
/etc/modprobe.d/truescale.conf
/etc/rdma
/etc/rdma/mlx4.conf
/etc/rdma/modules/infiniband.conf
/etc/rdma/modules/iwarp.conf
/etc/rdma/modules/opa.conf
/etc/rdma/modules/rdma.conf
/etc/rdma/modules/roce.conf
/etc/udev/rules.d/70-persistent-ipoib.rules
/usr/bin/ibdev2netdev
/usr/lib/.build-id
/usr/lib/.build-id/48
/usr/lib/.build-id/48/7f12de4a44644fd0e2f60d92cf365487cd3d0d
/usr/lib/.build-id/eb
/usr/lib/.build-id/eb/b0cfde72460493da997a3b1e0b2d46b7d5e651
/usr/lib/dracut
/usr/lib/dracut/modules.d
/usr/lib/dracut/modules.d/05rdma
/usr/lib/dracut/modules.d/05rdma/module-setup.sh
/usr/lib/modprobe.d
/usr/lib/modprobe.d/libmlx4.conf
/usr/lib/systemd/system/rdma-hw.target
/usr/lib/systemd/system/rdma-load-modules@.service
/usr/lib/systemd/system/rdma-ndd.service
/usr/lib/udev/rdma_rename
/usr/lib/udev/rules.d
/usr/lib/udev/rules.d/60-rdma-ndd.rules
/usr/lib/udev/rules.d/60-rdma-persistent-naming.rules
/usr/lib/udev/rules.d/75-rdma-description.rules
/usr/lib/udev/rules.d/90-rdma-hw-modules.rules
/usr/lib/udev/rules.d/90-rdma-ulp-modules.rules
/usr/lib/udev/rules.d/90-rdma-umad.rules
/usr/libexec/mlx4-setup.sh
/usr/libexec/truescale-serdes.cmds
/usr/sbin/rdma-ndd
/usr/share/doc/rdma-core
/usr/share/doc/rdma-core/README.md
/usr/share/doc/rdma-core/udev.md
/usr/share/licenses/rdma-core
/usr/share/licenses/rdma-core/COPYING.BSD_FB
/usr/share/licenses/rdma-core/COPYING.BSD_MIT
/usr/share/licenses/rdma-core/COPYING.GPL2
/usr/share/licenses/rdma-core/COPYING.md
/usr/share/man/man7/rxe.7.gz
/usr/share/man/man8/rdma-ndd.8.gz
/etc/rdma/modules/roce.conf 这个目录文件是给udev用的。
3个service:
/usr/lib/systemd/system/rdma-hw.target
/usr/lib/systemd/system/rdma-load-modules@.service
/usr/lib/systemd/system/rdma-ndd.service
udev的rules:
/etc/udev/rules.d/70-persistent-ipoib.rules
/usr/lib/udev/rules.d/60-rdma-ndd.rules
/usr/lib/udev/rules.d/60-rdma-persistent-naming.rules
/usr/lib/udev/rules.d/75-rdma-description.rules
/usr/lib/udev/rules.d/90-rdma-hw-modules.rules
/usr/lib/udev/rules.d/90-rdma-ulp-modules.rules
/usr/lib/udev/rules.d/90-rdma-umad.rules
工具文件:
/usr/bin/ibdev2netdev
/usr/sbin/rdma-ndd
关于udev与systemd以及与rdma_ndd的关系
通过环境变量触发服务
- 机制:udev 规则中使用
ENV{SYSTEMD_WANTS}
设置环境变量,通知 systemd 启动特定服务。# 示例规则:当检测到 Infiniband 设备时启动 rdma-ndd.service SUBSYSTEM=="infiniband", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rdma-ndd.service"
- 流程:
- udevd 处理规则时设置
SYSTEMD_WANTS
环境变量。 - systemd 监听 udev 事件,发现该变量后启动对应的服务单元。
- udevd 处理规则时设置
通过 TAG 标记集成
TAG+="systemd"
:将设备标记为 systemd 管理,使 systemd 能够跟踪设备生命周期。ENV{SYSTEMD_ALIAS}
:为设备创建 systemd 别名,便于通过systemctl
管理。# 示例:为网卡创建别名 SUBSYSTEM=="net", ATTR{address}=="00:11:22:33:44:55", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/eth0"
举例
假设插入一个 Infiniband 设备,触发规则 DRIVERS=="mlx5_core", ENV{ID_RDMA_INFINIBAND}="1"
:
- 内核通知:内核检测到新设备,通过
netlink
发送uevent
消息。 - udevd 接收:systemd-udevd 监听
netlink
,获取设备事件。 - 规则匹配:udevd 遍历规则,匹配到
DRIVERS=="mlx5_core"
,设置ID_RDMA_INFINIBAND=1
。 - 触发 systemd 服务:若规则包含
ENV{SYSTEMD_WANTS}+="rdma-ndd.service"
,systemd 启动该服务。 - 服务执行:
rdma-ndd
进程读取设备信息并更新拓扑数据库。 - 完成通知:udevd 向内核确认事件处理完成,设备就绪。
测试关系
udev提供了udevadm命令可以测试,执行udevadm test /sys/class/infiniband/mlx5_0
,就会模拟udev机制。会先读取所有的rules文件,并且匹配/sys/class/infiniband/mlx5_0相关的rules。
rdd的udev文件
其他
本文中这种infiniband设备添加删除等事件,会在系统启动加载驱动的时候触发。如果存在node_desc文件,说明驱动已经加载了infiniband设备
其实前面提到了要让驱动变化通知到用户态的需求,如果要实现除了通过udev这种方式,还可以提供字符设备 ioctl、系统syscall等很多种内核通知用户态的机制。但是rdma-core选择了udev机制来触发启动其他守护进程(ndd)以及udev机制在ndd中检测驱动action来更新文件。这里之所以要搞得这么负责,而不是驱动直接自己写一个字符驱动,然后ioctl等方式来观测。为什么这样?这涉及到udev的netlink机制以及字符设备方式的一些本质差别。主要2个点,可以借用udev的标准机制,比较方便;另外udev的功能也提供了异步机制以及udev提供netlink支持多播联动,这一点在字符设备+ioctl还得实现一套就比较麻烦并且ioctl是被动的,内核难直接触发。然后rdma框架利用这个udev机制+netlink机制+systemd机制+守护进程机制+poll机制,全部是利用现有机制,完成了内核态到用户态一对多的异步触发机制。这背后的工程实现值得深挖和学习。举个例子,然你来实现一个内核态驱动触发用户态修改某个sysfs文件,你会怎么做?如果要多播用户态有多个主体都需要观测你又该怎么做?如果要异步怎么做?如何减少系统占用?可能最后走下来,当前这个方式算是一个最佳实践(至少目前来看,结合实际业务是的)