2014년 10월 25일 토요일

[Linux Kernel] 75주차(2014.10.25)

ARM10C 75주차 후기

일시 : 2014.10.25 (75주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 4명

진도

  • init_IRQ();
    • gic_of_init()

기술

  • RedBlack Tree
  • get_vm_area_caller이 한일:
// alloc area (GIC) 를 만들고 rb tree에 alloc area 를 추가
// 가상주소 va_start 기준으로 GIC 를 RB Tree 추가한 결과
//
//                                  CHID-b
//                               (0xF8000000)
//                              /            \
//                         TMR-r               PMU-r
//                    (0xF6300000)             (0xF8180000)
//                      /      \               /           \
//                 SYSC-b      WDT-b         CMU-b         SRAM-b
//            (0xF6100000)   (0xF6400000)  (0xF8100000)   (0xF8400000)
//             /                                                 \
//        GIC-r                                                   ROMC-r
//   (0xF0000000)                                                 (0xF84C0000)
//
// (kmem_cache#30-oX (vm_struct))->flags: GFP_KERNEL: 0xD0
// (kmem_cache#30-oX (vm_struct))->addr: 0xf0000000
// (kmem_cache#30-oX (vm_struct))->size: 0x2000
// (kmem_cache#30-oX (vm_struct))->caller: __builtin_return_address(0)
//
// (kmem_cache#30-oX (vmap_area GIC))->vm: kmem_cache#30-oX (vm_struct)
// (kmem_cache#30-oX (vmap_area GIC))->flags: 0x04

main.c::start_kernel()

  • start_kernel()->init_IRQ()
asmlinkage void __init start_kernel(void)
{

...

    boot_cpu_init();
    // 현재 cpu(core id)를 얻어서 cpu_XXX_bits[] 의 cpu를 셋한다.

...

    setup_arch(&command_line);

...

    mm_init();
    // buddy와 slab 을 활성화 하고 기존 할당 받은 bootmem 은 buddy,
    // pcpu 메모리, vmlist 는 slab으로 이관

    sched_init();
    // scheduler가 사용하는 자료 구조 초기화, idle_threads를 init_task로 세팅

    preempt_disable();
    // preempt count를 증가시켜 preemption 못하도록 막음

...

    rcu_init();
    // rcu 자료구조 bh, sched, preempt 를 각각 초기화 수행함

...

    radix_tree_init();
    // radix tree로 사용하는 radix_tree_node_cachep에 kmem_cache#20을 생성 및 초기화 후 할당하고
    // height_to_maxindex을 초기화 수행

    /* init some links before init_ISA_irqs() */
    early_irq_init();
    // irq_desc 0 ~ 15 까지의 object을 할당 받고 초기화를 수행
    // allocated_irqs에 bit를 1로 세팅하고 radix tree에 각 irq_desc를 노트로 추가

    init_IRQ();

지난시간에 이어 init_IRQ() 을 계속 분석 진행합니다. 

irq.c::init_IRQ()

  • start_kernel()->init_IRQ()->irqchip_init()
// ARM10C 20141004
void __init init_IRQ(void)
{
    // CONFIG_OF=y, machine_desc->init_irq: __mach_desc_EXYNOS5_DT.init_irq: 0
    if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
        irqchip_init();
    else
        machine_desc->init_irq();
}

irqchip.h::irqchip_init()

  • start_kernel()->init_IRQ()->irqchip_init()->of_irq_init()
// ARM10C 20141004
void __init irqchip_init(void)
{
    // exynos-combiner.c 에 정의된 함수를 사용하여 초기화 수행
    // __irqchip_begin: irqchip_of_match_exynos4210_combiner
    of_irq_init(__irqchip_begin);
}

irqchip.h::irqchip_init()

  • start_kernel()->init_IRQ()->irqchip_init()
  • ARM10C 20141004
void __init of_irq_init(const struct of_device_id *matches)
{
    struct device_node *np, *parent = NULL;
    // parent: NULL
    struct intc_desc *desc, *temp_desc;
    struct list_head intc_desc_list, intc_parent_list;

    INIT_LIST_HEAD(&intc_desc_list);
    // intc_desc_list 리스트 초기화 수행

    INIT_LIST_HEAD(&intc_parent_list);
    // intc_parent_list 리스트 초기화 수행

    // matches: irqchip_of_match_exynos4210_combiner
    for_each_matching_node(np, matches) {
    // for (np = of_find_matching_node(NULL, matches); np; np = of_find_matching_node(np, matches))

        // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // of_find_property(devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, "interrupt-controller", NULL):
        // combiner node의 "interrupt-controller" property의 주소
        // np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        // of_find_property(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, "interrupt-controller", NULL):
        // gic node의 "interrupt-controller" property의 주소
        if (!of_find_property(np, "interrupt-controller", NULL))
            continue;
        /*
         * Here, we allocate and populate an intc_desc with the node
         * pointer, interrupt-parent device_node etc.
         */
        // sizeof(struct intc_desc): 16 bytes, GFP_KERNEL: 0xD0
        // kzalloc(16, GFP_KERNEL: 0xD0): kmem_cache#30-o10
        // sizeof(struct intc_desc): 16 bytes, GFP_KERNEL: 0xD0
        // kzalloc(16, GFP_KERNEL: 0xD0): kmem_cache#30-o11
        desc = kzalloc(sizeof(*desc), GFP_KERNEL);
        // desc: kmem_cache#30-o10
        // desc: kmem_cache#30-o11

        // desc: kmem_cache#30-o10
        // desc: kmem_cache#30-o11
        if (WARN_ON(!desc))
            goto err;

        // desc->dev: (kmem_cache#30-o10)->dev, np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // desc->dev: (kmem_cache#30-o11)->dev, np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        desc->dev = np;
        // desc->dev: (kmem_cache#30-o10)->dev: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소

// 2014/10/04 종료
// 2014/10/11 시작

        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent, np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // of_irq_find_parent(devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소): gic node 주소
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent, np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        // of_irq_find_parent(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소): gic node 주소
        desc->interrupt_parent = of_irq_find_parent(np);
        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: gic node 주소
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: gic node 주소

        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: gic node 주소
        // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: gic node 주소
        // np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        if (desc->interrupt_parent == np)
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: gic node 주소
            desc->interrupt_parent = NULL;
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL

        // &desc->list: &(kmem_cache#30-o10)->list
        // &desc->list: &(kmem_cache#30-o11)->list
        list_add_tail(&desc->list, &intc_desc_list);
        // intc_desc_list에 (kmem_cache#30-o10)->list를 tail에 추가
        // intc_desc_list에 (kmem_cache#30-o11)->list를 tail에 추가
    }

    // irqchip_of_match_exynos4210_combiner, irqchip_of_match_cortex_a15_gic 의
    // struct intc_desc 메모리 할당, intc_desc 맴버가 초기화 된 값이 intc_desc_list list의 tail로 추가됨

    /*
     * The root irq controller is the one without an interrupt-parent.
     * That one goes first, followed by the controllers that reference it,
     * followed by the ones that reference the 2nd level controllers, etc.
     */
    // list_empty(&intc_desc_list): 0
    while (!list_empty(&intc_desc_list)) {
        /*
         * Process all controllers with the current 'parent'.
         * First pass will be looking for NULL as the parent.
         * The assumption is that NULL parent means a root controller.
         */
        list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
        // for (desc = list_first_entry(&intc_desc_list, typeof(*desc), list),
        //  temp_desc = list_next_entry(desc, list);
        //      &desc->list != (&intc_desc_list);
        //      desc = temp_desc, temp_desc = list_next_entry(temp_desc, list))

            // desc: kmem_cache#30-o10 (exynos4210_combiner), temp_desc: kmem_cache#30-o11 (cortex_a15_gic)
            // desc: kmem_cache#30-o11 (cortex_a15_gic), temp_desc: NULL

            const struct of_device_id *match;
            int ret;
            of_irq_init_cb_t irq_init_cb;

            // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: gic node 주소, parent: NULL
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL, parent: NULL
            if (desc->interrupt_parent != parent)
                continue;
                // continue 수행 (exynos4210_combiner)

            // &desc->list: (kmem_cache#30-o11)->list
            list_del(&desc->list);
            // intc_desc_list에서 (kmem_cache#30-o11)->list를 삭제

            // matches: irqchip_of_match_cortex_a15_gic,
            // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
            // of_match_node(cortex_a15_gic, devtree에서 allnext로 순회 하면서 찾은 gic node의 주소):
            // irqchip_of_match_cortex_a15_gic
            match = of_match_node(matches, desc->dev);
            // match: irqchip_of_match_cortex_a15_gic

            // match->data; irqchip_of_match_cortex_a15_gic.data: gic_of_init
            if (WARN(!match->data,
                "of_irq_init: no init function for %s\n",
                match->compatible)) {
                kfree(desc);
                continue;
            }

            // match->compatible: irqchip_of_match_cortex_a15_gic.compatible: "arm,cortex-a15-gic",
            // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
            pr_debug("of_irq_init: init %s @ %p, parent %p\n",
                 match->compatible,
                 desc->dev, desc->interrupt_parent);
            // "of_irq_init: init arm,cortex-a15-gic @ 0x(gic node의 주소), parent 0\n"

// 2014/10/11 종료
// 2014/10/18 시작

            // match->data; irqchip_of_match_cortex_a15_gic.data: gic_of_init
            irq_init_cb = (of_irq_init_cb_t)match->data;
            // irq_init_cb: gic_of_init

            // desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소,
            // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
            // gic_of_init(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, NULL):
            ret = irq_init_cb(desc->dev, desc->interrupt_parent);
            if (ret) {
                kfree(desc);
                continue;
            }

            /*
             * This one is now set up; add it to the parent list so
             * its children can get processed in a subsequent pass.
             */
            list_add_tail(&desc->list, &intc_parent_list);
        }

        /* Get the next pending parent that might have children */
        desc = list_first_entry_or_null(&intc_parent_list,
                        typeof(*desc), list);
        if (!desc) {
            pr_err("of_irq_init: children remain, but no parents\n");
            break;
        }
        list_del(&desc->list);
        parent = desc->dev;
        kfree(desc);
    }

    list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
err:
    list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
}
  • irq_init_cb = (of_irq_init_cb_t)match->data;
    • 여기서 irq_init_cb: gic_of_init 로 되어 gic_of_init()를 실행한다.
    • ret = irq_init_cb(desc->dev, desc->interrupt_parent);

irq-gic.c::gic_of_init()

  • ARM10C 20141018
  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
  • desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소,
  • desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
// ARM10C 20141018
// desc->dev: (kmem_cache#30-o11)->dev: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소,
// desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
    void __iomem *cpu_base;
    void __iomem *dist_base;
    u32 percpu_offset;
    int irq;

    // node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
    if (WARN_ON(!node))
        return -ENODEV;

    // node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
    dist_base = of_iomap(node, 0);
  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap();
    • dist_base = of_iomap(node, 0);

address.c::of_iomap()

  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap();
  • dist_base = of_iomap(node, 0);
// ARM10C 20141018
// node: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, 0
void __iomem *of_iomap(struct device_node *np, int index)
{
    struct resource res;

    // np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, index: 0
    // of_address_to_resource(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, 0, &res): 0
    if (of_address_to_resource(np, index, &res))
        return NULL;

    // of_address_to_resource에서 한일:
    // (&res)->start: 0x10481000
    // (&res)->end: 0x10481fff
    // (&res)->flags: IORESOURCE_MEM: 0x00000200
    // (&res)->name: "/interrupt-controller@10481000"

    // res.start: 0x10481000, resource_size(&res): 0x1000
    return ioremap(res.start, resource_size(&res));
}
EXPORT_SYMBOL(of_iomap);
  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap();

io.h::ioremap()

  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap();
  • return ioremap(res.start, resource_size(&res));
#define ioremap(cookie,size)        __arm_ioremap((cookie), (size), MT_DEVICE)
  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap()->__arm_ioremap();

ioremap.c::__arm_ioremap()

  • return ioremap(res.start, resource_size(&res));
// ARM10C 20141018
// res.start: 0x10481000, resource_size(&res): 0x1000, MT_DEVICE: 0
void __iomem *
__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)
{
    // phys_addr: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0
    return arch_ioremap_caller(phys_addr, size, mtype,
        __builtin_return_address(0));
}
EXPORT_SYMBOL(__arm_ioremap);
  • cookie: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0
