arm linux的ASID (Address Space ID)

http://blog.163.com/awaken_ing/blog/static/1206131972015112011286335

平台:ARM Versatile Express for Cortex-A9 (ARMv7) 
# CONFIG_ARM_LPAE is not set, 也就是使用Short-descriptor格式, ASID存储在CONTEXTIDR的低8 bit:

 
 

31 7 0 +-------------------------+-----------+ | PROCID | ASID | +-------------------------+-----------+

页表项中 nG == 1时, 这个页表项信息就是 non-global, 或者说 process-specific, 对应的TLB中就会有ASID信息, 执行虚拟地址到物理地址转换时, ASID也需要参与该过程.


用户地址空间的页表才会设置nG标志 (内核地址的一部分地址范围使用TLB lockdown比较合适): 
linux-3.10.86/arch/arm/include/asm/pgtable.h

 
 

static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pteval) { ... if (addr < TASK_SIZE && pte_present_user(pteval)) { ... ext |= PTE_EXT_NG; } ... set_pte_ext(ptep, pteval, ext); }

set_pte_at -> set_pte_ext -> cpu_v7_set_pte_ext

arm linux中通过设置CONTEXTIDR, 进而设置ASID, 相关代码为: 
linux-3.10.86/arch/arm/mm/proc-v7-2level.S

 
 

ENTRY(cpu_v7_switch_mm) ... mcr p15, 0, r1, c13, c0, 1 @ set context ID

调用关系为: 
context_switch() ->switch_mm() -> check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) -> cpu_switch_mm() -> cpu_v7_switch_mm(pgd_t * pgd, struct mm_struct *mm) 
linux把ASID存储在mm->context.id的低8位 (D7:D0).

新创建的非内核线程 (内核线程不会调用到dup_mm), 初始化ASID为0: 
copy_mm -> dup_mm -> init_new_context 
#define init_new_context(tsk,mm)   ({ atomic64_set(&mm->context.id, 0); 0; }) 
到上下文切换的时候才真正开始给ASID赋值, 并设置CONTEXTIDR寄存器.


有分配, 就该有回收, 这也是使用bitmap来管理ASID, 而不是asid++的方式的原因. ASID的分配是通过bitmap变量asid_map来记录的. set bit的操作在new_context()中可以找到. 查找对该变量的clear bit操作, 进程退出时, 本应有该动作(清asid_map中该进程所对应的bit), 但很遗憾, 没有找到, 开发人员偷懒了?

由于ASID只有8bit, 范围为0~0xff, 当这些值分配完后, 就需要flush TLB, 同时对generation加1, 然后重新开始分配ASID. generation为mm->context.id的高24+32位(D63:D8), 这样, 后续再调度到这个进程时, 就可以通过判断generation是否变化了, 来知道 mm->context.id中的ASID是否还有效.


猜你喜欢

转载自blog.csdn.net/adaptiver/article/details/70228767
今日推荐