Cache Behaviour in Linux -- Cache Policy识别情景分析

Linux在boot的过程中判断cache policy的入口是cacheid_init().
 297 static void __init cacheid_init(void)
 298 {
 299         unsigned int cachetype = read_cpuid_cachetype();
 300         unsigned int arch = cpu_architecture();
 301
 302         if (arch >= CPU_ARCH_ARMv6) {
 303                 if ((cachetype & (7 << 29)) == 4 << 29) {
 304                         /* ARMv7 register format */
 305                         arch = CPU_ARCH_ARMv7;
 306                         cacheid = CACHEID_VIPT_NONALIASING;
 307                         switch (cachetype & (3 << 14)) {
 308                         case (1 << 14):
 309                                 cacheid |= CACHEID_ASID_TAGGED;
 310                                 break;
 311                         case (3 << 14):
 312                                 cacheid |= CACHEID_PIPT;
 313                                 break;
 314                         }
 315                 } else {
 316                         arch = CPU_ARCH_ARMv6;
 317                         if (cachetype & (1 << 23))
 318                                 cacheid = CACHEID_VIPT_ALIASING;
 319                         else
 320                                 cacheid = CACHEID_VIPT_NONALIASING;
 321                 }
 322                 if (cpu_has_aliasing_icache(arch))
 323                         cacheid |= CACHEID_VIPT_I_ALIASING;
 324         } else {
 325                 cacheid = CACHEID_VIVT;
 326         }
303行,根据cache type register的第31位判断是否为ARMV7. 31-29位是100:代表ARM v7;000代表ARMV6.
判断ARMV7的ICache的Policy呢?307~313行便是做这部分工作。
307行,比较cache type register中的第14和15位(0 based)。在V7的spec(P)中,该位写死了,其值为:10. 在PJ4C的spec中,其值是11,代表PIPT。(official v7 spec估计是笔误。)
Level 1 instruction cache policy. Indicates the indexing and tagging policy for the L1 instruction cache. 
00 Reserved
01 ASID-tagged Virtual Index, Virtual Tag (AIVIVT)
10 Virtual Index, Physical Tag (VIPT)
11 Physical Index, Physical Tag (PIPT)
引用自:DDI0406B.P1358.

315~320是ARMv6的代码。根据其代码,可以看到V6的Dcache全部都是VIPT。那么,到底是否存在cache aliasing问题呢?看317行。
317行根据第23位(0 based),来判断是否存在cache aliasing问题。
如果存在cache aliasing问题,那么cacheid的值为CACHEID_VIPT_ALIASING. 若Cache存在aliasing问题,则会产生很多异常。例如:VA1和VA2同时mapping到PA上,VA1通过VIPT的policy到cache中的某些连续的sets中去hit页,VA2也通过VIPT的policy到cache中的某些连续的sets中去hit页,如果VA1和VA2在cache中的mapping不一样,就会造成:同一份物理页在cache中有两份copy的问题,这个问题造成的直接后果是:进程通过VA1修改的数据后,通过VA2并不能立即得到最新的数据。
v6是如何解决这种问题的呢:
  • If multiple virtual addresses are mapped onto the same physical addresses, then for all mappings bits [13:12] of the virtual address must be equal, and must also be equal to bits [13:12] of the physical address. The same physical address can be mapped by TLB entries of different page sizes. These can be 4KB, 64KB, or sections.
  • If all mappings to a physical address are of a page size equal to 4KB, the restriction that bits [13:12] of the virtual address must equal bits [13:12] of the physical address is not required. Bits [13:12] of all virtual address aliases must still be equal.
引用自:DDI0406B.P1985.
即,v6的spec.要求映射同一块物理地址的虚拟地址,第13,12位必须相同(相同的page colour),这样,就保证了VA1和VA2在cache中的mapping是一样的。
322行是判断ICache是否具有aliasing,其代码如下:
 264 static int cpu_has_aliasing_icache(unsigned int arch)
 265 {
 266         int aliasing_icache;
 267         unsigned int id_reg, num_sets, line_size;
 268
 269         /* PIPT caches never alias. */
 270         if (icache_is_pipt())
 271                 return 0;
 272
 273         /* arch specifies the register format */
 274         switch (arch) {
 275         case CPU_ARCH_ARMv7:
 276                 asm("mcr        p15, 2, %0, c0, c0, 0 @ set CSSELR"
 277                     : /* No output operands */
 278                     : "r" (1));
 279                 isb();
 280                 asm("mrc        p15, 1, %0, c0, c0, 0 @ read CCSIDR"
 281                     : "=r" (id_reg));
 282                 line_size = 4 << ((id_reg & 0x7) + 2);
 283                 num_sets = ((id_reg >> 13) & 0x7fff) + 1;
 284                 aliasing_icache = (line_size * num_sets) > PAGE_SIZE;
 285                 break;
 286         case CPU_ARCH_ARMv6:
 287                 aliasing_icache = read_cpuid_cachetype() & (1 << 11);
 288                 break;
 289         default:
 290                 /* I-cache aliases will be handled by D-cache aliasing code */
 291                 aliasing_icache = 0;
 292         }
 293
 294         return aliasing_icache;
 295 }
判断V6的ICache是否具有cache alias的方法和判断DCache的方法是一样的。不同的是Dcache取决于CTR(Cache Type Register)的第23位, ICache取决于CTR的第11位(286~289行)。
在cacheid_init()函数中,判断了ARMV7是PIPT还是VIPT还是其他,那么如何判断其是否具有Alias问题呢?就要看“log2(Way_Size) > log2(PAGE_SIZE)”的值了(284行)。
以上,主要分析了ARM V7的Icache的Cache Policy以及是否存在Alias在Linux的识别过程。
ARM V7的Dcache的Cache Policy是怎样的呢?cacheid_init()函数的306行已经写死了。其值是CACHEID_VIPT_NOALIASING的。
即,Linux认为对于V7来说,DCache一定是VIPT with no alias或者PIPT,ICache可能是VIPT with alias或者PIPT或者ASID Taged的VIVT。对于VIPT,是否具有alias问题呢,要看log2(Way_size)与log2(PAGE_SIZE)的比较结果了。

评论

此博客中的热门博文

Linux/ARM Page Table Entry 属性设置分析

提交了30次才AC ---【附】POJ 2488解题报告

笔记