啟動(dòng)信息描述
內(nèi)核啟動(dòng)過(guò)程中有如下打印信息:
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
這行打印信息代表了處理器L1 CACHE所支持的尋址方式。
在kernel啟動(dòng)過(guò)程中,雖然這里第一次出現(xiàn)CACHE相關(guān)的打印信息,但是,此處并不是kernel第一次操作CACHE。
例如:對(duì)于zImage而言,解壓縮過(guò)程中會(huì)開(kāi)啟CACHE并配置CACHE 寫(xiě)屬性。
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
#define CB_BITS 0x08
#else
#define CB_BITS 0x0c
#endif
CPU初始化的匯編文件head.S中,會(huì)根據(jù)kernel配置來(lái)使能CACHE。
#ifdef CONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
代碼分析
以上打印信息源自于 kernel 代碼的setup.c中。
static void __init cacheid_init(void)
{
unsigned int arch = cpu_architecture();
if (arch >= CPU_ARCH_ARMv6) {
unsigned int cachetype = read_cpuid_cachetype();
if ((arch == CPU_ARCH_ARMv7M) && !(cachetype & 0xf000f)) {
cacheid = 0;
} else if ((cachetype & (7 < < 29)) == 4 < < 29) {
/* ARMv7 register format */
arch = CPU_ARCH_ARMv7;
cacheid = CACHEID_VIPT_NONALIASING;
switch (cachetype & (3 < < 14)) {
case (1 < < 14):
cacheid |= CACHEID_ASID_TAGGED;
break;
case (3 < < 14):
cacheid |= CACHEID_PIPT;
break;
}
} else {
arch = CPU_ARCH_ARMv6;
if (cachetype & (1 < < 23))
cacheid = CACHEID_VIPT_ALIASING;
else
cacheid = CACHEID_VIPT_NONALIASING;
}
if (cpu_has_aliasing_icache(arch))
cacheid |= CACHEID_VIPT_I_ALIASING;
} else {
cacheid = CACHEID_VIVT;
}
pr_info("CPU: %s data cache, %s instruction cache\\n",
cache_is_vivt() ? "VIVT" :
cache_is_vipt_aliasing() ? "VIPT aliasing" :
cache_is_vipt_nonaliasing() ? "PIPT / VIPT nonaliasing" : "unknown",
cache_is_vivt() ? "VIVT" :
icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" :
icache_is_vipt_aliasing() ? "VIPT aliasing" :
icache_is_pipt() ? "PIPT" :
cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown");
}
首先,通過(guò)cpu_architecture()獲取處理器內(nèi)核版本號(hào)
int __pure cpu_architecture(void)
{
BUG_ON(__cpu_architecture == CPU_ARCH_UNKNOWN);
return __cpu_architecture;
}
處理器內(nèi)核版本號(hào)通過(guò)數(shù)據(jù)結(jié)構(gòu)proc_arch來(lái)維護(hù),數(shù)據(jù)結(jié)構(gòu)成員的值代表的ARM-vXXX
static const char *proc_arch[] = {
"undefined/unknown",
"3",
"4",
"4T",
"5",
"5T",
"5TE",
"5TEJ",
"6TEJ",
"7",
"7M",
"?(12)",
"?(13)",
"?(14)",
"?(15)",
"?(16)",
"?(17)",
};
處理器內(nèi)核版本編號(hào)如下,例如__cpu_architecture為4則代表的是ARM-v5內(nèi)核。
#define CPU_ARCH_UNKNOWN 0
#define CPU_ARCH_ARMv3 1
#define CPU_ARCH_ARMv4 2
#define CPU_ARCH_ARMv4T 3
#define CPU_ARCH_ARMv5 4
#define CPU_ARCH_ARMv5T 5
#define CPU_ARCH_ARMv5TE 6
#define CPU_ARCH_ARMv5TEJ 7
#define CPU_ARCH_ARMv6 8
#define CPU_ARCH_ARMv7 9
#define CPU_ARCH_ARMv7M 10
需要注意的是,這里獲取處理器內(nèi)核版本號(hào),不是通過(guò)讀取MIDR寄存器來(lái)實(shí)現(xiàn)的。
針對(duì)具體的ARM內(nèi)核版本,獲取CACHE所支持的尋址方式,并據(jù)此初始化cacheid。后面以cacheid為依據(jù)對(duì)CACHE的所支持的尋址方式進(jìn)行分類(lèi)。
在ARM處理器內(nèi)部包含了CACHE類(lèi)型信息的只讀寄存器,這在芯片設(shè)計(jì)初期便已經(jīng)定義好,后期無(wú)法修改。在這個(gè)寄存器中包含了CACHE的尋址策略。對(duì)于ARM v系列的處理器內(nèi)核而言,獲取CACHE類(lèi)型的指令代碼如下:
#define read_cpuid(reg) \\
({ \\
unsigned int __val; \\
asm("mrc p15, 0, %0, c0, c0, " __stringify(reg) \\
: "=r" (__val) \\
: \\
: "cc"); \\
__val; \\
})
基于讀取CACHE類(lèi)型寄存器得到信息來(lái)初始化cacheid,利用cachetype.h中定義的宏,打印出cache類(lèi)型。
基于cacheid定義了如下的宏定義:
#define cache_is_vivt() cacheid_is(CACHEID_VIVT)
#define cache_is_vipt() cacheid_is(CACHEID_VIPT)
#define cache_is_vipt_nonaliasing() cacheid_is(CACHEID_VIPT_NONALIASING)
#define cache_is_vipt_aliasing() cacheid_is(CACHEID_VIPT_ALIASING)
#define icache_is_vivt_asid_tagged() cacheid_is(CACHEID_ASID_TAGGED)
#define icache_is_vipt_aliasing() cacheid_is(CACHEID_VIPT_I_ALIASING)
#define icache_is_pipt() cacheid_is(CACHEID_PIPT)
...
static inline unsigned int __attribute__((pure)) cacheid_is(unsigned int mask)
{
return (__CACHEID_ALWAYS & mask) |
(~__CACHEID_NEVER & __CACHEID_ARCH_MIN & mask & cacheid);
}
而本文開(kāi)頭中打印出來(lái)的信息,就是基于上面這些宏得出的結(jié)果。
從kenrel啟動(dòng)的日志信息中可以看出,當(dāng)前CPU內(nèi)核的CACHE類(lèi)型如下:
DCACHE
PIPT / VIPT nonaliasing
ICACHE
VIPT nonaliasing
ARM處理器不同內(nèi)核版本的CACHE類(lèi)型如下:
通常來(lái)說(shuō),CACHE得尋址類(lèi)型包括VIPT、PIPT、VIVT,而本文重點(diǎn)分析得是VIPT以及PIPT的工作原理。
VIPT和PIPT
具備MMU(TLB)、CACHE的ARM處理器,其地址翻譯的流程如下:
那么上圖中紅框內(nèi)的CACHE部分,根據(jù)索引或標(biāo)簽對(duì)應(yīng)的是物理地址還是虛擬地址,將CACHE的尋址方式分為VIPT CACHE和PIPT CACHE。
VI的含義是使用虛擬地址來(lái)構(gòu)建緩存索引
(index)。反之,PI的含義是使用物理地址來(lái)構(gòu)建緩存索引。
緩存索引用于從緩存中提取標(biāo)記,將它與從物理地址計(jì)算的緩存標(biāo)記比對(duì)。如果匹配,則緩存中命中
該查找,從緩存中提取相關(guān)的數(shù)據(jù)。否則,將從下一層緩存(或從內(nèi)存)中提取。
PT的含義是使用物理地址來(lái)構(gòu)建緩存標(biāo)記
(tag),之所以使用PT作為CACHE查找索引是為了解決VIVT中存在的索引沖突,即兩個(gè)進(jìn)程可以為不同的物理地址使用相同的虛擬地址。
VIPT
VIPT CACHE使用物理地址作為T(mén)ag,邏輯地址作為Index。通過(guò)Index查詢(xún)CACHE獲取到物理地址中的tag部分。同時(shí)呢,利用邏輯地址去查T(mén)LB,在TLB中獲取到物理地址。然后將CACHE中查詢(xún)到的物理地址Tag部分,同TLB中獲取到的物理地址Tag部分作比較。若二者相同,則CACHE hit,否則CACHE miss。
aliasing
對(duì)于VIPT CACHE而言,下面代碼中的virtual_addr_A和virtual_addr_B雖然不同,但是它們指向了同一個(gè)物理地址PA。
mmap(virtual_addr_A,4096,prot,flags,file_descriptor,offset)
mmap(virtual_addr_B,4096,prot,flags,file_descriptor,offset)
VIPT使用虛擬地址作為CACHE Index,因此物理地址A的數(shù)據(jù)在CACHE中有兩份,分別由virtual_addr_A和virtual_addr_B進(jìn)行管理。這樣的情形稱(chēng)之為CACHE 別名。
可以通過(guò)下面的4種方法來(lái)避免別名問(wèn)題帶來(lái)的影響:
1.當(dāng)進(jìn)行內(nèi)存數(shù)據(jù)更新時(shí)進(jìn)行cache invalid操作
- 多副本數(shù)據(jù)同步更新
以上兩種方法需要進(jìn)程通過(guò)虛實(shí)地址轉(zhuǎn)換等操作,獲取到是否有副本數(shù)據(jù)存在這樣的信息。而VIPT的設(shè)計(jì)初衷是避免虛實(shí)地址轉(zhuǎn)換,因此,這兩種方法并不是非常可取。
- 縮小CACHE size
假設(shè)對(duì)于一個(gè)32bit位寬的虛擬地址而言,他的page offset為12bit。若CACHE size小于4K則不會(huì)產(chǎn)生別名問(wèn)題,假設(shè)CACHE size大于4K,則會(huì)產(chǎn)生別名問(wèn)題。但是CACHE過(guò)小,cache miss又會(huì)大幅增加。
- page color或者cache cloring
若以上3種方法都沒(méi)有解決CACHE別名問(wèn)題,那么可以使用緩存著色的方法。
PIPT
PIPT中的tag和index均為物理地址。而CPU發(fā)出的邏輯地址也稱(chēng)之為虛擬地址,因此,首先需要通過(guò)TLB或查詢(xún)內(nèi)存中的頁(yè)表,將邏輯地址轉(zhuǎn)換為對(duì)應(yīng)的物理地址。再進(jìn)行CACHE緩存查找。索引和標(biāo)簽都使用物理地址。雖然這很簡(jiǎn)單,避免了重名問(wèn)題,但速度也很慢,因?yàn)楸仨毾炔檎椅锢淼刂?這可能涉及TLB丟失和訪問(wèn)主內(nèi)存),才能在緩存中查找該地址。
-
處理器
+關(guān)注
關(guān)注
68文章
19160瀏覽量
229116 -
ARM
+關(guān)注
關(guān)注
134文章
9046瀏覽量
366810 -
寄存器
+關(guān)注
關(guān)注
31文章
5317瀏覽量
120004 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21617 -
cache技術(shù)
+關(guān)注
關(guān)注
0文章
41瀏覽量
1047
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論