2014년 10월 11일 토요일

[Linux Kernel] 73주차(2014.10.11)

ARM10C 73주차 후기

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

스터디에 도움될 만한 정보 공유

스터디 진도 :

  • init_IRQ();
    • exynos-combiner.c 에 정의된 함수를 사용하여 초기화 수행
    • __irqchip_begin: irqchip_of_match_exynos4210_combiner

main.c::start_kernel()

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()

// 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.c::of_irq_init()

  • irqchip_init()은 다시 of_irq_init()을 __irqchip_begin을 매개변수로 해서 호출한다. 
// ARM10C 20141004
void __init irqchip_init(void)
{
    // exynos-combiner.c 에 정의된 함수를 사용하여 초기화 수행
    // __irqchip_begin: irqchip_of_match_exynos4210_combiner
    of_irq_init(__irqchip_begin);
}
  • 여기서 __irqchip_begin은 외부 구조체로 정의한다.
// ARM10C 20141004
extern struct of_device_id __irqchip_begin[];
  • 외부 구조체는 vmlinux.lds.h에서 링크된다.
o#ifdef CONFIG_IRQCHIP // CONFIG_IRQCHIP=y
/*
// ARM10C 20141004
*/
#define IRQCHIP_OF_MATCH_TABLE()            \
    . = ALIGN(8);                           \
    VMLINUX_SYMBOL(__irqchip_begin) = .;    \
    *(__irqchip_of_table)                   \
    *(__irqchip_of_end)
#else
#define IRQCHIP_OF_MATCH_TABLE()
#endif

irq.c::of_irq_init()

// ARM10C 20141004
// __irqchip_begin: irqchip_of_match_exynos4210_combiner
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의 주소): NULL
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent, np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        // of_irq_find_parent(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소): exynos5.dtsi 에 있는 gic node
        desc->interrupt_parent = of_irq_find_parent(np);
        // desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: NULL
        // desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL

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

            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: NULL, parent: NULL
            if (desc->interrupt_parent != parent)
                continue;

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

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

            // match->data; irqchip_of_match_exynos4210_combiner.data: combiner_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_exynos4210_combiner.compatible: "samsung,exynos4210-combiner",
            // desc->dev: (kmem_cache#30-o10)->dev: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
            // desc->interrupt_parent: (kmem_cache#30-o10)->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 samsung,exynos4210-combiner @ 0x(combiner node의 주소), parent 0\n"

// 2014/10/11 종료

