linux下关闭PCI设备访问控制限制ACS的方法

在做具体操作之前我们来了解下ACS,ACS是指PCI访问控制服务(PCI Access Control Service),是一种在启用VT-d(I/O虚拟化技术)时用于控制PCI设备访问权限的机制,属于BIOS芯片组配置中的选项,用于限制不同PCI设备之间的直接通信,防止未授权访问。
我们看到上述介绍以后了解到P2P流量会有限制需要绕行Root Complex,GPU之间无法直接通信且RDMA网卡与GPU之间也无法通过网卡与 GPU 之间无法直接通过GDR通信。

1.关闭ACS优势

我们在GPU场景下关闭ACS以后具有以下优势
P2P通信带宽提升5-15%
虚拟化延迟降低5-15%
NCCL_ALLREDUCE性能提升:
- 小消息(8KB-1MB):提升3-8%
- 大消息(>100MB):提升1-3%
虚拟化GPU性能提升:
- TensorFlow训练:加速2-7%
- CUDA内核启动延迟:减少5-15%
- VRAM访问延迟:改善3-10%
DMA事务延迟:降低10-20%

但是我们在关闭ACS前还是需要建议做些充分的测试,确保应用兼容性和进行对应的风险评估

2.查询ACS是否开启

我可以使用命令查询当前ACS是否开启
lspci -vvv | grep -i "acsctl"

202601192026054327210579.png

可以看到我返回的都是SrcValid-,说明当前环境已经关闭了ACS

ACSCtl: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans-

如果没有关闭会显示SrcValid+

3.如何关闭ACS

接下来我们就看看如何关闭ACS,关闭ACS需要我们做几块地方,不同的平台可能有一些不同,下面就是我收集整理的关闭ACS的方法,如果没有bios权限可以使用3.3的方法,但是不能确保成功。

3.1.BIOS中关闭ACS

在所有方法中还是最推荐在bios里面进行修改,这样避免侵入设备配置空间,导致错误修改,或者修改了不该修改的地方导致不稳定的情况出现。

进入bios,一般在高级->主板配置->北桥->IIO配置->VT-d->ACS控制器,然后选择Disable

3.2.内核参数绕过

这种配置有局限性,主要是在/etc/default/grub文件添加iommu=pt
然后整个grub中相关配置如下
GRUB_CMDLINE_LINUX="iommu=pt"

然后更新grub并重启

sudo update-grub

3.3.使用setpci修改

修改PCI设备的配置空间寄存器,包括ACS能力寄存器,修改后重启失效
读取当前值

sudo setpci -s <PCI地址> <ACS能力寄存器偏移>.w

写入新值

sudo setpci -s <PCI地址> <ACS能力寄存器偏移>.w=<新值>

我们可以使用脚本进行遍历修改

lspci -nn | grep -iE "nvidia|gpu" | awk '{print $1}' | while read -r bus; do
    echo ">>> 处理 GPU: $bus"
    pos=0x100   # 扩展能力起始位置
    found=0
    while (( pos < 0x1000 )); do
        # 读取 16 位能力 ID
        cap_id_hex=$(setpci -s "$bus" $(printf "0x%x" $pos).w 2>/dev/null)
        if [ -z "$cap_id_hex" ]; then
            break
        fi
        cap_id=$(( 16#$cap_id_hex & 0xFFFF ))
        # 读取下一个能力指针(偏移 +2,16 位,但按字节计算需乘注意事项)
        # 实际扩展能力头格式:能力 ID (2B) + 能力版本+下一能力指针 (2B)
        # 其中下一能力指针是 12 位?不对,标准是 16 位中的低 12 位是偏移,高 4 位是版本。
        # 简便方法:直接读取下一能力指针的原始 16 位值,然后取低 12 位(实际偏移 = 值 & 0xFFF)
        next_ptr_hex=$(setpci -s "$bus" $(printf "0x%x" $((pos+2))).w 2>/dev/null)
        if [ -z "$next_ptr_hex" ]; then
            break
        fi
        next_ptr=$(( 16#$next_ptr_hex ))
        next_off=$(( next_ptr & 0xFFF ))  # 低 12 位是下一能力偏移
        
        if (( cap_id == 0x000D )); then
            echo "找到 ACS 能力: $bus @ 0x$(printf %x $pos)"
            # ACS 控制寄存器在能力偏移 + 0x06,16 位宽
            ctrl_off=$((pos + 0x06))
            setpci -s "$bus" $(printf "0x%x" $ctrl_off).w=0x0000
            if [ $? -eq 0 ]; then
                echo "$bus : ACS 禁用成功!"
            else
                echo "$bus : ACS 禁用失败"
            fi
            found=1
            break
        fi
        if (( next_off == 0 )) || (( next_off <= pos )); then
            break
        fi
        pos=$next_off
    done
    if (( found == 0 )); then
        echo "$bus : 未找到 ACS 能力(可能不支持)"
    fi
done

另外需要注意以下设备不要进行修改,避免引起系统或和硬件不稳定

1.PCIe根端口/桥接器
2.系统芯片组设备
3.USB/SATA控制器

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://sulao.cn/post/1163

评论列表

0%