Linux高端内存映射(中)

临时内核映射

       临时内核映射和永久内核映射相比,其最大的特点就是不会阻塞请求映射页框的进程,因此临时内核映射请求可以发生在中断和可延迟函数中。系统中的每个CPU都有自己的临时内核映射窗口,根据不同的需求,选择不同的窗口来创建映射,这些窗口都以枚举类型定义在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_NR标志了一个CPU可以拥有多少个页表项窗口来建立映射。

       临时内核映射的实现也比永久内核映射要简单,当一个进程申请在某个窗口创建映射,即使这个窗口已经在之前就建立了映射,新的映射也会建立并且覆盖之前的映射,所以说这种映射机制是临时的,并且不会阻塞当前进程。

 

void *kmap_atomic(struct page *page, enum km_type type)
{
	return kmap_atomic_prot(page, type, kmap_prot);
}


 

/*
 * 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, enum km_type type, pgprot_t prot)
{
	enum fixed_addresses idx;
	unsigned long vaddr;

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

	if (!PageHighMem(page))/*属于低端内存则直接返回page的线性地址*/
		return page_address(page);

	debug_kmap_atomic(type);

	/*smp_processor_id()得到CPU的标识号,用KM_TYPE_NR乘以该标识号就得到了该CPU可用窗口的区段,
	再加上type就得到了相应的属于该CPU的窗口*/
	idx = type + KM_TYPE_NR*smp_processor_id();
	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);/*得到对应页表项的虚拟地址*/
	BUG_ON(!pte_none(*(kmap_pte-idx)));
	/*将页表项与page进行关联,用kmap_pte-idx而不是用kmap_pte+idx,因为固定映射区是逆向生长的,
	也就是说枚举项越靠前的部分的虚拟地址越靠后*/
	set_pte(kmap_pte-idx, mk_pte(page, prot));

	return (void *)vaddr;
}


 


若要手动撤销当前的临时内核映射,则可调用kunmap_atomic()函数

void kunmap_atomic(void *kvaddr, enum km_type type)
{
	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
	enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();

	/*
	 * 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.
	 */
	 /*如果建立了映射则清除页表项的内容,并且淸刷相应的TLB项*/
	if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx))
		kpte_clear_flush(kmap_pte-idx, vaddr);
	else {
#ifdef CONFIG_DEBUG_HIGHMEM
		BUG_ON(vaddr < PAGE_OFFSET);
		BUG_ON(vaddr >= (unsigned long)high_memory);
#endif
	}

	pagefault_enable();/*重新使能page fault handler*/
}


 

你可能感兴趣的:(Linux高端内存映射(中))