#define ioremap(cookie,size)        __arm_ioremap((cookie), (size), MT_DEVICE)

ioremap.c::__arm_ioremap()

  • cookie: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0
// ARM10C 20141018
// res.start: 0x10481000, resource_size(&res): 0x1000, MT_DEVICE: 0
void __iomem *
__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)
{
    // phys_addr: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0
    return arch_ioremap_caller(phys_addr, size, mtype,
        __builtin_return_address(0));
}
EXPORT_SYMBOL(__arm_ioremap);
  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap()->__arm_ioremap()->__arch_ioremap_caller();

ioremap.c::__arm_ioremap()_caller()

  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap()->__arm_ioremap()->__arm_ioremap_caller();
// ARM10C 20141018
// phys_addr: 0x10481000, size: 0x1000, mtype: MT_DEVICE: 0, __builtin_return_address(0)
void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size,
    unsigned int mtype, void *caller)
{
    phys_addr_t last_addr;
    // phys_addr: 0x10481000, PAGE_MASK: 0xFFFFF000
    unsigned long offset = phys_addr & ~PAGE_MASK;
    // offset: 0

    // phys_addr: 0x10481000, __phys_to_pfn(0x10481000): 0x10481
    unsigned long pfn = __phys_to_pfn(phys_addr);
    // pfn: 0x10481

    /*
     * Don't allow wraparound or zero size
     */
    // phys_addr: 0x10481000, size: 0x1000
    last_addr = phys_addr + size - 1;
    // last_addr: 0x10481fff

    // size: 0x1000, last_addr: 0x10481fff, phys_addr: 0x10481000
    if (!size || last_addr < phys_addr)
        return NULL;

    // pfn: 0x10481, offset: 0, size: 0x1000, mtype: MT_DEVICE: 0, caller: __builtin_return_address(0)
    return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,
            caller);
}
  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap()->__arm_ioremap()->__arm_ioremap_caller()->__arm_ioremap_pfn_caller();

