zoukankan      html  css  js  c++  java
  • 高端内存映射之kmap_atomic固定映射--Linux内存管理(二十一)

    1 固定映射

    1.1 数据结构

    linux高端内存中的临时内存区为固定内存区的一部分, 对于固定内存在linux内核中有下面描述

    x86 arm arm64
    arch/x86/include/asm/fixmap.h?v=4.7, line 67 arch/arm/include/asm/fixmap.h?v=4.7, line 11 arch/arm64/include/asm/fixmap.h?v=4.7, line 36
    /*
     * Here we define all the compile-time 'special' virtual
     * addresses. The point is to have a constant address at
     * compile time, but to set the physical address only
     * in the boot process.
     *
     * These 'compile-time allocated' memory buffers are
     * page-sized. Use set_fixmap(idx,phys) to associate
     * physical memory with fixmap indices.
     *
     */
    enum fixed_addresses {
        FIX_HOLE,
    
        /*
         * Reserve a virtual window for the FDT that is 2 MB larger than the
         * maximum supported size, and put it at the top of the fixmap region.
         * The additional space ensures that any FDT that does not exceed
         * MAX_FDT_SIZE can be mapped regardless of whether it crosses any
         * 2 MB alignment boundaries.
         *
         * Keep this at the top so it remains 2 MB aligned.
         */
    #define FIX_FDT_SIZE        (MAX_FDT_SIZE + SZ_2M)
        FIX_FDT_END,
        FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,
    
        FIX_EARLYCON_MEM_BASE,
        FIX_TEXT_POKE0,
        __end_of_permanent_fixed_addresses,
    
        /*
         * Temporary boot-time mappings, used by early_ioremap(),
         * before ioremap() is functional.
         */
    #define NR_FIX_BTMAPS       (SZ_256K / PAGE_SIZE)
    #define FIX_BTMAPS_SLOTS    7
    #define TOTAL_FIX_BTMAPS    (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
    
        FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
        FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
    
        /*
         * Used for kernel page table creation, so unmapped memory may be used
         * for tables.
         */
        FIX_PTE,
        FIX_PMD,
        FIX_PUD,
        FIX_PGD,
    
        __end_of_fixed_addresses
    };
    

    1.2 固定映射

    ioremap的作用是将IOBIOS以及物理地址空间映射到在896M至1G的128M的地址空间内, 使得kernel能够访问该空间并进行相应的读写操作。

    start_kernel()->setup_arch()->early_ioremap_init()
    

    然后arm和arm64上early_ioremap_init又是early_ioremap_setup的前端

    函数 x86 arm arm64
    early_ioremap_init arch/x86/mm/ioremap.c?v=4.7, line 445 arch/arm/mm/ioremap.c?v=4.7, line 489 arch/arm64/mm/ioremap.c?v=4.7, line 110
    early_ioremap_setup mm/early_ioremap.c?v=4.7, line 67 体系结构无关 体系结构无关
    /*
     * Must be called after early_fixmap_init
     */
    void __init early_ioremap_init(void)
    {
        early_ioremap_setup();
    }
    

    但是arm和arm64下的setup_arch函数则会先调用early_fixmap_init函数来填充fixmap. 参见arch/arm/kernel/setup.c?v=4.7, line 1058arch/arm64/kernel/setup.c?v=4.7, line 229.

    void __init setup_arch(char **cmdline_p)
    {
        early_fixmap_init();
        early_ioremap_init();
    }
    

    early_fixmap_init函数的定义在

    arm arm64
    arch/arm/mm/mmu.c?v=4.7, line 385 arch/arm64/mm/mmu.c?v=4.7, line 676

    其中arm架构的定义如下所示, 在arch/arm/mm/mmu.c?v=4.7, line 385

    void __init early_fixmap_init(void)
    {
        pmd_t *pmd;
    
        /*
         * The early fixmap range spans multiple pmds, for which
         * we are not prepared:
         */
        BUILD_BUG_ON((__fix_to_virt(__end_of_early_ioremap_region) >> PMD_SHIFT)
                 != FIXADDR_TOP >> PMD_SHIFT);
    
        /*得到固定映射区的pmd
        ,此pmd为虚拟地址转换为物理地址的pmd*/
        pmd = fixmap_pmd(FIXADDR_TOP);
         /*将bm_pte页表设置为固定映射区开始地址的pmd的第一个页表;*/
        pmd_populate_kernel(&init_mm, pmd, bm_pte);
    
        pte_offset_fixmap = pte_offset_early_fixmap;
    }
    

    1.3 ioremap函数

    对于ioremap的使用需要通过early_memremapearly_iounmap进行.

    由于对应于ioremap的内存空间是有限的, 所以对于ioremap空间的使用遵照使用结束马上释放的原则. 这就是说early_memremap和early_iounmap必须配对使用并且访问结束必须马上执行unmap

    2 临时内核映射

    刚才描述的kmap函数不能用于中断处理程序, 因为它可能进入睡眠状态. 如果pkmap数组中没有空闲位置, 该函数会进入睡眠状态, 直至情形有所改善.

    因此内核提供了一个备选的映射函数, 其执行是原子的, 逻辑上称为kmap_atomic. 该函数的一个主要优点是它比普通的kmap快速. 但它不能用于可能进入睡眠的代码. 因此, 它对于很快就需要一个临时页的简短代码,是非常理想的.

    kmap_atomic的定义在IA-32, PPC, Sparc32上是特定于体系结构的, 但这3种实现只有非常细微的差别. 其原型是相同的.

    2.1 kmap_atomic函数

    //  http://lxr.free-electrons.com/source/arch/arm/mm/highmem.c?v=4.7#L55
    void *kmap_atomic(struct page *page)
    

    page是一个指向高端内存页的管理结构的指针, 而早期的内核中, 增加了一个类型为enum km_typetype参数, 用于指定所需的映射类型

    //  http://lxr.free-electrons.com/source/arch/arm/mm/highmem.c?v=2.6.32#L39
    void *kmap_atomic(struct page *page, enum km_type type)
    

    而在新的内核中, 删除了这个标识, 但是保留了km_type的最大值KM_TYPE_NR

    void *kmap_atomic(struct page *page)
    {
        unsigned int idx;
        unsigned long vaddr;
        void *kmap;
        int type;
    
        preempt_disable();
        pagefault_disable();
        if (!PageHighMem(page))
            return page_address(page);
    
    #ifdef CONFIG_DEBUG_HIGHMEM
        /*
         * There is no cache coherency issue when non VIVT, so force the
         * dedicated kmap usage for better debugging purposes in that case.
         */
        if (!cache_is_vivt())
            kmap = NULL;
        else
    #endif
            kmap = kmap_high_get(page);
        if (kmap)
            return kmap;
    
        type = kmap_atomic_idx_push();
    
        idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
        vaddr = __fix_to_virt(idx);
    #ifdef CONFIG_DEBUG_HIGHMEM
        /*
         * With debugging enabled, kunmap_atomic forces that entry to 0.
         * Make sure it was indeed properly unmapped.
         */
        BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
    #endif
        /*
         * When debugging is off, kunmap_atomic leaves the previous mapping
         * in place, so the contained TLB flush ensures the TLB is updated
         * with the new mapping.
         */
        set_fixmap_pte(idx, mk_pte(page, kmap_prot));
    
        return (void *)vaddr;
    }
    EXPORT_SYMBOL(kmap_atomic);
    

    这个函数不会被阻塞, 因此可以用在中断上下文和起亚不能重新调度的地方. 它也禁止内核抢占, 这是有必要的, 因此映射对每个处理器都是唯一的(调度可能对哪个处理器执行哪个进程做变动).

    2.2 kunmap_atomic函数

    可以通过函数kunmap_atomic取消映射

    /*
     * Prevent people trying to call kunmap_atomic() as if it were kunmap()
     * kunmap_atomic() should get the return value of kmap_atomic, not the page.
     */
    #define kunmap_atomic(addr)                     
    do {                                
        BUILD_BUG_ON(__same_type((addr), struct page *));       
        __kunmap_atomic(addr);                  
    } while (0)
    

    这个函数也不会阻塞. 在很多体系结构中, 除非激活了内核抢占, 否则kunmap_atomic根本无事可做, 因为只有在下一个临时映射到来前上一个临时映射才有效. 因此, 内核完全可以”忘掉”kmap_atomic映射, kunmap_atomic也无需做什么实际的事情. 下一个原子映射将自动覆盖前一个映射.

  • 相关阅读:
    笔试题 输出金字塔 面试经典
    C++ 函数, 虚函数, 纯虚函数
    EJB 根据beanName引用EJB
    【J2EE性能分析篇】JVM参数对J2EE性能优化的影响【转】
    C++ 引用和指针作为函数参数的例子。请不要拍砖
    lucene 总结
    二维数组按列序号排序 面试经典
    http://www.linuxidc.com/Linux/201004/25494.htm
    银行取款费用
    PHP 生成 csv 文件时乱码解决
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/10206300.html
Copyright © 2011-2022 走看看