high memory 映射 ----2

临时内核映射:

 临时内核映射和永久内核映射相比,不会阻塞请求映射页框的进程,可知临时内核映射请求可以发生在中断和可延迟函数中。也称原子映射;在高端内存的任一页框可以通过一个窗口映射到内核地址空间,根据不同的需求,选择不同的窗口来创建映射,这些窗口都以枚举类型定义在km_type中;

内核必须确保同一个窗口不能被两个不同的控制路径同时使用;

enum km_type {
KMAP_D(0) KM_BOUNCE_READ,
KMAP_D(1) KM_SKB_SUNRPC_DATA,
KMAP_D(2) KM_SKB_DATA_SOFTIRQ,
KMAP_D(3) KM_USER0,
KMAP_D(4) KM_USER1,
KMAP_D(5) KM_BIO_SRC_IRQ,
KMAP_D(6) KM_BIO_DST_IRQ,
KMAP_D(7) KM_PTE0,
KMAP_D(8) KM_PTE1,
KMAP_D(9) KM_IRQ0,
KMAP_D(10) KM_IRQ1,
KMAP_D(11) KM_SOFTIRQ0,
KMAP_D(12) KM_SOFTIRQ1,
KMAP_D(13) KM_SYNC_ICACHE,
KMAP_D(14) KM_SYNC_DCACHE,
/* UML specific, for copy_*_user - used in do_op_one_page */
KMAP_D(15) KM_UML_USERCOPY,
KMAP_D(16) KM_IRQ_PTE,
KMAP_D(17) KM_NMI,
KMAP_D(18) KM_NMI_PTE,
KMAP_D(19) KM_TYPE_NR
};

km_type中的每个符号(除了最后一个)都是固定映射的线性地址的一个下标。enum fixed_addressed数据结构包含符号FIX_KMAP_BEGINFIX_KMAP_END;把后者的值赋成下标FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1。在这种方式下,系统中的每个CPU都有KM_TYPE_NR个固定映射的线性地址。

/*
 * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
 * no global lock is needed and because the kmap code must perform a global TLB
 * invalidation when the kmap pool wraps.
 *
 * However when holding an atomic kmap it is not legal to sleep, so atomic
 * kmaps are appropriate for short, tight code paths only.
 */
void *kmap_atomic_prot(struct page *page, pgprot_t prot)
{
	unsigned long vaddr;
	int idx, type;

	/* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
	pagefault_disable(); /*为了保证函数的原子性,禁止page fault handler*/  
	

	if (!PageHighMem(page))
		return page_address(page);

	type = kmap_atomic_idx_push();
<span style="white-space:pre">	/*smp_processor_id()得到CPU的标识号,用KM_TYPE_NR乘以该标识号就得到了该CPU可用窗口的区段,</span>
	idx = type + KM_TYPE_NR*smp_processor_id();  
	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);/*得到线性地址对应页表项的虚拟地址*/  
	BUG_ON(!pte_none(*(kmap_pte-idx)));
	set_pte(kmap_pte-idx, mk_pte(page, prot));/*将页表项与page进行关联,用kmap_pte-idx而不是用kmap_pte+idx,因为固定映射区是逆向生长的,即枚举项越靠前的部分的虚拟地址越靠后*/  
	arch_flush_lazy_mmu_mode();

	return (void *)vaddr;
}
EXPORT

  1. #define __fix_to_virt(x)    (FIXADDR_TOP - ((x) << PAGE_SHIFT))  
撤销临时映射

分析:

void __kunmap_atomic(void *kvaddr)
{
	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;

	if (vaddr >= __fix_to_virt(FIX_KMAP_END) &&
	    vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) {   //记住固定地址是逆向生长的
		int idx, type;

		type = kmap_atomic_idx();
		idx = type + KM_TYPE_NR * smp_processor_id();

#ifdef CONFIG_DEBUG_HIGHMEM
		WARN_ON_ONCE(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
#endif
		/*
		 * Force other mappings to Oops if they'll try to access this
		 * pte without first remap it.  Keeping stale mappings around
		 * is a bad idea also, in case the page changes cacheability
		 * attributes or becomes a protected page in a hypervisor.
		 */
		kpte_clear_flush(kmap_pte-idx, vaddr);  /*如果建立了映射则清除页表项的内容,并且淸刷相应的TLB项*/  
		kmap_atomic_idx_pop();
		arch_flush_lazy_mmu_mode();
	}
#ifdef CONFIG_DEBUG_HIGHMEM
	else {
		BUG_ON(vaddr < PAGE_OFFSET);
		BUG_ON(vaddr >= (unsigned long)high_memory);
	}
#endif

	pagefault_enable();<span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 13.3333px; line-height: 20px; background-color: rgb(248, 248, 248);"></span>/*重新使能page fault handler*/  <span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 13.3333px; line-height: 20px; background-color: rgb(248, 248, 248);"></span>

kunmap_atomic(),这个函数减少当前进程的preempt_count;因此,如果在请求临时内核映像之前能抢占内核控制路径,那么在同一个映像被撤销后可以再次抢占。??????


high memory 映射 ----2_第1张图片

linux内存布局;

来源于http://blog.csdn.net/bullbat/article/details/7179282 




你可能感兴趣的:(high memory 映射 ----2)