ioremap.c::__arm_ioremap_pfn_caller()

  • start_kernel()->init_IRQ()->irqchip_init()->irq_init_cb(gic_of_init);
    • gic_of_init()->of_iomap()->ioremap()->__arm_ioremap()->__arm_ioremap_caller()->__arm_ioremap_pfn_caller();
  • pfn: 0x10481, offset: 0, size: 0x1000, mtype: MT_DEVICE: 0, caller: __builtin_return_address(0)
// ARM10C 20141018
// pfn: 0x10481, offset: 0, size: 0x1000, mtype: MT_DEVICE: 0, caller: __builtin_return_address(0)
void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,
    unsigned long offset, size_t size, unsigned int mtype, void *caller)
{
    const struct mem_type *type;
    int err;
    unsigned long addr;
    struct vm_struct *area;
    // pfn: 0x10481, __pfn_to_phys(0x10481): 0x10481000
    phys_addr_t paddr = __pfn_to_phys(pfn);
    // paddr: 0x10481000

#ifndef CONFIG_ARM_LPAE // CONFIG_ARM_LPAE=n
    /*
     * High mappings must be supersection aligned
     */
    // pfn: 0x10481, paddr: 0x10481000, SUPERSECTION_MASK: 0xff000000
    if (pfn >= 0x100000 && (paddr & ~SUPERSECTION_MASK))
        return NULL;
#endif

    // mtype: MT_DEVICE: 0
    // get_mem_type(MT_DEVICE: 0): &mem_types[0]
    type = get_mem_type(mtype);
    // type: &mem_types[0]

    // type: &mem_types[0]
    if (!type)
        return NULL;

    /*
     * Page align the mapping size, taking account of any offset.
     */
    // offset: 0, size: 0x1000, PAGE_ALIGN(0x1000): 0x1000
    size = PAGE_ALIGN(offset + size);
    // size: 0x1000

    /*
     * Try to reuse one of the static mapping whenever possible.
     */
    // size: 0x1000, sizeof(phys_addr_t): 4, pfn: 0x10481
    if (size && !(sizeof(phys_addr_t) == 4 && pfn >= 0x100000)) {
        struct static_vm *svm;

        // paddr: 0x10481000 size: 0x1000, mtype: MT_DEVICE: 0
        // find_static_vm_paddr(0x10481000, 0x1000, MT_DEVICE: 0): NULL
        svm = find_static_vm_paddr(paddr, size, mtype);
        // svm: NULL
        if (svm) {
            addr = (unsigned long)svm->vm.addr;
            addr += paddr - svm->vm.phys_addr;
            return (void __iomem *) (offset + addr);
        }
    }

// 2014/10/18 종료
// 2014/10/25 시작

    /*
     * Don't allow RAM to be mapped - this causes problems with ARMv6+
     */
    // pfn: 0x10481, pfn_valid(0x10481): 0
    if (WARN_ON(pfn_valid(pfn)))
        return NULL;

    // size: 0x1000, VM_IOREMAP: 0x00000001, caller: __builtin_return_address(0)
    // get_vm_area_caller(0x1000, 0x00000001, __builtin_return_address(0)): kmem_cache#30-oX (vm_struct)
    area = get_vm_area_caller(size, VM_IOREMAP, caller);
    // area: kmem_cache#30-oX (vm_struct)

    /*
    // get_vm_area_caller이 한일:
    // alloc area (GIC) 를 만들고 rb tree에 alloc area 를 추가
    // 가상주소 va_start 기준으로 GIC 를 RB Tree 추가한 결과
    //
    //                                  CHID-b
    //                               (0xF8000000)
    //                              /            \
    //                         TMR-r               PMU-r
    //                    (0xF6300000)             (0xF8180000)
    //                      /      \               /           \
    //                 SYSC-b      WDT-b         CMU-b         SRAM-b
    //            (0xF6100000)   (0xF6400000)  (0xF8100000)   (0xF8400000)
    //             /                                                 \
    //        GIC-r                                                   ROMC-r
    //   (0xF0000000)                                                 (0xF84C0000)
    //
    // (kmem_cache#30-oX (vm_struct))->flags: GFP_KERNEL: 0xD0
    // (kmem_cache#30-oX (vm_struct))->addr: 0xf0000000
    // (kmem_cache#30-oX (vm_struct))->size: 0x2000
    // (kmem_cache#30-oX (vm_struct))->caller: __builtin_return_address(0)
    //
    // (kmem_cache#30-oX (vmap_area GIC))->vm: kmem_cache#30-oX (vm_struct)
    // (kmem_cache#30-oX (vmap_area GIC))->flags: 0x04
    */

    // area: kmem_cache#30-oX (vm_struct)
    if (!area)
        return NULL;

    // area->addr: (kmem_cache#30-oX (vm_struct))->addr: 0xf0000000
    addr = (unsigned long)area->addr;
    // addr: 0xf0000000

    // area->phys_addr: (kmem_cache#30-oX (vm_struct))->phys_addr, paddr: 0x10481000
    area->phys_addr = paddr;
    // area->phys_addr: (kmem_cache#30-oX (vm_struct))->phys_addr: 0x10481000

#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE) // CONFIG_SMP=y, CONFIG_ARM_LPAE=n
    if (DOMAIN_IO == 0 &&
        (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
           cpu_is_xsc3()) && pfn >= 0x100000 &&
           !((paddr | size | addr) & ~SUPERSECTION_MASK)) {
        area->flags |= VM_ARM_SECTION_MAPPING;
        err = remap_area_supersections(addr, pfn, size, type);
    } else if (!((paddr | size | addr) & ~PMD_MASK)) {
        area->flags |= VM_ARM_SECTION_MAPPING;
        err = remap_area_sections(addr, pfn, size, type);
    } else
#endif
        // addr: 0xf0000000, size: 0x1000, paddr: 0x10481000,
        // type->prot_pte: (&mem_types[0])->prot_pte: PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED | L_PTE_SHARED (0x653)
        err = ioremap_page_range(addr, addr + size, paddr,
                     __pgprot(type->prot_pte));

    if (err) {
        vunmap((void *)addr);
        return NULL;
    }

    flush_cache_vmap(addr, addr + size);
    return (void __iomem *) (offset + addr);
}
  • err = ioremap_page_range(addr, addr + size, paddr, __pgprot(type->prot_pte));