irq.c::of_irq_init() 요약.

  • INIT_LIST_HEAD(&intc_desc_list);
    • intc_desc_list 리스트 초기화
  • INIT_LIST_HEAD(&intc_parent_list);
    • intc_parent_list 리스트 초기화
  • for_each_matching_node(np, matches) {
    • desc = kzalloc(sizeof(*desc), GFP_KERNEL);
      • desc: kmem_cache#30-o10
    • desc: kmem_cache#30-o11
    • 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의 주소
    • desc->interrupt_parent = of_irq_find_parent(np);
      • desc->interrupt_parent: (kmem_cache#30-o10)->interrupt_parent: NULL
    • desc->interrupt_parent: (kmem_cache#30-o11)->interrupt_parent: NULL
    • 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에 추가
  • while (!list_empty(&intc_desc_list)) {
    • list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
      • list_del(&desc->list);
        • intc_desc_list에서 (kmem_cache#30-o10)->list를 삭제
    • match = of_match_node(matches, desc->dev);
      • match: irqchip_of_match_exynos4210_combiner
    • pr_debug("of_irq_init: init %s @ %p, parent %p\n", match->compatible, desc->dev, desc->interrupt_parent);
      • "of_irq_init: init samsung,exynos4210-combiner @ 0x(combiner node의 주소), parent 0\n"
  • 다음주에 init_IRQ()를 계속 분석합니다.

string.c::strlen()

size_t strlen(const char *s)
{
    const char *sc = s;

    while (*sc != '\0')
        sc++;
    return sc - s;
}

irq.c::of_irq_find_parent()

// ARM10C 20141011
// np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
struct device_node *of_irq_find_parent(struct device_node *child)
{
    struct device_node *p;
    const __be32 *parp;

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

    do {
        // child: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        // of_get_property(devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, "interrupt-parent", NULL): gic 의 주소
        parp = of_get_property(child, "interrupt-parent", NULL);
        // parp: gic 의 주소

        // parp: gic 의 주소
        if (parp == NULL)
            p = of_get_parent(child);
        else {
            // of_irq_workarounds: 0, OF_IMAP_NO_PHANDLE: 0x00000002
            if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
                p = of_node_get(of_irq_dflt_pic);
            else
                // parp: gic 의 주소, of_find_node_by_phandle(gic 의 주소): NULL
                p = of_find_node_by_phandle(be32_to_cpup(parp));
                // p: NULL
        }

        // child: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소
        of_node_put(child); // null function

        // child: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소, p: NULL
        child = p;
        // child: NULL

        // p: NULL, of_get_property(NULL, "#interrupt-cells", NULL): NULL
    } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL);

    // p: NULL
    return p;
    // return NULL
}

base.c::of_get_property()

// ARM10C 20141011
const void *of_get_property(const struct device_node *np, const char *name,
                int *lenp)
{
    struct property *pp = of_find_property(np, name, lenp);

    return pp ? pp->value : NULL;
}
EXPORT_SYMBOL(of_get_property);

base.c::of_find_property()

// ARM10C 20141011
// np: devtree에서 allnext로 순회 하면서 찾은 gic node의 주소 (cortex_a15_gic), "interrupt-controller", NULL
struct property *of_find_property(const struct device_node *np,
                  const char *name,
                  int *lenp)
{
    struct property *pp;
    unsigned long flags;

    raw_spin_lock_irqsave(&devtree_lock, flags);
    // devtree_lock을 사용한 spin lock 수행하고 cpsr을 flags에 저장

    // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, name: "interrupt-controller", lenp: NULL
    // __of_find_property(devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, "interrupt-controller", NULL):
    // combiner node의 "interrupt-controller" property의 주소
    pp = __of_find_property(np, name, lenp);
    // pp: combiner node의 "interrupt-controller" property의 주소

    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    // devtree_lock을 사용한 spin lock 해재하고 flags에 저장된 cpsr을 복원

    // pp: combiner node의 "interrupt-controller" property의 주소
    return pp;
    // return: combiner node의 "interrupt-controller" property의 주소
}
EXPORT_SYMBOL(of_find_property);

base.c::__of_find_property()

// ARM10C 20141004
// np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, name: "interrupt-controller", lenp: NULL
static struct property *__of_find_property(const struct device_node *np,
                       const char *name, int *lenp)
{
    struct property *pp;

    // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
    if (!np)
        return NULL;

    // np: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
    for (pp = np->properties; pp; pp = pp->next) {
        // pp: pp->name이 "interrupt-controller" 일때의 주소 (exynos5.dtsi)

        // pp->name: "interrupt-controller", name: "interrupt-controller"
        // of_prop_cmp("interrupt-controller", "interrupt-controller"): 0
        if (of_prop_cmp(pp->name, name) == 0) {
            // lenp: NULL
            if (lenp)
                *lenp = pp->length;
            break;
        }
    }

    // pp: combiner node의 "interrupt-controller" property의 주소
    return pp;
    // return combiner node의 "interrupt-controller" property의 주소
}

base.c::of_find_node_by_phandle()

  • np->phandle에 값이 존재하기 위해선 exynos5420 용 dts 파일들의 node 들 중에
  • "phandle", "linux,phandle" property를 가지는 node가 존재해야 값을 가짐 (fdt.c의 360 line 참고)
  • exynos5420 용 dts 파일들의 node를 찾아 본 결과 "phandle", "linux,phandle" property를
  • 가지는 node는 존재하지 않음
  • for loop 수행 결과 np값은 NULL을 가지게 됨
// ARM10C 20141011
// parp: gic 의 주소
struct device_node *of_find_node_by_phandle(phandle handle)
{
    struct device_node *np;
    unsigned long flags;

    raw_spin_lock_irqsave(&devtree_lock, flags);
    // devtree_lock을 사용한 spin lock 수행하고 cpsr을 flags에 저장

    for (np = of_allnodes; np; np = np->allnext)
        // np->phandle: NULL, handle: gic 의 주소
        if (np->phandle == handle)
            break;

    // np: NULL
    of_node_get(np);

    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    // devtree_lock을 사용한 spin lock 해재하고 flags에 저장된 cpsr을 복원

    // np: NULL
    return np;
    // return NULL
}
EXPORT_SYMBOL(of_find_node_by_phandle);

of.h::of_node_get()

// ARM10C 20141011
static inline struct device_node *of_node_get(struct device_node *node)
{
    return node;
}

of.h::of_node_put()

// ARM10C 20141004
static inline void of_node_put(struct device_node *node) { }

base.c::of_get_property()

// ARM10C 20141011
const void *of_get_property(const struct device_node *np, const char *name,
                int *lenp)
{
    struct property *pp = of_find_property(np, name, lenp);

    return pp ? pp->value : NULL;
}
EXPORT_SYMBOL(of_get_property);

base.c::of_match_node()

// ARM10C 20141011
// matches: irqchip_of_match_exynos4210_combiner,
// desc->dev: (kmem_cache#30-o10)->dev: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
const struct of_device_id *of_match_node(const struct of_device_id *matches,
                     const struct device_node *node)
{
    const struct of_device_id *match;
    unsigned long flags;

    raw_spin_lock_irqsave(&devtree_lock, flags);
    // devtree_lock을 사용한 spin lock 수행하고 cpsr을 flags에 저장

    // matches: irqchip_of_match_exynos4210_combiner, node: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
    // __of_match_node(irqchip_of_match_exynos4210_combiner, devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소):
    // irqchip_of_match_exynos4210_combiner
    match = __of_match_node(matches, node);
    // match: irqchip_of_match_exynos4210_combiner

    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    // devtree_lock을 사용한 spin lock 해재하고 flags에 저장된 cpsr을 복원

    // match: irqchip_of_match_exynos4210_combiner
    return match;
    // return irqchip_of_match_exynos4210_combiner
}
EXPORT_SYMBOL(of_match_node);

base.c::__of_match_node()

// ARM10C 20141011
// matches: irqchip_of_match_exynos4210_combiner, node: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소
static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
                       const struct device_node *node)
{
    // matches: irqchip_of_match_exynos4210_combiner
    // matches: irqchip_of_match_cortex_a15_gic
    if (!matches)
        return NULL;

    // matches->name[0]: irqchip_of_match_cortex_a15_gic->name[0]: NULL,
    // matches->type[0]: irqchip_of_match_cortex_a15_gic->type[0]: NULL,
    // matches->compatible[0]: irqchip_of_match_cortex_a15_gic->compatible[0]: 'a'
    while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
        int match = 1;
        // match: 1

        // matches->name[0]: irqchip_of_match_cortex_a15_gic->name[0]: NULL
        if (matches->name[0])
            match &= node->name
                && !strcmp(matches->name, node->name);

        // matches->type[0]: irqchip_of_match_cortex_a15_gic->type[0]: NULL
        if (matches->type[0])
            match &= node->type
                && !strcmp(matches->type, node->type);

        // matches->compatible[0]: irqchip_of_match_cortex_a15_gic->compatible[0]: 'a'
        if (matches->compatible[0])
            // match: 1, node: gic node의 주소,
            // matches->compatible: irqchip_of_match_cortex_a15_gic->compatible: "arm,cortex-a15-gic"
            // __of_device_is_compatible(gic node의 주소, "arm,cortex-a15-gic"): 1
            match &= __of_device_is_compatible(node,
                               matches->compatible);
            // match: 1

        // match: 1
        if (match)
            // matches: irqchip_of_match_cortex_a15_gic
            return matches;
            // return irqchip_of_match_cortex_a15_gic

        matches++;
    }
    return NULL;
}
  • match = __of_match_node()에서 한일
    • match: irqchip_of_match_exynos4210_combiner
  • 다음시간에 init_IRQ() 계속 분석합니다.

댓글 없음:

댓글 쓰기