修志龙_ZenonXiu · 2022年12月10日 · 上海市

系统设计提示:可能的GIC ITS的 command queue和ITS tables访问一致性问题

理解此文需要对GICv3构架,GICv3 IP和GIC driver有一定的知识。

GIC ITS的ITS tables和Command Queue访问

GICv3的ITS将Message Based Interrupt (MSI)转换为一个发送给GIC Redistributor的LPI中断。这个过程需要使用GIC ITS Device Table,Interrupt Translation Tables和Collection Table.
1.jpg

而这些Device Table,Interrupt Translation Tables和Collection Table是放在外部内存中的,它们占用的内存由系统软件分配(通常是GIC driver),软件通过将这些table的base address写入GICITS_BASER[n]寄存器告诉ITS hardware。为了加速GIC ITS对这些tables的访问,GIC hardware可以对这些table项进行缓存(cache)。

未命名绘图-第 2 页.jpg

CPU软件如GIC driver一般只在分配这些table时,直接写0到它们进行初始化。这些table对这些table项的配置是由软件发送command到ITS command queue来间接实现的,ITS command queue也是放在外部内存中的,它占用的内存也是由软件分配。软件通过将command queue的base address写入GICITS_CBASER寄存器告诉ITS hardware.为了加速GICITS对这些tables的访问,GIC hardware可以对这些command queue进行缓存(cache)。

在GICITS_BASER[n]和GICITS_CBASER寄存器中,除了上面所说的cacheability属性之外,还可以设置shareability属性。通过将ITS对这些table和command queue的访问设置为Shareable, Cacheable, 可能可以实现GIC ITS和CPU对它们访问的一致性,避免额外的cache maintenance操作,但这取决于hardware层面上是否可以实现这个一致性。

对于GIC-500, 因为它ITS访问tables和command queue的接口是AXI4 master interface. 因此硬件上不支持与CPU的coherency,它的GICITS_BASER[n]和GICITS_CBASER寄存器中的shareability属性是不可写的。但其cacheability还是可以programmable的。在GIC-500的TRM中提到,

The GIC-500 does not support shareability, but does have programmable cacheability settings. However, as the GIC-500 is unable to snoop caches, ARM recommends that cacheability fields in registers such as GICR_PENDBASER are always programmed to 0b001 (Normal, Non-cacheable).

对于GIC-600/GIC-700, GIC ITS访问tables和command queue的接口是ACE-lite master interface, 通过coherent interconnect, 硬件上可以实现与CPU的一致性。因而它的GICITS_BASER[n]和GICITS_CBASER寄存器中的shareability和cacheability属性都是programmable的。

Linux kernel的处理方式

Linux kernel的GICv3 driver是在一般的Normal Cacheable, Shareable的内存中分配ITS tables和command queue的内存的。因而CPU软件对它的访问属性是Normal Cacheable, Shareable的。
Linux kernel的GICv3 driver通过检测GICITS_BASER[n]和GICITS_CBASER寄存器中的shareability是否可写(方式是:先在寄存器中写为shareable,在读回这个值,看看是否写成功)来判断硬件设计上GIC ITS和CPU是否可以实现一致性。
1. 如果shareability可以成功设置为shareable,那么在GICITS_BASER[n]和GICITS_CBASER寄存器中设置GIC ITS对ITS tables/command queue访问属性为Normal Cacheable, Shareable。
GIC-600/700的GICITS_BASER[n]和GICITS_CBASER寄存器中的shareability是可写的。
2. 如果shareability不能成功设置为shareable,那么在GICITS_BASER[n]和GICITS_CBASER寄存器中设置GIC ITS对ITS tables/command queue访问属性为Normal non-Cacheable, non-Shareable.同时设置一个软件flag, ITS_FLAGS_CMDQ_NEEDS_FLUSHING, 在GIC driver将ITS命令写入command queue后额外加上gic_flush_dcache_to_poc,进行cache clean到PoC(到外部内存)。
GIC-500的GICITS_BASER[n]和GICITS_CBASER寄存器中的shareability是不可写的。
具体代码见

