arm linux的ASID (Address Space ID)
来自本人的旧博客 http://blog.163.com/awaken_ing/blog/static/1206131972015112011286335
平台:ARM Versatile Express for Cortex-A9 (ARMv7)
# CONFIGARMLPAE 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变量asidmap来记录的. set bit的操作在newcontext()中可以找到. 查找对该变量的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是否还有效.
本文地址: https://awakening-fong.github.io/posts/arm/arm_asid
转载请注明出处: https://awakening-fong.github.io
若无法评论, 请打开JavaScript, 并通过proxy.
blog comments powered by Disqus