从3588适配atlas 200i pro聊起

背景

在昇腾官方文档《AI-P 加速模块 25.5.T7 驱动开发指南(EP场景)》给出的常见问题中,有适配RK3588开发板时无法跑模型和算力测试的问题描述。具体描述如下:

RK3588芯片自身存在不支持缓存一致性问题,需要手动申请4K对齐的地址保持数据一致。

DMA传输数据时没有保序,需要新增DMA传输数据保序逻辑。

vi ./driver/kernel/pcie_host/devdrv_dma.c

在devdrv_dma_copy函数中,新增如下加粗字体代码。

复制代码
if (dma_dev->dev_status != DEVDRV_DMA_ALIVE) {
        devdrv_warn_spinlock("Dma dev disable. (dev_id=%d)\n", dev_id);
        return -EINVAL;
}
if (dma_node->direction == DEVDRV_DMA_HOST_TO_DEVICE) {
    dma_sync_single_for_device(dma_dev->dev, dma_node->src_addr, dma_node->size, DMA_TO_DEVICE); //即添加此处的dma操作。同步到设备。
}
devdrv_debug_spinlock("Get copy_type. (type=%x; instance=%d; node_cnt=%x; copy_type=%d)\n", para->type, 

vi ./driver/kernel/ts_drv_common/tsdrv_nvme/logic/tsdrv_logic_cq.c

logic_sqcq_phy_cq_handler

复制代码
void logic_sqcq_phy_cq_handler(u32 devid, u32 tsid, struct devdrv_ts_cq_info *cq_info)
{
spin_lock_bh(&logic_cq->lock);
    tsdrv_dma_sync_cpu(devid, cq_sub->phy_addr, cq_sub->size, DMA_FROM_DEVICE);
    offset = cq_sub->slot_size * cq_info->head;
    report = (struct tag_ts_logic_cq_report_msg *)(uintptr_t)(cq_sub->virt_addr + offset);
...(此段代码没有修改,省略)
    offset = cq_sub->slot_size * cq_info->head;
    TSDRV_PRINT_DEBUG("v4(phy_cqid=%u; cq_head=%u; cq_tail=%u; cq_size=0x%lx; cq_depth=%u; report->phase=%u; cq_info->phase=%u)\n",cq_info->index, cq_info->head, cq_info->tail, cq_sub->slot_size, cq_sub->depth, (u32)report->phase, cq_info->phase);
    for (i = 0; i < LOGIC_CQ_REPORT_RETRY_CNT; i++) {
        rmb();

}

这里对DMA操作的双向进行了同步,并且在CPU读取前调用了rmb进行内存屏障处理,以保证顺序。

这些都是常规套路,在各种教材上都会涉及。

然而 这套驱动在飞腾D2000、D2000+、intel x86上运行时都不需要进行上述处理。细品这说明了什么?

tsdrv_dma_sync_cpu

走读host 310P 驱动的代码

tsdrv_dma_sync_cpu

复制代码
void tsdrv_dma_sync_cpu(u32 devid, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir)
{
#ifndef TSDRV_UT
    enum tsdrv_env_type env_type = tsdrv_get_env_type();
    if (env_type == TSDRV_ENV_ONLINE) {
        struct device *pci_dev = NULL;
        pci_dev = tsdrv_get_dev_p(devid);
        if (pci_dev != NULL) {
            dma_sync_single_for_cpu(pci_dev, dma_addr, size, dir);
        }
    }
#endif
}

task_dispatch_handler_ex

复制代码
void task_dispatch_handler_ex(u32 devid, u32 fid, u32 tsid, u32 tail_valid, u32 vcq_tail)
{

    info = tsdrv_get_cpu_task(phy_cq);
    tsdrv_dma_sync_cpu(devid, phy_cq->paddr, phy_cq->size * phy_cq->depth, DMA_FROM_DEVICE);
    while (info->phase == phy_cq->phase) {
        if ((tail_valid == 1) && (phy_cq->head == vcq_tail)) {
            break;

shm_sqcq_rec_handler

复制代码
STATIC int shm_sqcq_rec_handler(u32 devid, struct tsdrv_ts_resource *ts_res, struct tsdrv_ctx *ctx,
    struct devdrv_recycle_message *recycle_msg)
{
  
    offset = cq_sub->slot_size * cq_info->head;
    shm_cq_report = (struct tag_ts_driver_msg *)((uintptr_t)cq_sub->virt_addr + offset);
    tsdrv_dma_sync_cpu(devid, cq_sub->phy_addr, cq_sub->size, DMA_FROM_DEVICE);
    while (shm_cq_report->phase == cq_info->phase) {

shm_sqcq_phy_cq_handler

dma_sync_single_for_device

tsdrv_ioctl_sq_msg_send

复制代码
int tsdrv_ioctl_sq_msg_send(struct tsdrv_ctx *ctx, struct devdrv_ioctl_arg *arg)
{


   
    sq_sub = tsdrv_get_sq_sub_info(ts_res, sq_id);
    if (sq_sub->addr_side == TSDRV_MEM_ON_HOST_SIDE) {
        dma_sync_single_for_device(tsdrv_dev->dev, sq_sub->phy_addr, sq_sub->queue_size, DMA_TO_DEVICE);
    }


    return 0;
}

devdrv_dma_copy_sq_desc_to_slave

复制代码
 dma_sync_single_for_device(chan->msg_dev->dev, src, len, DMA_TO_DEVICE);
    ret = devdrv_dma_async_copy_plus(chan->msg_dev->pci_ctrl->dev_id, data_type, instance, src, dst,
                                     len, DEVDRV_DMA_HOST_TO_DEVICE, para);

hdcdrv_sync_used_fast_mem

复制代码
hdcdrv_sync_used_fast_mem


        /* dma_map_page or dma_map_single address, cache consistency is not
        guaranteed(arm), need to cooperate with dma_sync_single */
        if ((type == HDCDRV_FAST_MEM_TYPE_TX_DATA) || (type == HDCDRV_FAST_MEM_TYPE_TX_CTRL)) {
            dma_sync_single_for_device(dev, addr, dma_len, DMA_TO_DEVICE);
        } else {
            dma_sync_single_for_cpu(dev, addr, dma_len, DMA_FROM_DEVICE);
        }

/* dma_map_page or dma_map_single address, cache consistency is not

guaranteed(arm), need to cooperate with dma_sync_single */

既然第一章的两段代码都没有做同步处理,为何2、3两章中这些代码需要处理呢? 和最后这段注释类似,因为arm不保证,那么在某些arm保证一致性的机器里面,这些代码不就产生了冗余。毕竟内核对同步接口的实现根据CPU类型来做的,而非区分飞腾或者RK arm来实现的。

或者

3588 GIC的bug

[PATCH v2] arm64: dts: rockchip: Use "dma-noncoherent" in base RK3588 SoC dtsi

[PATCH v2] arm64: dts: rockchip: Use "dma-noncoherent" in base RK3588 SoC dtsi

Linux-Kernel Archive: [PATCH v2] arm64: dts: rockchip: Use "dma-noncoherent" in base RK3588 SoC dtsi

复制代码
gic: interrupt-controller@fe600000 {
compatible = "arm,gic-v3";
+ dma-noncoherent;
reg = <0x0 0xfe600000 0 0x10000>, /* GICD */
<0x0 0xfe680000 0 0x100000>; /* GICR */
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH 0>;
interrupt-controller;
mbi-alias = <0x0 0xfe610000>;
mbi-ranges = <424 56>;
msi-controller;
ranges;
#address-cells = <2>;
#interrupt-cells = <4>;
#size-cells = <2>;

its0: msi-controller@fe640000 {
compatible = "arm,gic-v3-its";
+ dma-noncoherent;
reg = <0x0 0xfe640000 0x0 0x20000>;
msi-controller;
#msi-cells = <1>;
};

its1: msi-controller@fe660000 {
compatible = "arm,gic-v3-its";
+ dma-noncoherent;
reg = <0x0 0xfe660000 0x0 0x20000>;
msi-controller;
#msi-cells = <1>;

问题根源:GIC-600 集成设计缺陷

  • 涉及的硬件:RK3588 内建的 ARM GIC-600 中断控制器。GIC-600 是 ARM 公司设计的 IP 核,其原始设计是支持"共享性(shareability)"属性的。

  • 缺陷表现 :RK3588 在将 GIC-600 IP 集成到其 SoC 中时,其 ACE/ACE-lite 总线接口未能正确实现,导致该 IP 的"共享性"功能无法使用。

DPDK

下面以Intel的I350网卡为例:(对于其它厂家的PCIE网卡,需要按照下面第6点的说明,让厂家修改符合我们平台要求的驱动)

复制代码
1. kernel defconfig打开如下配置:
+CONFIG_UIO=m
+CONFIG_HUGETLBFS=y

2. kernel代码修改如下:
diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h
index 77131e8fefcc1..0a70d70bed6df 100644
--- a/include/linux/uio_driver.h
+++ b/include/linux/uio_driver.h
@@ -45,7 +45,7 @@ struct uio_mem {
     struct uio_map        *map;
 };
 
-#define MAX_UIO_MAPS    5
+#define MAX_UIO_MAPS    13

 struct uio_portio;
 
@@ -65,7 +65,7 @@ struct uio_port {
     struct uio_portio    *portio;
 };
 
-#define MAX_UIO_PORT_REGIONS    5
+#define MAX_UIO_PORT_REGIONS    13
 
 struct uio_device {
     struct module           *owner;

3.把rk_drivers/igb_uio驱动,用实际使用的内核编译出一个igb_uio.ko
+obj-m                += igb_uio/

配置

#驱动(下面两个ko都从实际使用的内核编译)

insmod uio.ko

insmod igb_uio.ko

#开启性能模式(命令报错忽略)

echo performance | tee $(find /sys/ -name *governor) /dev/null || true

#开启hugepages

echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

#绑定网卡 (0000:01:00.X要改成实际的)

dpdk/usertools/dpdk-devbind.py -b igb_uio 0000:01:00.0

dpdk/usertools/dpdk-devbind.py -b igb_uio 0000:01:00.1

dpdk/usertools/dpdk-devbind.py -b igb_uio 0000:01:00.2

dpdk/usertools/dpdk-devbind.py -b igb_uio 0000:01:00.3

... ...

#测试工具/方法

(DPDK可以直接从官网下载编译,具体参考GMAC开发文档的介绍)

dpdk/build/app/dpdk-testpmd

./dpdk-testpmd --iova-mode=pa -l 0,2,3 --main-lcore=0 -- -i

RK平台特殊说明

RK主控硬件层不支持DMA访问外部内存一致性, 而开源DPDK代码网卡驱动使用的API:rte_eth_dma_zone_reserve 和 rte_mbuf_raw_alloc,默认要求硬件保证访问内存一致性,比如:

发送数据的场景:CPU把数据写到内存(带cache),然后通知网卡DMA来搬运这块内存的数据,DPDK默认支持的硬件平台会自动刷新cache,使的网卡DMA能直接拿到最新数据,而RK平台需要手动取刷新;

DPDK内存主要有两种:

一个是给网卡BD描述符使用的内存,使用rte_eth_dma_zone_reserve来分配,由于它会被频繁使用,所以解决策略是在内核使用dma_alloc_coherent分配非cache的内存,然后映射给dpdk的网卡驱动使用(查看igb_uio驱动的修改);

二是网卡存放数据的内存,比如用rte_mbuf_raw_alloc分配,由于内存分配量比较大,所以直接使用arm标准的指令刷cache的命令来实现,比如写发送数据时,写完数据后主动刷新这块内存,让实际的内存写到DDR里面取,此时DMA就能拿到实际写入的数据;(参考e1000网卡的发送函数的实现)

6、如果要支持其它型号的PCIE网卡,请按照上述的要求让网卡厂商去修改他们的驱动即可。

7、共存:

PCIE和GMAC共存问题

7.1 请优先加载PCIE的DPDK驱动,然后再加载GMAC的.

7.2 PCIE intel网卡和rtk网卡一起使用情况

r8168_ethdev.c static int r8168_uio_cnt = 0;

igb_ethdev.c static int uio_cnt = 0;

规则修改如下:

intel 4口网卡先加载/rtk后加载,占用uio0/1/2/3, rtl8111h从uio4开始,注意如果0/1/2/3代表物理网口的个数,要按直接情况修改r8168_uio_cnt = 4;

总结

RK3588 在cache一致性支持不完善,导致在适配各种软件时需要特殊处理。

相关推荐
Zain Lau20 天前
华为昇腾310P废物利用——大模型推理服务
人工智能·昇腾·310p·华为昇腾推理芯片
yaoxin5211231 个月前
IRIS 代码格式化 Skill 使用说明
cache·iris·m·isc
放飞梦想C2 个月前
CPU Cache
linux·cache
Felven3 个月前
华为昇腾310P带宽性能测试
华为·昇腾·带宽测试·310p
曲幽4 个月前
FastAPI实战:Redis缓存与分布式锁的深度解析
redis·python·cache·fastapi·web·lock
曲幽4 个月前
FastAPI缓存提速实战:手把手教你用Redis为接口注入“记忆”
redis·python·cache·fastapi·web·asyncio
一个平凡而乐于分享的小比特4 个月前
Cache与主存映射方式详解:三种“找车位”策略
cache·直接映射·主存映射·全相联映射·组相联映射
一个平凡而乐于分享的小比特4 个月前
Cache(高速缓冲器)完全解读:计算机的速度“秘密武器”
cache·高速缓冲器
robinson19884 个月前
验证崖山数据库标量子查询是否带有CACHE功能
数据库·oracle·cache·自定义函数·崖山·标量子查询