Infiniband 网卡驱动安装的坑

跟IT借了一根40G QSPF+ 的DAC线缆,型号两台机器在一个机柜,1m的长度就够了。连接好后,绿色的网卡端口状态指示灯就亮了,硬件应该OK的,接下来就是安装驱动了

应用介绍

最近需要一个infiniband的网络环境做k8s的测试,本来想提个研发的采购申请买几块Mellanox的CX6 200G IB网卡,无奈被驳回了。周一的时候隔壁组的机器升级100G RoCE网络,刚好退下来几张CX3的FDR网卡,终于可以捡个垃圾玩玩了。
找了两台有空余PCI槽位的机器,插上CX3的IB网卡,IB交换机就免了;跟IT借了一根40G QSPF+ 的DAC线缆,型号两台机器在一个机柜,1m的长度就够了。连接好后,绿色的网卡端口状态指示灯就亮了,硬件应该OK的,接下来就是安装驱动了。 
没想到堂堂一个任老板竟然栽倒了一个小小的驱动上面!
实际上按照Mellanox的官方指导文档,安装驱动也是很简单的一件事情,毕竟文档写的还是挺清晰的:
这里用的驱动版本是:4.9-2.2.4.0
1.登录官网选择适合操作系统的驱动版本https://www.mellanox.com/products/ethernet-drivers/linux/mlnx_en 注意:这里需要注意一下ConnectX-3 Pro以及ConnectX-3 的硬件 在 5.x-x.x.x.x版本的驱动中就不再支持了; 因此这里需要下载LTS 版本的驱动 2.官方提供两种格式的驱动包:ISO以及tag包,可以根据个人喜好下载      解压:tar -xvzf MLNX_OFED_LINUX-4.9-2.2.4.0-rhel7.7-x86_64.tgz 安装:cd MLNX_OFED_LINUX-4.9-2.2.4.0-rhel7.7-x86_64       ./mlnxofedinstall      3.启动加载驱动以及opensm子网管理器驱动加载:sudo /etc/init.d/openibd restart启动子网管理:sudo /etc/init.d/opensmd restart(在IB网络里面需要子网管理组件,由于这里没有带管理的IB交换机, 因此需要在一台机器上面启动opensmd)
也就三个步骤而已,正常的话半个小时就可以搞定;
偏偏任老板就栽倒了第三步上:openibd死活启动不了了,也就是驱动加载失败:
# /etc/init.d/openibd start Loading Mellanox MLX5_IB HCA driver:                       [FAILED]Loading HCA driver and Access Layer:                       [FAILED] Please run /usr/sbin/sysinfo-snapshot.py to collect the debug informationand open an issue in the http://support.mellanox.com/SupportWeb/service_center/SelfService
操作系统的版本是Centos 7.7(3.10.0-1062.1.1.el7.x86_64)
刚开始以为驱动太新了,网卡又比较老了,所以尝试换了几个4.7.x.x版本,问题依然存在。
也有用'yum groupinstall  "Infiniband Support"'这种inbox的驱动去尝试,也没有调试成功。
期间,重启大法已经被实施了无数次!
看起来,这个问题还有些小复杂,坐下来慢慢看一下了。
先看一下dmesg信息: 
[三 12月 30 14:24:41 2020] Request for unknown module key 'Mellanox Technologies signing key: 61feb074fc7292f958419386ffdd9d5ca999e403' err -11[三 12月 30 14:24:41 2020] user_verbs: couldn't register device number[三 12月 30 14:24:41 2020] Request for unknown module key 'Mellanox Technologies signing key: 61feb074fc7292f958419386ffdd9d5ca999e403' err -11[三 12月 30 14:24:41 2020] user_mad: couldn't register device number[三 12月 30 14:24:41 2020] Request for unknown module key 'Mellanox Technologies signing key: 61feb074fc7292f958419386ffdd9d5ca999e403' err -11[三 12月 30 14:24:41 2020] user_verbs: couldn't register device number
看到了一个signingkey校验失败的信息,放狗一搜:
看到了这个链接:
https://docs.mellanox.com/display/ConnectX5ENOCP2/Linux+Driver+Installation
原来是UEFI Secure Boot的问题,可是我的机器并没有用UEFI启动啊 ,无奈先试一把吧:
1.Download the x.509 public key.# wget http://www.mellanox.com/downloads/ofed/mlnx_signing_key_pub.der 2.Add the public key to the MOK list using the mokutil utility.# mokutil --import mlnx_signing_key_pub.der

再去启动openibd,熟悉的错误还是出现了!