vmalloc.c::get_vm_area_caller()

  • area = get_vm_area_caller(size, VM_IOREMAP, caller);
struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
                const void *caller)
{
    return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,
                  NUMA_NO_NODE, GFP_KERNEL, caller);
}

vmalloc.c::__get_vm_area_node()

  • __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END, NUMA_NO_NODE, GFP_KERNEL, caller);
static struct vm_struct *__get_vm_area_node(unsigned long size,
        unsigned long align, unsigned long flags, unsigned long start,
        unsigned long end, int node, gfp_t gfp_mask, const void *caller)
{
    struct vmap_area *va;
    struct vm_struct *area;

    BUG_ON(in_interrupt());
    if (flags & VM_IOREMAP)
        align = 1ul << clamp(fls(size), PAGE_SHIFT, IOREMAP_MAX_ORDER);

    size = PAGE_ALIGN(size);
    if (unlikely(!size))
        return NULL;

    area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
    if (unlikely(!area))
        return NULL;

    /*
     * We always allocate a guard page.
     */
    size += PAGE_SIZE;

    va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
    if (IS_ERR(va)) {
        kfree(area);
        return NULL;
    }

    setup_vmalloc_vm(area, va, flags, caller);

    return area;
}

vmalloc.c::alloc_vmap_area()

  • va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
