理解此文需要对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.
而这些Device Table,Interrupt Translation Tables和Collection Table是放在外部内存中的,它们占用的内存由系统软件分配(通常是GIC driver),软件通过将这些table的base address写入GICITS_BASER[n]寄存器告诉ITS hardware。为了加速GIC ITS对这些tables的访问,GIC hardware可以对这些table项进行缓存(cache)。
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, 但有的系统可能会这样设计
*注: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访问)有如上类似的处理,也可能有类似的一致性问题。