无奈还是求助圈内的大佬吧,连续找了三个大佬(包含一个微博上面的大佬,一天的时间过去了,最后得到一个建议更换操作系统。
算了,先回家睡觉吧,不过问题没有搞定,睡觉也是睡不着的:Centos7.7这个版本的系统目前看应该算是主流的,另外Mellanox的技术应该还是可以的,应该还是哪个角落的一个小问题没有发现导致的,不会是那种操作系统版本不支持的大问题,如果真是是不支持应该会有文档说明。第二天可以先找一个老一点儿的系统试一下,但是主要精力应该是放在debug上面,一定是有个问题没有发现,而且不会大的问题(要调整好的代码的那种)。 
第二天找了一台Centos7.6的机器,试了一下,竟然也有同样的问题,这更坚定了我的想法,应该是我的环境机器的问题,再次检查dmesg以及journal日志信息,最后把重点放在了这几个信息上面:
user_verbs: couldn't register device numberuser_mad: couldn't register device numberuser_verbs: couldn't register device number
这个"device number"显得格外的刺眼,像极了冬日里的太阳!
那就先来了解一下了!
我们知道,在Unix系系统中,一切皆是文件,所有硬盘,键盘,网卡等设备都有文件来代表,对应着/dev/下面的文件。对于应用程序来说,可以像对待普通文件一样打开,关闭,读写这些设备文件。但是,这种文件名比如:/dev/sda  、/dev/raw/raw1 都是用户空间名称,OS Kernel根本不知道这个名称代指什么。在内核空间是通过major、minor device number来区分设备的。
major device number:可以看做是设备驱动程序,被同一设备驱动程序管理的设备有相同的major device number。这个数字实际是Kernel 中device driver table的索引。这个表保存着不同的设备驱动程序。
minor device number:代表被访问的具体设备。也就是说,Kernel根据major device number找到设备驱动程序,然后再从minor device number获得设备位置等属性。
[root@server ~]# ls /dev/ -l总用量 0crw-------  1 root root     10235 10月 27 16:55 autofscrw------- 1 root root 10, 60 1027 16:55 network_latencycrw------- 1 root root 10, 59 1027 16:55 network_throughput-rw-r--r-- 1 root root 0 1229 23:17 nillcrw-rw-rw- 1 root root 1, 3 1027 16:55 nullcrw-rw-rw- 1 root root 195, 0 1027 16:58 nvidia0crw-rw-rw- 1 root root 195, 1 1027 16:58 nvidia1crw-rw-rw- 1 root root 195, 2 1027 16:58 nvidia2drwxr-xr-x 2 root root 80 1027 16:58 nvidia-capscrw-rw-rw- 1 root root 195, 255 1027 16:58 nvidiactlcrw-rw-rw-  1 root root    195254 10月 27 17:31 nvidia-modesetbrw-rw---- 1 root disk 8, 0 1027 16:55 sdabrw-rw---- 1 root disk 8, 1 1027 16:55 sda1brw-rw----  1 root disk      8,   2 10月 27 16:55 sda2lrwxrwxrwx 1 root root 15 1027 16:55 stderr -> /proc/self/fd/2lrwxrwxrwx 1 root root 15 1027 16:55 stdin -> /proc/self/fd/0lrwxrwxrwx 1 root root 15 1027 16:55 stdout -> /proc/self/fd/1crw------- 1 root root 10, 58 112 16:29 tgtcrw-rw-rw- 1 root tty 5, 0 1229 20:32 ttycrw--w---- 1 root tty 4, 0 1027 16:55 tty0crw--w---- 1 root tty 4, 1 1027 16:55 tty1crw--w---- 1 root tty 4, 10 1027 16:55 tty10crw--w---- 1 root tty 4, 11 1027 16:55 tty11crw--w----  1 root tty       4,  12 10月 27 16:55 tty12

上面第5、6列部分就是设备号,前面是major,后面是minor major号表示设备所使用的驱动,而minor号则表示具体的设备。在上图中,tty的驱动都是driver 4,而利用minor号区别不同的tty设备。另外,通过/proc/devices文件也可以看到设备所使用的驱动,即major号:

[root@server32 ~]# cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS  5 /dev/tty........

关于dev_tmajorminor号定义如下:

/* <linux/types.h>: */typedef __u32 __kernel_dev_t;typedef __kernel_dev_t dev_t; /* <linux/kdev_t.h> */#define MINORBITS   20#define MINORMASK   ((1U << MINORBITS) - 1) #define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))#define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))
dev_t32 bit长,其中高12位是major,低20位是minor
获取设备号的两种方法:
(1)预先指定设备号:
int register_chrdev_region(dev_t from, unsigned count, const char *name) 
from包含majorminor,通常情况下minor指定为0count指定连续设备号的数量,name指定设备的名字。register_chrdev_region实现如下:
#from fs/char_dev.c/** * register_chrdev_region() - register a range of device numbers * @from: the first in the desired range of device numbers; must include *        the major number. * @count: the number of consecutive device numbers required * @name: the name of the device or driver. * * Return value is zero on success, a negative error code on failure. */int register_chrdev_region(dev_t from, unsigned count, const char *name){ struct char_device_struct *cd; dev_t to = from + count; dev_t n, next;  for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); if (next > to) next = to; cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name); if (IS_ERR(cd)) goto fail; } return 0;fail: to = n; for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); } return PTR_ERR(cd);}
看到register_chrdev_region即是把from开始连续count个设备号(dev_t类型,包含majorminor)都注册。
举个例子(/drivers/tty/tty_io.c):
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console")
(2)动态分配设备号(推荐使用):
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
dev是传出参数,为动态获得的设备号;firstminor指定第一个minorcountnameregister_chrdev_region的参数定义。alloc_chrdev_region实现如下:
/** * alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers.  The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev.  Returns zero or a negative error code. */int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name){ struct char_device_struct *cd; cd = __register_chrdev_region(0, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); *dev = MKDEV(cd->major, cd->baseminor); return 0;}
举个例子(/drivers/watchdog/watchdog_dev.c):
alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
释放设备号:
void unregister_chrdev_region(dev_t first, unsigned int count);
好了,现在大概了解了device number 是什么了。
那我们就看一下关于注册失败的那个模块的是怎么获取device number的
user_mad: couldn't register device number
驱动包的源码在
# ls ./MLNX_OFED_LINUX-4.9-2.2.4.0-rhel7.7-x86_64/src/MLNX_OFED_SRC-4.9-2.2.4.0.tgz # tar xvf MLNX_OFED_SRC-4.9-2.2.4.0.tgz # cd MLNX_OFED_SRC-4.9-2.2.4.0/SRPMS# rpm2cpio mlnx-ofa_kernel-4.9-OFED.4.9.2.2.4.1.src.rpm | cpio -iv# tar xvf mlnx-ofa_kernel-4.9.tgz # cd mlnx-ofa_kernel-4.9# ls backport_includes  code-metrics.txt  compat-2.6.18 compat_base_tree_version  COPYING   Documentation  LINUX_BASE_BRANCH  mlnx-ofa_kernel.spec  ofed_scriptsbackports          compat            compat_base       compat_version            debian    drivers        makefile           Module.supported      READMEblock              compat-2.6.16     compat_base_tree  configure                 devtools  include        Makefile           net                   scripts 先看一下user_mad这个模块是如何注册device # cat  drivers/infiniband/core/user_mad.c...... MODULE_AUTHOR("Roland Dreier");MODULE_DESCRIPTION("InfiniBand userspace MAD packet access");MODULE_LICENSE("Dual BSD/GPL"); enum { IB_UMAD_MAX_PORTS  = RDMA_MAX_PORTS, IB_UMAD_MAX_AGENTS = 32,  IB_UMAD_MAJOR      = 231, IB_UMAD_MINOR_BASE = 0, IB_UMAD_NUM_FIXED_MINOR = 64, IB_UMAD_NUM_DYNAMIC_MINOR = IB_UMAD_MAX_PORTS - IB_UMAD_NUM_FIXED_MINOR, IB_ISSM_MINOR_BASE        = IB_UMAD_NUM_FIXED_MINOR,}; ...... static const dev_t base_umad_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE);...... static int __init ib_umad_init(void){ int ret;  ret = register_chrdev_region(base_umad_dev, IB_UMAD_NUM_FIXED_MINOR * 2, umad_class.name); if (ret) { pr_err("couldn't register device number\n"); goto out;        }
很不幸,发现了register_chrdev_region ,而且指定了device major number  231,到这里应该基本可以定位就是这个device number导致的。
# /proc/devices 227 mlx5_fpga_tools228 nvidia-uvm229 nvidia-nvswitch230 nvidia-nvlink231 nvidia-caps232 mei233 ipmidev234 ttyVS235 cambr-msg236 cambr-rpc237 ttyMS238 cn-mbox-test239 cmsg240 aux241 megaraid_sas_ioctl242 ptp243 pps244 dimmctl245 ndctl246 hidraw247 usbmon248 bsg249 hmm_device250 watchdog251 iio252 rtc253 dax254 tpm
可以看到231这个number已经被使用了,导致了 user_mad 这个模块没有加载成功。
那问题来了,所有kernel的这些major、minor device number是已经预先分配好了的呢,还是随机分配呢? 
可以直接看一下源码中的这个文件: 
 # cat Documentation/admin-guide/devices.txt 首先231确实是被分配给了IB这个device   231 char  InfiniBand 0 = /dev/infiniband/umad0 1 = /dev/infiniband/umad1 ... 63 = /dev/infiniband/umad63    63rd InfiniBandMad device 64 = /dev/infiniband/issm0     First InfiniBand IsSM device 65 = /dev/infiniband/issm1     Second InfiniBand IsSM device ... 127 = /dev/infiniband/issm63    63rd InfiniBand IsSM device 128 = /dev/infiniband/uverbs0   First InfiniBand verbs device 129 = /dev/infiniband/uverbs1   Second InfiniBand verbs device ... 159 = /dev/infiniband/uverbs31  31st InfiniBand verbs device 预留给动态分配部分:     234-254  char  RESERVED FOR DYNAMIC ASSIGNMENT Character devices that request a dynamic allocation of major number will take numbers starting from 254 and downward.  kernel 4.15后新增:384-511 char  RESERVED FOR DYNAMIC ASSIGNMENT Character devices that request a dynamic allocation of major number will take numbers starting from 511 and downward, once the 234-254 range is full.

