Linux高端内存映射(中)

来源:互联网 发布:帝国时代3中文版 mac 编辑:程序博客网 时间:2024/04/29 22:09

临时内核映射

       临时内核映射和永久内核映射相比,其最大的特点就是不会阻塞请求映射页框的进程,因此临时内核映射请求可以发生在中断和可延迟函数中。系统中的每个CPU都有自己的临时内核映射窗口,根据不同的需求,选择不同的窗口来创建映射,这些窗口都以枚举类型定义在km_type中

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


其中KM_TYPE_NR标志了一个CPU可以拥有多少个页表项窗口来建立映射。

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

 

[cpp] view plaincopy
  1. void *kmap_atomic(struct page *page, enum km_type type)  
  2. {  
  3.     return kmap_atomic_prot(page, type, kmap_prot);  
  4. }  


 

[cpp] view plaincopy
  1. /* 
  2.  * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because 
  3.  * no global lock is needed and because the kmap code must perform a global TLB 
  4.  * invalidation when the kmap pool wraps. 
  5.  * 
  6.  * However when holding an atomic kmap it is not legal to sleep, so atomic 
  7.  * kmaps are appropriate for short, tight code paths only. 
  8.  */  
  9. void *kmap_atomic_prot(struct page *page, enum km_type type, pgprot_t prot)  
  10. {  
  11.     enum fixed_addresses idx;  
  12.     unsigned long vaddr;  
  13.   
  14.     /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */  
  15.     /*为了保证函数的原子性,禁止page fault handler*/  
  16.     pagefault_disable();  
  17.   
  18.     if (!PageHighMem(page))/*属于低端内存则直接返回page的线性地址*/  
  19.         return page_address(page);  
  20.   
  21.     debug_kmap_atomic(type);  
  22.   
  23.     /*smp_processor_id()得到CPU的标识号,用KM_TYPE_NR乘以该标识号就得到了该CPU可用窗口的区段, 
  24.     再加上type就得到了相应的属于该CPU的窗口*/  
  25.     idx = type + KM_TYPE_NR*smp_processor_id();  
  26.     vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);/*得到对应页表项的虚拟地址*/  
  27.     BUG_ON(!pte_none(*(kmap_pte-idx)));  
  28.     /*将页表项与page进行关联,用kmap_pte-idx而不是用kmap_pte+idx,因为固定映射区是逆向生长的, 
  29.     也就是说枚举项越靠前的部分的虚拟地址越靠后*/  
  30.     set_pte(kmap_pte-idx, mk_pte(page, prot));  
  31.   
  32.     return (void *)vaddr;  
  33. }  


 


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

[cpp] view plaincopy
  1. void kunmap_atomic(void *kvaddr, enum km_type type)  
  2. {  
  3.     unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;  
  4.     enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();  
  5.   
  6.     /* 
  7.      * Force other mappings to Oops if they'll try to access this pte 
  8.      * without first remap it.  Keeping stale mappings around is a bad idea 
  9.      * also, in case the page changes cacheability attributes or becomes 
  10.      * a protected page in a hypervisor. 
  11.      */  
  12.      /*如果建立了映射则清除页表项的内容,并且淸刷相应的TLB项*/  
  13.     if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx))  
  14.         kpte_clear_flush(kmap_pte-idx, vaddr);  
  15.     else {  
  16. #ifdef CONFIG_DEBUG_HIGHMEM  
  17.         BUG_ON(vaddr < PAGE_OFFSET);  
  18.         BUG_ON(vaddr >= (unsigned long)high_memory);  
  19. #endif  
  20.     }  
  21.   
  22.     pagefault_enable();/*重新使能page fault handler*/  
  23. }  
Linux高端内存映射(中)
0 0
原创粉丝点击