目录
目的
以ASID为8位长度叙述
引入版本号好处
当进程调度时
为了减少在进程切换时清空页表缓存的需要,ARM64处理器的页表缓存使用非全局位区分内核和进程的页表项。
ASID只有256个值,其中0是保留值;进程数量超过255,两个ASID可能相同,内核引入版本号,解决。具体步骤如下:
1)每个进程有64位软件ASID,低8位存放硬件ASID,高56位存放ASID版本号;
2)64位全局变量asid_generation的高56位保存全局ASID版本号;
3)当进程被调度时,比较进程ASID版本号和全局ASID版本号,如果版本号相同,那么直接使用上次分配的硬件ASID,否则重新给进程分配硬件ASID。
避免每次进程切换都需要清空页表缓存,只需要在硬件ASID回滚时,把处理器的页表缓存清空。
内存描述符成员id存放内核给进程分配的ASID
typedef struct{
atomic64_t id;
}mm_context_t;
//asid_bits保存ASID长度
static u32 asid_bits;
static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
//asid_generation高56位保存全局ASID版本号
static atomic64_t asid_generation;
//位图asid_map记录哪些ASID被分配
static unsigned long *asid_map;
//active_asids 保存处理器正在使用的ASID
static DEFINE_PER_CPU(atomic64_t, active_asids);
//存放预留的ASID,用来在全局变量ASID加1时保存处理器正在执行的进程的ASID
static DEFINE_PER_CPU(u64, reserved_asids);
static cpumask_t tlb_flush_pending;
处理器给进程分配ASID时,如果ASID分配完了,那么把全局ASID加1,重新从1开始分配ASID,针对每个处理器,使用该处理器的reserved_asids保存该处理器正在执行的进程的ASID,并且把该处理器的active_asids设置0。active_asids为0 具有特殊意义,说明全局ASID版本号变化,ASID从255回到1
当全局ASID加1时,每个处理器都需要清空页表缓存,位图tlb_flush_pending保存需要清空页表缓存的处理器集合。
void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)
{
unsigned long flags;
unsigned int cpu = smp_processor_id();
u64 asid;
if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq))
__check_vmalloc_seq(mm);
/*
* We cannot update the pgd and the ASID atomicly with classic
* MMU, so switch exclusively to global mappings to avoid
* speculative page table walking with the wrong TTBR.
*/
cpu_set_reserved_ttbr0();
asid = atomic64_read(&mm->context.id);
if (!((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS)
&& atomic64_xchg(&per_cpu(active_asids, cpu), asid))
goto switch_mm_fastpath;
//禁止硬件中断并申请自旋锁
raw_spin_lock_irqsave(&cpu_asid_lock, flags);
/* Check that our ASID belongs to the current generation. */
//重新比较版本号,如果不同重新分配
asid = atomic64_read(&mm->context.id);
if ((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) {
asid = new_context(mm, cpu);
atomic64_set(&mm->context.id, asid);
}
//tlb_flush_pending 当前处理器对应的位被设置,那么把当前处理器的页表缓存清空
//把全局ASID版本号加1时,需要把所有处理器的页表缓存清空,在位图tlb_flush_pending中把所有处理器对应的位设置
if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending)) {
local_flush_bp_all();
local_flush_tlb_all();
}
//把单当前处理器的active_asids设置为ASID
atomic64_set(&per_cpu(active_asids, cpu), asid);
cpumask_set_cpu(cpu, mm_cpumask(mm));
//释放自旋锁并开启硬中断
raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
switch_mm_fastpath://快速路径
cpu_switch_mm(mm->pgd, mm);
}
1、如果进程的ASID与全局ASID版本号相同,那么调用函数atomic64_xchg 把当前处理器的active_asid设置成进程的ASID,并且返回active_asids的旧值。
2、如果active_asidsde 旧值不是0,那么执行快速路径
3、如果旧值是0,说明其他处理器在分配ASID时把全局ASID版本号加1了,那么执行慢速路径。
4、调用函数cpu_switch_mm设置寄存器TTBR0_EL1