https://elixir.bootlin.com/li...  


    baser = (virt_to_phys(its->cmd_base)    |
         GITS_CBASER_RaWaWb        |
         GITS_CBASER_InnerShareable    |
         (ITS_CMD_QUEUE_SZ / SZ_4K - 1)    |
         GITS_CBASER_VALID);

    gits_write_cbaser(baser, its->base + GITS_CBASER);
    tmp = gits_read_cbaser(its->base + GITS_CBASER);

    if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) {
        if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) {
            /*
             * The HW reports non-shareable, we must
             * remove the cacheability attributes as
             * well.
             */
            baser &= ~(GITS_CBASER_SHAREABILITY_MASK |
                   GITS_CBASER_CACHEABILITY_MASK);
            baser |= GITS_CBASER_nC;
            gits_write_cbaser(baser, its->base + GITS_CBASER);
        }
        pr_info("ITS: using cache flushing for cmd queue\n");
        its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING;
    }

    gits_write_cwriter(0, its->base + GITS_CWRITER);

https://elixir.bootlin.com/li... 

static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd)
{
    /*
     * Make sure the commands written to memory are observable by
     * the ITS.
     */
    if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING)
        gic_flush_dcache_to_poc(cmd, sizeof(*cmd));
    else
        dsb(ishst);
}

可能存在的问题

包含GIC-600/GIC-700的系统中,虽然ITS可以通过ACE-Lite支持IO coherency, 但有的系统可能会这样设计
2.png

*注:GIC-600 ITS可以有单独的ACE-lite port,或是共用GIC Distributor的ACE-Lite port
GIC-700 ITS共用GIC Distributor的ACE-Lite port*

GIC ITS/Distributor通过一个interconnect将ACE-Lite转换为了AXI interface,因而这个系统设计不能实现GIC ITS和CPU的硬件一致性。
在这样的系统上运行Linux GICv3 driver可能会出现一致性问题:
因为GIC-600/700的GICITS_BASER[n]和GICITS_CBASER寄存器中的shareability是可写的,在GICITS_BASER[n]和GICITS_CBASER寄存器中设置GIC ITS对ITS tables/command queue访问属性为Normal Cacheable, Shareable,并且GIC driver在发送ITS command到command queue后,不会附加CPU cache clean的操作。但却无法实现硬件的一致性,因而可能出现GIC ITS无法看到还在CPU cache中的command。

解决方式

对于这样的系统,可能需要hack一下GICv3 driver代码,将GICITS_BASER[n]和GICITS_CBASER寄存器中强行设置GIC ITS对ITS tables/command queue访问属性为Normal non-Cacheable, non-Shareable


    baser = (virt_to_phys(its->cmd_base)    |
         GITS_CBASER_RaWaWb        |
         GITS_CBASER_InnerShareable    |
         (ITS_CMD_QUEUE_SZ / SZ_4K - 1)    |
         GITS_CBASER_VALID);

    gits_write_cbaser(baser, its->base + GITS_CBASER);
    tmp = gits_read_cbaser(its->base + GITS_CBASER);

            /*
             * The HW reports non-shareable, we must
             * remove the cacheability attributes as
             * well.
             */
            baser &= ~(GITS_CBASER_SHAREABILITY_MASK |
                   GITS_CBASER_CACHEABILITY_MASK);
            baser |= GITS_CBASER_nC;
            gits_write_cbaser(baser, its->base + GITS_CBASER);
        
        pr_info("ITS: using cache flushing for cmd queue\n");
        its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING;

    gits_write_cwriter(0, its->base + GITS_CWRITER);

更长远的方式是在Linux kernel 社区推出一个patch, 在device tree/ACPI的GIC ITS node中新加一个标识系统是否可以实现GIC ITS和CPU一致性的属性。而不是现在这样通过仅仅判断GIC IP本身是否支持一致性。

扩展内容

GICv3 driver 对GIC Redistributor访问LPI tables (如LPI pending table,通过GIC Distributor的interface访问)有如上类似的处理,也可能有类似的一致性问题。

推荐阅读
关注数
8647
内容数
61
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息