可以看到0~233是固定分配出去了,234-254 是预留动态分配部分,从254,253,252往小的方向分配使用。

这里有个问题,如果234-254 不够动态分配了怎么办呢?

4.15之前的kernel是不管三七二十一,继续往小的方向去使用。

# cat fs/char_dev.c if (major == 0) { for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { if (chrdevs[i] == NULL) break; }  if (i < CHRDEV_MAJOR_DYN_END) pr_warn("CHRDEV \"%s\" major number %d goes below the dynamic allocation range\n", name, i);  if (i == 0) { ret = -EBUSY;
即使是用超过了动态分配的范围也是只是打印个warning就完事了。 
4.15之后的kernel又增加了384-511一段number,来使用,同样也是从511开始往小的方向去分配。
# cat fs/char_dev.cstatic int find_dynamic_major(void){ int i; struct char_device_struct *cd;  for (i = ARRAY_SIZE(chrdevs)-1; i > CHRDEV_MAJOR_DYN_END; i--) { if (chrdevs[i] == NULL) return i; }  for (i = CHRDEV_MAJOR_DYN_EXT_START; i > CHRDEV_MAJOR_DYN_EXT_END; i--) { for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next) if (cd->major == i) break;  if (cd == NULL || cd->major != i) return i; }  return -EBUSY;}......   if (major == 0) { ret = find_dynamic_major(); if (ret < 0) { pr_err("CHRDEV \"%s\" dynamic allocation region is full\n", name); goto out; } major = ret; }