static struct vmap_area *alloc_vmap_area(unsigned long size,
                unsigned long align,
                unsigned long vstart, unsigned long vend,
                int node, gfp_t gfp_mask)
{
    struct vmap_area *va;
    struct rb_node *n;
    unsigned long addr;
    int purged = 0;
    struct vmap_area *first;

    BUG_ON(!size);
    BUG_ON(size & ~PAGE_MASK);
    BUG_ON(!is_power_of_2(align));

    va = kmalloc_node(sizeof(struct vmap_area),
            gfp_mask & GFP_RECLAIM_MASK, node);
    if (unlikely(!va))
        return ERR_PTR(-ENOMEM);

    /*
     * Only scan the relevant parts containing pointers to other objects
     * to avoid false negatives.
     */
    kmemleak_scan_area(&va->rb_node, SIZE_MAX, gfp_mask & GFP_RECLAIM_MASK);

retry:
    spin_lock(&vmap_area_lock);
    /*
     * Invalidate cache if we have more permissive parameters.
     * cached_hole_size notes the largest hole noticed _below_
     * the vmap_area cached in free_vmap_cache: if size fits
     * into that hole, we want to scan from vstart to reuse
     * the hole instead of allocating above free_vmap_cache.
     * Note that __free_vmap_area may update free_vmap_cache
     * without updating cached_hole_size or cached_align.
     */
    // static unsigned long cached_hole_size;
    // size:0x2000, vstart: 0xf0000000, cached_vstart: 0,
    // align: 0x2000, cached_align: 0
    if (!free_vmap_cache ||
            size < cached_hole_size ||
            vstart < cached_vstart ||
            align < cached_align) {
nocache:
        cached_hole_size = 0;
        // cached_hole_size: 0
        free_vmap_cache = NULL;
        // free_vmap_cache: NULL
    }
    /* record if we encounter less permissive parameters */
    cached_vstart = vstart;
    // cached_vstart: 0xf0000000
    cached_align = align;
    // cached_align: 0x2000

    /* find starting point for our search */
    if (free_vmap_cache) {
        first = rb_entry(free_vmap_cache, struct vmap_area, rb_node);
        addr = ALIGN(first->va_end, align);
        if (addr < vstart)
            goto nocache;
        if (addr + size < addr)
            goto overflow;

    } else {
        addr = ALIGN(vstart, align);
        // addr: 0xf0000000
        if (addr + size < addr)
        // addr:0xf0000000, size: 0x2000 < addr: 0x0xf0000000
        // 조건이 부정
            goto overflow;

        n = vmap_area_root.rb_node;
        // n: vmap_area_root.rb_node
        // vmalloc_init()->__insert_vmap_area()에서 호출
        first = NULL;

        while (n) {
            struct vmap_area *tmp;
            tmp = rb_entry(n, struct vmap_area, rb_node);
            if (tmp->va_end >= addr) {
                first = tmp;
                if (tmp->va_start <= addr)
                    break;
                n = n->rb_left;
            } else
                n = n->rb_right;
        }

        if (!first)
            goto found;
    }

    /* from the starting point, walk areas until a suitable hole is found */
    while (addr + size > first->va_start && addr + size <= vend) {
        if (addr + cached_hole_size < first->va_start)
            cached_hole_size = first->va_start - addr;
        addr = ALIGN(first->va_end, align);
        if (addr + size < addr)
            goto overflow;

        if (list_is_last(&first->list, &vmap_area_list))
            goto found;

        first = list_entry(first->list.next,
                struct vmap_area, list);
    }

found:
    if (addr + size > vend)
        goto overflow;

    va->va_start = addr;
    va->va_end = addr + size;
    va->flags = 0;
    __insert_vmap_area(va);
    free_vmap_cache = &va->rb_node;
    spin_unlock(&vmap_area_lock);

    BUG_ON(va->va_start & (align-1));
    BUG_ON(va->va_start < vstart);
    BUG_ON(va->va_end > vend);

    return va;

overflow:
    spin_unlock(&vmap_area_lock);
    if (!purged) {
        purge_vmap_area_lazy();
        purged = 1;
        goto retry;
    }
    if (printk_ratelimit())
        printk(KERN_WARNING
            "vmap allocation for size %lu failed: "
            "use vmalloc=<size> to increase size.\n", size);
    kfree(va);
    return ERR_PTR(-EBUSY);
}
  • tmp = rb_entry(n, struct vmap_area, rb_node);
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
  • CMU->va_end: 0xf8124000 > addr: 

git log

   73e11e6..c41e395  master     -> origin/master
Updating 73e11e6..c41e395
Fast-forward
Reference/linux_scheduler_notes_final.pdf   | Bin 0 -> 298859 bytes
arch/arm/include/asm/memory.h               |   2 +
arch/arm/include/asm/pgtable-2level-types.h |   1 +
arch/arm/include/asm/pgtable-2level.h       |   9 ++
arch/arm/include/asm/pgtable.h              |   3 +
arch/arm/kernel/setup.c                     |   3 +
arch/arm/mm/ioremap.c                       |  40 +++++-
arch/arm/mm/mmu.c                           |   8 ++
include/asm-generic/pgtable.h               |   3 +
include/linux/err.h                         |   2 +
include/linux/io.h                          |   3 +-
init/main.c                                 |  15 +++
lib/ioremap.c                               |  22 ++++
lib/rbtree.c                                |  95 ++++++++++++--
mm/vmalloc.c                                | 189 ++++++++++++++++++++++++----
15 files changed, 360 insertions(+), 35 deletions(-)
create mode 100755 Reference/linux_scheduler_notes_final.pdf
mode change 100644 => 100755 lib/rbtree.c