zoukankan      html  css  js  c++  java
  • 映射内存高端内存永久映射分析

    每日一贴,今天的内容关键字为映射内存

        chipset: MSM8X25Q

        Codebase: Android4.1

        Kernel: 3.4.0

        

    基本概念:

             当你需要将高端页面期长映射到内核间空的时候,就要应用Kmap数函来现实,即高端内存永久映射。这样免避页表和TLB的更新而致导资源的占用。

             应用的时候一般先通过alloc_page(__GFP_HIGHMEM)请申一个page,然后将这个page传给kmap,kmap会建立这个page的页表项,并回返一个虚拟地址供操纵。

             Pkmap_count是一个长度为LAST_PKMAP的数组,长度为512,,每一个元素对应一个永久映射的页,所以可以映射512*4k = 2M页巨细。

    static int pkmap_count[LAST_PKMAP];
    #define LAST_PKMAP		PTRS_PER_PTE
    #define PTRS_PER_PTE		512

        Pkmap_count元素的值不同时的义意对应如下:

        0:相干也还没应用

        1:示表页已被映射,但是由于TLB没被更新而法无应用。

        >=2:为2时,示表内核有一处应用该映射页。为n时,示表有n-1处应用该页。

        另外,内核应用struct page_address_map来存保页和虚拟地址之前的系关。

    struct page_address_map {
    	struct page *page;
    	void *virtual;
    	struct list_head list;
    };

        另外,高端内存永久映射是通过page_address_htable这个变量来管理的,结构为:

    static struct page_address_slot {
    	struct list_head lh;			/* List of page_address_maps */
    	spinlock_t lock;			/* Protect this bucket's list */
    }

        它的管理机制应用了哈希表,对应的数函是page_slot(),对于哈希道理,可自行查资料学习。

    static struct page_address_slot *page_slot(const struct page *page)
    {
    	return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
    }

        

    初始化:

        统系机开启动的时候有如下用调:

    Start_kernel() –> setup_arch() -> paging_init() -> kmap_init()
    static void __init kmap_init(void)
    {
    #ifdef CONFIG_HIGHMEM
    	/*该变量存保了PKMAP_BASE对应的页表项地址,PKMAP_BASE为永久映射的肇端地址。*/
    	pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE),
    		PKMAP_BASE, _PAGE_KERNEL_TABLE);
    #endif
    }

        

    建创映射:

             用调是kmap().

    void *kmap(struct page *page)
    {
    	/*会sleep,所以不能于用中断上下文*/
    	might_sleep();
    	/*如果是低端内存,那么回返page对应虚拟地址*/
    	if (!PageHighMem(page))
    		return page_address(page);
    	/*否则执行高端内存映射*/
    	return kmap_high(page);
    }
    EXPORT_SYMBOL(kmap);

        

    page_address():

    void *page_address(const struct page *page)
    {
    	unsigned long flags;
    	void *ret;
    	struct page_address_slot *pas;
    	/*又一次判断是低端还是高端内存,因为此数函开放给用调者用调。*/
    	if (!PageHighMem(page))
    	/*低端内存就按牢固偏移回返虚拟地址*/
    		return lowmem_page_address(page);
    	/*高端内存就从哈希表中查找回返。*/
    	pas = page_slot(page);
    	ret = NULL;
    	spin_lock_irqsave(&pas->lock, flags);
    	if (!list_empty(&pas->lh)) {
    		struct page_address_map *pam;
    			/*页应用lh链表来管理。*/
    		list_for_each_entry(pam, &pas->lh, list) {
    			if (pam->page == page) {
    				ret = pam->virtual;
    				goto done;
    			}
    		}
    	}
    done:
    	spin_unlock_irqrestore(&pas->lock, flags);
    	return ret;
    }

        

    kmap_high():

        每日一道理
    青春是用意志的血滴和拼搏的汗水酿成的琼浆——历久弥香;青春是用不凋的希望和不灭的向往编织的彩虹——绚丽辉煌;青春是用永恒的执著和顽强的韧劲筑起的一道铜墙铁壁——固若金汤。
    void *kmap_high(struct page *page)
    {
    	unsigned long vaddr;
    
    	/*
    	 * For highmem pages, we can't trust "virtual" until
    	 * after we have the lock.
    	 */
    	lock_kmap();
    	/*先判断否是已被映射过了*/
    	vaddr = (unsigned long)page_address(page);
    	/*没有就新建创页表项*/
    	if (!vaddr)
    		vaddr = map_new_virtual(page);
    	/*前当页对应的元素值加1.  PKMAP_NR示表相对PKMAP_BASE的偏移。
    #define PKMAP_NR(virt)		(((virt) - PKMAP_BASE) >> PAGE_SHIFT) */
    	pkmap_count[PKMAP_NR(vaddr)]++;
    	/*<2确定不正常了,因为1示表建创成完,如果跑到这里,2就示表有块模应用此映射了。*/
    	BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
    	unlock_kmap();
    	/*回返映射以后的虚拟地址*/
    	return (void*) vaddr;
    }

        

    map_new_virtual():

    static inline unsigned long map_new_virtual(struct page *page)
    {
    	unsigned long vaddr;
    	int count;
    
    start:
    	count = LAST_PKMAP;
    	/* Find an empty entry */
    	for (;;) {
    		/* last_pkmap_nr 录记前当映射的页数,也可以认为是最后应用的位置。*/
    		last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK;
    		/*为0示表查找完一轮了,这时它会去刷新pkmap_count的值为1的TLB。*/
    		if (!last_pkmap_nr) {
    			flush_all_zero_pkmaps();
    			count = LAST_PKMAP;
    		}
    		/*找到闲空区了*/
    		if (!pkmap_count[last_pkmap_nr])
    			break;	/* Found a usable entry */
    		/*未找到,继承找下一个*/
    		if (--count)
    			continue;
    		/*找了LAST_PKMAP 个以后就就寝,待等其他块模unmap*/
    		{
    			DECLARE_WAITQUEUE(wait, current);
    			__set_current_state(TASK_UNINTERRUPTIBLE);
    			add_wait_queue(&pkmap_map_wait, &wait);
    			unlock_kmap();
    			schedule();
    			remove_wait_queue(&pkmap_map_wait, &wait);
    			lock_kmap();
    			/*醒来以后看看是不是有其他进程已做了映射了,如果是,
    就直接回返。*/
    			/* Somebody else might have mapped it while we slept */
    			if (page_address(page))
    				return (unsigned long)page_address(page);
    			/*没有就再去重新新一轮查找*/
    			/* Re-start */
    			goto start;
    		}
    	}
    	/*从这里看到,永久映射的虚拟地址是继承PKMAP_BASE加上一个offset现实的。*/
    	vaddr = PKMAP_ADDR(last_pkmap_nr);
    	/*修改内核页表,将该页与页表行进联关,但还未更新TLB。*/
    	set_pte_at(&init_mm, vaddr,
    		   &(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));
    	/*示表建创映射页表成完*/
    	pkmap_count[last_pkmap_nr] = 1;
    	/*将vaddr加入到pkmap_page_table 哈希表和struct page_address_map中管理以供面后用调page_address.*/
    	set_page_address(page, (void *)vaddr);
    
    	return vaddr;
    }

        

    flush_all_zero_pkmaps():

    static void flush_all_zero_pkmaps(void)
    {
    	int i;
    	int need_flush = 0;
    
    	flush_cache_kmaps();
    
    	for (i = 0; i < LAST_PKMAP; i++) {
    		struct page *page;
    
    /*0的时候还没建创映射,用不管。
    >=2的时候示表还有块模在应用,也不处置。
    1示表已没有块模应用了,即示表已unmap了,但是页表还没放释,
    这里就是针对页行进放释。*/
    		if (pkmap_count[i] != 1)
    			continue;
    		pkmap_count[i] = 0;
    		page = pte_page(pkmap_page_table[i]);
    		pte_clear(&init_mm, (unsigned long)page_address(page),
    			  &pkmap_page_table[i]);
    
    		set_page_address(page, NULL);
    		need_flush = 1;
    	}
    	/*有转变,刷新永久映射区全体TLB。*/
    	if (need_flush)
    		flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
    }

        20130422

    文章结束给大家分享下程序员的一些笑话语录: 雅虎最擅长的不是开通新业务,是关闭旧业务。

  • 相关阅读:
    from import 的认识
    模块初识
    eq方法
    hash介绍
    item系列
    析构函数
    serializers进阶
    APIView源码解析
    RPC协议
    面试题补充
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3041118.html
Copyright © 2011-2022 走看看