#! /bin/bash

# udev rule file: 99-ucloud-net-and-rdma-name.rules
## For Mellanox
#ACTION=="add", ATTRS{vendor}=="0x15b3", DRIVERS=="mlx5_core", \
#    PROGRAM="{{THIS_SCRIPT_PATH}} name", \
#    RUN:="{{THIS_SCRIPT_PATH}} rename $result"

# 配置
rep_prefix="mlx_"
udisk_net="udisk0"
udisk_rdma="rdma_udisk0"

# debug
# udevadm info -a /sys/class/...
# udevadm info -p /sys/class/...

# 关键参数
[ "$SUBSYSTEM" == "net" ]        && kernel="$INTERFACE"
[ "$SUBSYSTEM" == "infiniband" ] && kernel="$NAME"
pci_dev="/sys$(dirname $(dirname $DEVPATH))"
pci_addr="$(basename $pci_dev)"
driver="$(basename "$(readlink -f "$pci_dev/driver")")"
vendor_id="$(cat "$pci_dev/vendor" 2>/dev/null)"
model_id="$(cat "$pci_dev/device" 2>/dev/null)"
phys_switch_id="$(cat "/sys$DEVPATH/phys_switch_id" 2>/dev/null)"
phys_port_name="$(cat "/sys$DEVPATH/phys_port_name" 2>/dev/null)"

# udev规则冗余
[ "$ACTION" == "add" ]                                        || exit 101
[ "$vendor_id" == "0x15b3" ]                                  || exit 102
[ "$driver" == "mlx5_core" ]                                  || exit 103

# 必要的信息确认
[ "$SUBSYSTEM" == "net" -o "$SUBSYSTEM" == "infiniband" ]     || exit 104
[ -n "$pci_addr" -a -n "$model_id" ]                          || exit 105

vf_seq() {
    _vfs="$(lspci -D -d "$vendor_id:" | grep Ethernet | grep Virtual | awk '{print $1}')"
    _seq="$(echo "$_vfs" | grep -n -F "$pci_addr" | cut -d: -f1)"
    echo "$_seq"
}

vf_total() {
    _vfs="$(lspci -D -d "$vendor_id:" | grep Ethernet | grep Virtual | awk '{print $1}')"
    _total="$(echo "$_vfs" | wc -l)"
    echo "$_total"
}

mlx_25G() {
    # pf
    if [ "$SUBSYSTEM" == "net" -a "$model_id" == "0x1017" -a -z "$phys_switch_id" ]; then
        exit 202 # ignore
    fi
    # rep
    if [ "$SUBSYSTEM" == "net" -a "$model_id" == "0x1017" -a -n "$phys_switch_id" ]; then
        # pf1vf3
        pfidx="$(echo "$phys_port_name" | grep -oP "(?<=^pf)\d+")"
        vfidx="$(echo "$phys_port_name" | grep -oP "(?<=vf)\d+$")"
        [ -n "$pfidx" -a -n "$vfidx" ] || exit 203 # only match vf's rep

        vfs_per_pf="$(cat $pci_dev/sriov_totalvfs)"
        echo "$rep_prefix$((pfidx*vfs_per_pf + vfidx))"
        exit 0
    fi

    # mlx5_bond_0 => $udisk_rdma
    if [ "$SUBSYSTEM" == "infiniband" -a "$model_id" == "0x1017" ]; then
        # bond网卡没有什么属性特征可以做区分
        if echo "$kernel" | grep -q -P "(\b|_)bond[_\d]"; then
            echo "$udisk_rdma" # 只管bond
            exit 0
        fi
        exit 203 # only match bond
    fi

    exit 107
}

mlx_100G() {
    # pf
    if [[ "$SUBSYSTEM" == "net" && "0x1019 0x101d" =~ "$model_id" && -z "$phys_switch_id" ]]; then
        exit 202 # ignore
    fi
    # rep
    if [[ "$SUBSYSTEM" == "net" && "0x1019 0x101d" =~ "$model_id" && -n "$phys_switch_id" ]]; then
        # pf1vf3
        pfidx="$(echo "$phys_port_name" | grep -oP "(?<=^pf)\d+")"
        vfidx="$(echo "$phys_port_name" | grep -oP "(?<=vf)\d+$")"
        [ -n "$pfidx" -a -n "$vfidx" ] || exit 203 # only match vf's rep

        vfs_per_pf="$(cat $pci_dev/sriov_totalvfs)"
        echo "$rep_prefix$((pfidx*vfs_per_pf + vfidx))"
        exit 0
    fi
    # vf
    if [[ "$SUBSYSTEM" == "net" && "0x101a 0x101e" =~ "$model_id" ]]; then
        seq="$(vf_seq)"
        total="$(vf_total)"
        [ "$seq" == "$total" ] || exit 203 # only match last one
        echo "$udisk_net"
        exit 0
    fi
    # ib
    if [[ "$SUBSYSTEM" == "infiniband" && "0x101a 0x101e" =~ "$model_id" ]]; then
        seq="$(vf_seq)"
        total="$(vf_total)"
        [ "$seq" == "$total" ] || exit 203 # only match last one
        echo "$udisk_rdma"
        exit 0
    fi
    exit 107
}

cmd_name() {
    # 25G, 4119
    if [ "$model_id" == "0x1017" -o "$model_id" == "0x1018" ]; then
        mlx_25G
        exit 0
    fi

    # 100G, cx5 cx6, 4121 4125
    if [[ "0x1019 0x101a 0x101d 0x101e" =~ "$model_id" ]]; then
        mlx_100G
        exit 0
    fi

    exit 108
}

cmd_rename() {
    name="$1"
    [ -n "$name" ] || exit 101
    
    if [ "$SUBSYSTEM" == "net" ]; then
        if [ "$kernel" != "$name" ]; then
            /sbin/ip link set "$kernel" name "$name"
            [ $? -ne 0 ] && exit 1
            if echo "$name" | grep -qP "^$rep_prefix\d+$"; then # mlx_n
                /sbin/ethtool -L $name combined 4
            fi
        fi
    fi
    if [ "$SUBSYSTEM" == "infiniband" ]; then
        if [ "$kernel" != "$name" ]; then
            /sbin/rdma dev set $kernel name $name
            [ $? -ne 0 ] && exit 2
        fi
    fi

    exit 102
}

case "$1" in
"name")
     # 作为udev rule的PROGRAM值, 做命名计算
     # 计算出的结果通过cmd_rename执行重命名
     cmd_name
     ;;
"rename")
     # 作为udev rule的RUN值, 执行重命名操作
     # 目标名称即cmd_name的计算结果, 通过参数传入
     cmd_rename $2
     ;;
*)
     echo "Usage: $0 must used at udev scope" >&2
     exit 1
esac

