听老歌 发表于 2012-01-05 19:03

linux高端内存管理之临时内核映射

linux高端内存管理之临时内核映射








临时内核映射区属于高端内存中的固定内核映射区中的一部分。当必须创建一个映射而当前的上下文又不能睡眠时,内核提供了临时映射(也就是所谓的原子映射)。有一组保留的映射,他们可以存放新创建的临时映射。内核可以原子地把高端内存中的一个页映射到某个保留的映射中。因此,临时映射可以用在不能睡眠的地方,比如中断处理程序中,因为获取映射时绝不会阻塞。


每个CPU都有他自己的窗口集合,他们用enum km_type数据结构表示。该数据结构中定义的每个符号,如KM_BOUNCE_READ、KM_USER0等标示了窗口的线性地址。view plaincopy to clipboardprint?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
};
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_BEGIN和FIX_KMAP_END;把后者的值赋成下标FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1。在这种方式下,系统中的每个CPU都有KM_TYPE_NR个固定映射的线性地址。

view plaincopy to clipboardprint?enum fixed_addresses {
   ……
FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
   ……
}
enum fixed_addresses {
   ……
FIX_KMAP_BEGIN,        /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
   ……
} 临时内核映射的建立

内核调用kmap_atomic()函数view plaincopy to clipboardprint?用数学公式来避免混乱,他空间有限且虚拟地址固定,这意味着他映射的内存空间不能被
长时间占用,而不被unmap,在效率上比kmap提升不少,然而他和kmap不是用于统一
场合的,
**/
void *kmap_atomic(struct page *page, enum km_type type)
{
    return kmap_atomic_prot(page, type, kmap_prot);
}
用数学公式来避免混乱,他空间有限且虚拟地址固定,这意味着他映射的内存空间不能被
长时间占用,而不被unmap,在效率上比kmap提升不少,然而他和kmap不是用于统一
场合的,
**/
void *kmap_atomic(struct page *page, enum km_type type)
{
        return kmap_atomic_prot(page, type, kmap_prot);
}view plaincopy to clipboardprint?/*
* 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 */
    /**原子映射是基于每个cpu的,因此在当前cpu上应用抢占,直到unmap的时候才
    开启,这样不会导致原子映射的重入了,
    */
    pagefault_disable();

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

    /* 递增type,保证下面公式起作用 */
    debug_kmap_atomic(type);
    /*
    kernel可以在多个cpu上同时运行不同的task,然而他们共同使用一个内存地址空间,
    也就是说,内存空间对于多个cpu看到的是同一个,该函数使用的是地址空间中顶部的
    一小段地址空间,也就是临时映射区,内核逻辑将这一小段地址空间分成若干各节
    每一节的大小是一个页面的大小,可以映射一个页面,根据公用地址空间的原理
    所有的cpu共同使用这些节,因此如何能保证N个cpu调用此函数不会将page映射到   一个地址呢,这就是这个数学公式所起到的作用
    */
    idx = type + KM_TYPE_NR*smp_processor_id();
    vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
    /*这里为什么是减法
    越靠前的枚举项对应的线性地址越靠后*/
    BUG_ON(!pte_none(*(kmap_pte-idx)));
    /*设置pte*/
    set_pte(kmap_pte-idx, mk_pte(page, prot));

    return (void *)vaddr;
}
/*
* 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 */
        /**原子映射是基于每个cpu的,因此在当前cpu上应用抢占,直到unmap的时候才
        开启,这样不会导致原子映射的重入了,
        */
        pagefault_disable();

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

        /* 递增type,保证下面公式起作用 */
        debug_kmap_atomic(type);
        /*
        kernel可以在多个cpu上同时运行不同的task,然而他们共同使用一个内存地址空间,
        也就是说,内存空间对于多个cpu看到的是同一个,该函数使用的是地址空间中顶部的
        一小段地址空间,也就是临时映射区,内核逻辑将这一小段地址空间分成若干各节
        每一节的大小是一个页面的大小,可以映射一个页面,根据公用地址空间的原理
        所有的cpu共同使用这些节,因此如何能保证N个cpu调用此函数不会将page映射到        一个地址呢,这就是这个数学公式所起到的作用
        */
        idx = type + KM_TYPE_NR*smp_processor_id();
        vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
        /*这里为什么是减法
        越靠前的枚举项对应的线性地址越靠后*/
        BUG_ON(!pte_none(*(kmap_pte-idx)));
        /*设置pte*/
        set_pte(kmap_pte-idx, mk_pte(page, prot));

        return (void *)vaddr;
}view plaincopy to clipboardprint?#define __fix_to_virt(x)    (FIXADDR_TOP - ((x) << PAGE_SHIFT))
#define __fix_to_virt(x)        (FIXADDR_TOP - ((x) << PAGE_SHIFT)) 撤销临时映射

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

蓝猫淘气啦啦 发表于 2012-01-05 19:04

谢谢分享
页: [1]
查看完整版本: linux高端内存管理之临时内核映射