3. 查询端口属性

任务目标

编写一个命令行工具,通过 InfiniBand 内核接口查询指定设备端口的属性,包括端口状态(Port State)、物理状态(Physical State)、链路宽度(Active Width)和速率(Active Speed),并输出可读结果。

环境依赖

需要 Linux 系统且已安装 InfiniBand 驱动(内核模块 ib_uverbs、ib_core)。编译依赖 libibverbs-dev 和 libibmad-dev(后者用于获取端口状态映射表)。

sudo apt install libibverbs-dev libibmad-dev

关键完整代码

/* port_query.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <infiniband/verbs.h>
#include <infiniband/mad.h>

static const char *port_state_str(enum ibv_port_state st) {
    switch (st) {
        case IBV_PORT_NOP:    return "NOP";
        case IBV_PORT_DOWN:   return "DOWN";
        case IBV_PORT_INIT:   return "INIT";
        case IBV_PORT_ARMED:  return "ARMED";
        case IBV_PORT_ACTIVE: return "ACTIVE";
        default:              return "UNKNOWN";
    }
}

static const char *phys_state_str(enum ibv_port_phys_state st) {
    switch (st) {
        case IBV_PHYS_STATE_SLEEP:  return "SLEEP";
        case IBV_PHYS_STATE_POLLING: return "POLLING";
        case IBV_PHYS_STATE_DISABLED: return "DISABLED";
        case IBV_PHYS_STATE_PORT_CONFIGURATION_TRAINING: return "CFG_TRAIN";
        case IBV_PHYS_STATE_LINK_UP: return "LINK_UP";
        case IBV_PHYS_STATE_LINK_ERROR_RECOVERY: return "LINK_ERR_RECOVERY";
        case IBV_PHYS_STATE_PHY_TEST: return "PHY_TEST";
        default: return "UNKNOWN";
    }
}

int main(int argc, char *argv[]) {
    struct ibv_device **dev_list;
    struct ibv_context *ctx = NULL;
    struct ibv_port_attr attr;
    struct ibv_device_attr dev_attr;
    int port_num = 1;
    int i;

    if (argc > 1) port_num = atoi(argv[1]);

    dev_list = ibv_get_device_list(NULL);
    if (!dev_list) {
        perror("ibv_get_device_list");
        return 1;
    }

    for (i = 0; dev_list[i]; ++i) {
        ctx = ibv_open_device(dev_list[i]);
        if (!ctx) continue;

        if (ibv_query_device(ctx, &dev_attr)) {
            ibv_close_device(ctx);
            ctx = NULL;
            continue;
        }

        if (port_num >= 1 && port_num <= dev_attr.phys_port_cnt) {
            if (!ibv_query_port(ctx, port_num, &attr)) {
                printf("Device: %s\n", ibv_get_device_name(dev_list[i]));
                printf("Port:   %d\n", port_num);
                printf("State:  %s (%d)\n", port_state_str(attr.state), attr.state);
                printf("Phys:   %s (%d)\n", phys_state_str(attr.phys_state), attr.phys_state);
                printf("Width:  %dX\n", attr.active_width);
                printf("Speed:  %s\n", ibv_width_to_rate(attr.active_width, attr.active_speed));
                ibv_close_device(ctx);
                ibv_free_device_list(dev_list);
                return 0;
            }
        }
        ibv_close_device(ctx);
        ctx = NULL;
    }

    fprintf(stderr, "No usable IB device found for port %d\n", port_num);
    ibv_free_device_list(dev_list);
    return 1;
}

编译与运行

gcc -o port_query port_query.c -libverbs -libmad
# 查询默认端口(端口1)
./port_query
# 查询指定端口(如端口2)
./port_query 2

验证方法

运行程序后,观察输出中的 State 字段。若 InfiniBand 链路已建立,应显示 ACTIVE (4);若未连接或链路断开,则显示 DOWN (2) 或其他非 ACTIVE 状态。物理状态 Phys 在链路正常时应为 LINK_UP (5)。宽度和速率应与实际链路配置一致(例如 Width: 4X、Speed: 40 Gb/sec (4X QDR))。

可通过 ibstat 命令交叉验证:

ibstat | grep -A 8 "Port 1"

对比 ibstat 输出的 State、Physical State、Active Width 和 Active Speed 字段,应与本程序输出一致。

预期输出示例

Device: mlx5_0
Port:   1
State:  ACTIVE (4)
Phys:   LINK_UP (5)
Width:  4X
Speed:  40 Gb/sec (4X QDR)

以上为基于代码逻辑的预期输出格式,实际数值取决于运行环境的链路配置。