arm linux的iounmap()如何处理主页表(master page table)的同步
来自本人的旧博客: http://blog.163.com/awaken_ing/blog/static/120613197201510308269773
1. 背景
平台为qemu ARM Versatile/PB (ARM926EJ-S) ARMv5TEJ, UP
我们知道进程的内核地址空间的页表 和 swapperpgdir 进行同步, 在进程创建时, do_fork()会拷贝一级页表.
2. 问题
vmalloc() area发生异常, 由dotranslationfault()处理, 处理的是一级页表无效的情况, 处理方法: If the init_task's first level page tables contains the relevant entry, we copy the it to this task.
可以猜想, vmalloc区域的页表是 两级页表(低两位D1D0为0b01), 而不是section (D1D0为0b10)
打印vmalloc区域的对应的页表, 可以看到 D1D0 既有0b01(指向二级页表), 也有0b10(无需二级页表) 的.
从cat /proc/vmallocinfo可知, 可知0b10不是由vmalloc()引发的页表创建, 而是ioremap(). (这里并不是说ioremap创建的都是section)
虚拟地址0xc9000000开始对应的一级页表entry
(gdb) p /x *(int *)0xc0007240@65
$1 = {0x34000452, 0x34100452, 0x34200452, 0x34300452, 0x34400452, 0x34500452, 0x34600452, 0x34700452, 0x34800452,
0x34900452, 0x34a00452, 0x34b00452, 0x34c00452, 0x34d00452, 0x34e00452, 0x34f00452, 0x35000452, 0x35100452, 0x35200452,
0x35300452, 0x35400452, 0x35500452, 0x35600452, 0x35700452, 0x35800452, 0x35900452, 0x35a00452, 0x35b00452, 0x35c00452,
0x35d00452, 0x35e00452, 0x35f00452, 0x36000452, 0x36100452, 0x36200452, 0x36300452, 0x36400452, 0x36500452, 0x36600452,
0x36700452, 0x36800452, 0x36900452, 0x36a00452, 0x36b00452, 0x36c00452, 0x36d00452, 0x36e00452, 0x36f00452, 0x37000452,
0x37100452, 0x37200452, 0x37300452, 0x37400452, 0x37500452, 0x37600452, 0x37700452, 0x37800452, 0x37900452, 0x37a00452,
0x37b00452, 0x37c00452, 0x37d00452, 0x37e00452, 0x37f00452, 0x0}
# cat /proc/vmallocinfo
...
0xc9000000-0xcd001000 67112960 armflash_subdev_probe+0x64/0x120 ioremap
对于 0b10(无需二级页表) 就不好理解. 考虑这样一种情况,
进程A执行ioremap(), 然后进程B更新了the first level page table. 之后进程A执行iounmap(), 进程B如何能够知道呢? (use-after-iounmap cases)
所以, ioremap()对应的iounmap() 如何处理一级页表, 才能达到 页表间的同步?
3. 解
先看下在何处创建:
虚拟地址的0xc9000000的一级页表在0xc0007240
(gdb) watch *(unsigned int *)0xc0007240
Watchpoint 5: *(unsigned int *)0xc0007240
(gdb) c
Continuing.
Watchpoint 5: *(unsigned int *)0xc0007240
Old value = 0
New value = 872416338
remap_area_sections (virt=<value optimized out>, pfn=213248, size=<value optimized out>, type=<value optimized out>)
at arch/arm/mm/ioremap.c:207
207 pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
(gdb) p /x 872416338
$7 = 0x34000452
由 (gdb) bt 可知, 调用路径
ioremap
|--__arm_ioremap
| |--__arm_ioremap_caller
| | |--__arm_ioremap_pfn_caller
| | | |--remap_area_sections
ioremap()有remapareasections(), 相对应的iounmap()有unmapareasections()
linux-2.6.35.7/arch/arm/mm/ioremap.c
#ifndef CONFIG_SMP
/*
* Section support is unsafe on SMP - If you iounmap and ioremap a region,
* the other CPUs will not see this change until their next context switch.
* Meanwhile, (eg) if an interrupt comes in on one of those other CPUs
* which requires the new ioremap'd region to be referenced, the CPU will
* reference the _old_ region.
*
* Note that get_vm_area_caller() allocates a guard 4K page, so we need to
* mask the size back to 1MB aligned or we will overflow in the loop below.
*/
static void unmap_area_sections(unsigned long virt, unsigned long size)
{
do {
...
init_mm.context.kvm_seq++;
...
} while (addr < end);
/*
* Ensure that the active_mm is up to date - we want to
* catch any use-after-iounmap cases.
*/
if (current->active_mm->context.kvm_seq != init_mm.context.kvm_seq)
__check_kvm_seq(current->active_mm);
}
void __check_kvm_seq(struct mm_struct *mm)
{
...
memcpy(pgd_offset(mm, VMALLOC_START), ...);
...
}
这里只处理了一个进程, 其他进程呢? 搜索 kvm_seq, 找到
linux-2.6.35.7/arch/arm/include/asm/mmu_context.h
check_context
{
if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))
__check_kvm_seq(mm);
}
switch_mm
{
check_context(next);
}
所以, 在上下文切换时, 会知道一级页表发生了变化, 进而同步上.
本文地址: https://awakening-fong.github.io/posts/arm/arm_iounmap_master_table
转载请注明出处: https://awakening-fong.github.io
若无法评论, 请打开JavaScript, 并通过proxy.
blog comments powered by Disqus