/* fs/char_dev.c */#define CHRDEV_MAJOR_MAX 512/* Marks the bottom of the first segment of free char majors */#define CHRDEV_MAJOR_DYN_END 234/* Marks the top and bottom of the second segment of free char majors */#define CHRDEV_MAJOR_DYN_EXT_START 511#define CHRDEV_MAJOR_DYN_EXT_END 384

commit:https://github.com/torvalds/linux/commit/a5d31a3f81c6fb13b381951bf6163444c0257e8b#diff-3b17f7c08e0e1995904f19fbfff59700e41d1fe60a11ab3a40d4e0675a12c732

解决方案:如果你跟我一样不幸,是用的古董级别的内核(4.15之前的版本),又恰好碰到了231这个device number被占用了,导致IB驱动启动失败,唯一的方案就是先卸载占用这个number的内核模块,然后加载IB驱动,再重新加载导致问题的这个内核模块,然后就可以正常工作了。 


文件列表(部分)

名称 大小 修改日期

立即下载

相关下载

[IB Specification Vol 1-Release-1.5] IB Specification Vol 1-Release-1.5最新版本
[什么是PERFQUERY?] Perfquery是一种诊断实用程序,它使用通用服务管理包(GMPS)查询InfiniBand端口的性能和错误计数器,以获取端口计数器,例如: PortCounters, PortCountersExtended, PortXmitDataSL, PortRcvDataSL 以及收发数据(e.g. PortXmitData and PortRcvData).​ # perfquery
[MELLANOX SB77X0/SB78X0 EDR 交换机用户手册] 1U EDR 100Gb / s InfiniBand交换系统和IB路由器硬件用户手册 适用的EDR交换机型号:SB7700,SB7790,SB7800,SB7890,SB7780和SB7880。 本
[MELLANOX QM8700 和 QM8790用户手册] 与该手册相关型号的交换机:QM8700和QM8790 本手册介绍了基于Mellanox Quantum™交换机ASIC的Mellanox 1U HDR InfiniBand交换机系统的安装和基本用法。
[Mellanox ConnectX-5 手册] 本用户手册介绍了multi-host网卡的ConnectX-5VPI网卡进行了介绍。 它提供有关板卡接口,规格,操作板卡所需的软件和固件以及相关文档的详细信息。
[mellanox 培训教程] 这是mellanox的一个培训文档, 争对infiniband几层协议进行稍微详细的介绍,如链路层16个服务等级,16 虚拟lane,它们之间有着怎样的对应关系

评论列表 共有 0 条评论

暂无评论

微信捐赠

微信扫一扫体验

立即
上传
发表
评论
返回
顶部