2014년 9월 20일 토요일

[Linux Kernel] 70주차(2014.09.20)

ARM10C 70주차 후기

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

스터디 진도 :

  • sched_init()

main.c::start_kernel()

  • sched_init()을 분석합니다.
  • start_kernel()부터 sched_init() 전에 어떤 일을 했는지 간추려서 보겠습니다.
asmlinkage void __init start_kernel(void)
{
    char * command_line;
    extern const struct kernel_param __start___param[], __stop___param[];

...
    local_irq_disable();
    // IRQ를 disable한다.
    early_boot_irqs_disabled = true;

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

    page_address_init();
    // 128개의 page_address_htable 배열을 초기화

    pr_notice("%s", linux_banner);
    // 배너:
    //  Linux version 2.6.37_DM385_IPNC_3.50.00
    //  (a0875405@bangvideoapps01) (gcc version 4.5.3 20110311 
    //  (prerelease) (GCC) ) #1 Fri Dec 21 17:27:08 IST 2012

    setup_arch(&command_line);
    // ARM 아키텍쳐에 맞게 초기화한다.

    setup_nr_cpu_ids();
    setup_per_cpu_areas();
    // pcpu 구조체를 만들어 줌 (mm/percpu.c)

    smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
    // boot cpu 0의 pcpu 영역의 base주소를 core register에 설정해줌

...
    pidhash_init();
    // pidhash의 크기를 16kB만큼 할당 받고 4096개의 hash list를 만듬

    vfs_caches_init_early();
    // Dentry cache, Inode-cache용 hash를 위한 메모리 공간을 각각 512kB, 256kB만큼 할당 받고,
    // 131072, 65536개 만큼 hash table을 각각 만듬

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

    sched_init();
mm_init()에서 buddy와 slab을 활성화하고 sched_init()을 실행한다.

core.c::sched_init()

  • CONFIG_xxx에 따라서 Sched는 분기한다.
    • CONFIG_FAIR_GROUP_SCHED
    • CONFIG_RT_GROUP_SCHED
    • CONFIG_CPUMASK_OFFSTACK
  • init_defrootdomain();
    • // def_root_domain의 맴버 값을 초기화 수행
    • // (&(&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 101])->count: 0
    • // (&(&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 101])->mask.bit[0]: 0
    • // (&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 3]: -1
    • // &def_root_domain.refcount: 1
  • init_rt_bandwidth(&def_rt_bandwidth, global_rt_period(), global_rt_runtime());
    • // init_rt_bandwidth에서 한일:
    • // (&def_rt_bandwidth)->rt_period: 1000000000
    • // (&def_rt_bandwidth)->rt_runtime: 950000000
    • // &(&def_rt_bandwidth)->rt_runtime_lock을 사용한 spinlock 초기화
    • // (&def_rt_bandwidth)->rt_period_timer의 값을 0으로 초기화
    • // &(&def_rt_bandwidth)->rt_period_timer)->base: &hrtimer_bases->clock_base[0]
    • // (&(&(&def_rt_bandwidth)->rt_period_timer)->node)->node의 RB Tree의 초기화
    • // &(&def_rt_bandwidth)->rt_period_timer.function: sched_rt_period_timer
  • for_each_possible_cpu(i) {
    • raw_spin_lock_init(&rq->lock);
      • // [pcp0] &rq->lock: &(&runqueues)->lock 을 사용한 spinlock 초기화 수행
    • // [pcp0] rq->nr_running: (&runqueues)->nr_running: 0
    • // [pcp0] rq->calc_load_active: (&runqueues)->calc_load_active: 0
    • // [pcp0] rq->calc_load_update: (&runqueues)->calc_load_update: -29499 (0xFFFFFFFFFFFF8CC5)
    • init_cfs_rq(&rq->cfs);
      • // init_cfs_rq 에서 한일:
    • // (&(&runqueues)->cfs)->tasks_timeline: (struct rb_root) { NULL, }
    • // (&(&runqueues)->cfs)->min_vruntime: 0xFFFFFFFFFFF00000
    • // (&(&runqueues)->cfs)->min_vruntime_copy: 0xFFFFFFFFFFF00000
    • // (&(&runqueues)->cfs)->decay_counter: 1
    • // (&(&runqueues)->cfs)->removed_load: 0
    • init_rt_rq(&rq->rt, rq);
      • // init_rt_rq 에서 한일:
    • // (&(&(&runqueues)->rt)->active)->bitmap의 0 ... 99 bit를 클리어
    • // (&(&(&runqueues)->rt)->active)->queue[0 ... 99] 의 리스트 초기화
    • // (&(&(&runqueues)->rt)->active)->bitmap의 100 bit를 1로 세팅
    • // (&(&runqueues)->rt)->rt_runtime_lock 을 사용한 spinlock 초기화
    • // (&(&runqueues)->rt)->rt_runtime: 0
    • // (&(&runqueues)->rt)->rt_throttled: 0
    • // (&(&runqueues)->rt)->rt_time: 0
    • // (&(&(&runqueues)->rt)->pushable_tasks)->node_list 리스트 초기화
    • // (&(&runqueues)->rt)->overloaded: 0
    • // (&(&runqueues)->rt)->rt_nr_migratory: 0
    • // (&(&runqueues)->rt)->highest_prio.next: 100
    • // (&(&runqueues)->rt)->highest_prio.curr: 100
    • // [pcp0] rq->rt.rt_runtime: (&runqueues)->rt.rt_runtime: 950000000
    • // [pcp0] j: 0, rq->cpu_load[0]: (&runqueues)->cpu_load[0]: 0, 1, 2, 3 실행
    • // [pcp0] rq->last_load_update_tick: (&runqueues)->last_load_update_tick: -30000 (0xFFFFFFFFFFFF8AD0)
    • // [pcp0] rq->sd: (&runqueues)->sd: NULL
    • // [pcp0] rq->rd: (&runqueues)->rd: NULL
    • // [pcp0] rq->cpu_power: (&runqueues)->cpu_power: 0x400
    • // [pcp0] rq->post_schedule: (&runqueues)->post_schedule: 0
    • // [pcp0] rq->active_balance: (&runqueues)->active_balance: 0
    • // [pcp0] rq->next_balance: (&runqueues)->next_balance: -30000 (0xFFFFFFFFFFFF8AD0)
    • // [pcp0] rq->push_cpu: (&runqueues)->push_cpu: 0
    • // [pcp0] rq->cpu: (&runqueues)->cpu: 0
    • // [pcp0] rq->online: (&runqueues)->online: 0
    • // [pcp0] rq->idle_stamp: (&runqueues)->idle_stamp: 0
    • // [pcp0] rq->avg_idle: (&runqueues)->avg_idle: 1000000UL
    • // [pcp0] rq->max_idle_balance_cost: (&runqueues)->max_idle_balance_cost: 500000UL
    • INIT_LIST_HEAD(&rq->cfs_tasks);
      • // [pcp0] &rq->cfs_tasks: &(&runqueues)->cfs_tasks 의 list 초기화 수행
    • rq_attach_root(rq, &def_root_domain);
      • // rq_attach_root에서 한일:
    • // (&def_root_domain)->span: 1
    • // (&runqueues)->rd: &def_root_domain
    • // &(&runqueues)->refcount: 1
    • // [pcp0] rq->nohz_flags: (&runqueues)->nohz_flags: 0
    • init_rq_hrtick(rq);
      • // init_rq_hrtick에서 한일:
    • // (&runqueues)->hrtick_csd_pending: 0
    • // (&runqueues)->hrtick_csd.flags: 0
    • // (&runqueues)->hrtick_csd.func: __hrtick_start
    • // (&runqueues)->hrtick_csd.info: &runqueues
    • // (&runqueues)->hrtick_timer의 값을 0으로 초기화
    • // (&(&runqueues)->hrtick_timer)->base: &hrtimer_bases->clock_base[0]
    • // RB Tree의 (&(&(&runqueues)->hrtick_timer)->node)->node 를 초기화
    • // &rq->hrtick_timer.function: &(&runqueues)->hrtick_timerhrtick_timer.function: hrtick
    • // [pcp0] &rq->nr_iowait: &(&runqueues)->nr_iowait: 0
  • set_load_weight(&init_task);
    • // set_load_weight에서 한일:
    • // (&(&init_task)->se.load)->weight: 1024
    • // (&(&init_task)->se.load)->inv_weight: 4194304
  • plist_head_init(&init_task.pi_waiters);
    • // plist_head_init에서 한일:
    • // (&init_task.pi_waiters)->node_list 리스트 초기화
  • atomic_inc(&init_mm.mm_count);
    • // init_mm.mm_count: 2
  • init_idle(current, smp_processor_id());
    • // init_idle에서 한일:
    • // (&init_task)->on_rq: 0
    • // (&init_task)->se.on_rq: 0
    • // (&init_task)->se.exec_start: 0
    • // (&init_task)->se.sum_exec_runtime: 0
    • // (&init_task)->se.prev_sum_exec_runtime: 0
    • // (&init_task)->se.nr_migrations: 0
    • // (&init_task)->se.vruntime: 0
    • // &(&init_task)->se.group_node의 리스트 초기화
    • // &(&init_task)->rt.run_list의 리스트 초기화
    • // (&init_task)->state: TASK_RUNNING: 0
    • // (&init_task)->se.exec_start: 0
    • // (&init_task)->cpus_allowed->bits[0]: 1
    • // (&init_task)->nr_cpus_allowed: 1
    • // ((struct thread_info *)(&init_task)->stack)->cpu: 0
    • // (&init_task)->wake_cpu: 0
    • // pcpu0->curr: &init_task
    • // pcpu0->idle: &init_task
    • // (&init_task)->on_cpu: 1
    • // ((struct thread_info *)(&init_task)->stack)->preempt_count: PREEMPT_ENABLED: 0
    • // (&init_task)->sched_class: &idle_sched_class
    • // (&init_task)->comm: "swapper/0"
  • calc_load_update = jiffies + LOAD_FREQ;
    • // calc_load_update: -29499 (0xFFFFFFFFFFFF8cc5)
  • // current->sched_class: (&init_task)->sched_class: &fair_sched_class
  • zalloc_cpumask_var(&sched_domains_tmpmask, GFP_NOWAIT);
    • // sched_domains_tmpmask.bits[0]: 0
  • zalloc_cpumask_var(&cpu_isolated_map, GFP_NOWAIT);
    • // cpu_isolated_map.bits[0]: 0
  • idle_thread_set_boot_cpu();
    • // [pcp0] idle_threads: &init_task
  • init_sched_fair_class();
    • // init_sched_fair_class가 한일:
    • // softirq_vec[7].action: run_rebalance_domains
    • // nohz.next_balance: -30000 (0xFFFFFFFFFFFF8AD0)
    • // nohz.idle_cpus_mask.bits[0]: 0
    • // (&cpu_chain)->head: sched_ilb_notifier_nb 포인터 대입
    • // (&sched_ilb_notifier_nb)->next은 (&slab_notifier)->next로 대입
  • // scheduler_running: 1
// ARM10C 20140830
void __init sched_init(void)
{
    int i, j;
    unsigned long alloc_size = 0, ptr;
    // alloc_size: 0

#ifdef CONFIG_FAIR_GROUP_SCHED // CONFIG_FAIR_GROUP_SCHED=n
    alloc_size += 2 * nr_cpu_ids * sizeof(void **);
#endif
#ifdef CONFIG_RT_GROUP_SCHED // CONFIG_RT_GROUP_SCHED=n
    alloc_size += 2 * nr_cpu_ids * sizeof(void **);
#endif
#ifdef CONFIG_CPUMASK_OFFSTACK // CONFIG_CPUMASK_OFFSTACK=n
    alloc_size += num_possible_cpus() * cpumask_size();
#endif
    // alloc_size: 0
    if (alloc_size) {
        ptr = (unsigned long)kzalloc(alloc_size, GFP_NOWAIT);

#ifdef CONFIG_FAIR_GROUP_SCHED // CONFIG_FAIR_GROUP_SCHED=n
        root_task_group.se = (struct sched_entity **)ptr;
        ptr += nr_cpu_ids * sizeof(void **);

        root_task_group.cfs_rq = (struct cfs_rq **)ptr;
        ptr += nr_cpu_ids * sizeof(void **);

#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHED // CONFIG_RT_GROUP_SCHED=n
        root_task_group.rt_se = (struct sched_rt_entity **)ptr;
        ptr += nr_cpu_ids * sizeof(void **);

        root_task_group.rt_rq = (struct rt_rq **)ptr;
        ptr += nr_cpu_ids * sizeof(void **);

#endif /* CONFIG_RT_GROUP_SCHED */
#ifdef CONFIG_CPUMASK_OFFSTACK // CONFIG_CPUMASK_OFFSTACK=n
        for_each_possible_cpu(i) {
            per_cpu(load_balance_mask, i) = (void *)ptr;
            ptr += cpumask_size();
        }
#endif /* CONFIG_CPUMASK_OFFSTACK */
    }

#ifdef CONFIG_SMP // CONFIG_SMP=y
    init_defrootdomain();
    // def_root_domain의 맴버 값을 초기화 수행
    // (&(&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 101])->count: 0
    // (&(&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 101])->mask.bit[0]: 0
    // (&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 3]: -1
    // &def_root_domain.refcount: 1
#endif

    // global_rt_period(): 1000000000, global_rt_runtime(): 950000000
    init_rt_bandwidth(&def_rt_bandwidth,
            global_rt_period(), global_rt_runtime());
    // init_rt_bandwidth에서 한일:
    // (&def_rt_bandwidth)->rt_period: 1000000000
    // (&def_rt_bandwidth)->rt_runtime: 950000000
    // &(&def_rt_bandwidth)->rt_runtime_lock을 사용한 spinlock 초기화
    // (&def_rt_bandwidth)->rt_period_timer의 값을 0으로 초기화
    // &(&def_rt_bandwidth)->rt_period_timer)->base: &hrtimer_bases->clock_base[0]
    // (&(&(&def_rt_bandwidth)->rt_period_timer)->node)->node의 RB Tree의 초기화
    // &(&def_rt_bandwidth)->rt_period_timer.function: sched_rt_period_timer

#ifdef CONFIG_RT_GROUP_SCHED // CONFIG_RT_GROUP_SCHED=n
    init_rt_bandwidth(&root_task_group.rt_bandwidth,
            global_rt_period(), global_rt_runtime());
#endif /* CONFIG_RT_GROUP_SCHED */

#ifdef CONFIG_CGROUP_SCHED // CONFIG_CGROUP_SCHED=n
    list_add(&root_task_group.list, &task_groups);
    INIT_LIST_HEAD(&root_task_group.children);
    INIT_LIST_HEAD(&root_task_group.siblings);
    autogroup_init(&init_task);

#endif /* CONFIG_CGROUP_SCHED */

    for_each_possible_cpu(i) {
    // for ((i) = -1; (i) = cpumask_next((i), (cpu_possible_mask)), (i) < nr_cpu_ids; )
        struct rq *rq;

        // i: 0
        // cpu_rq(0):
        // &({
        //      do {
        //      const void __percpu *__vpp_verify = (typeof(&(runqueues)))NULL;
        //      (void)__vpp_verify;
        //      } while (0)
        //      (&(runqueues) + (pcpu_unit_offsets[0] + __per_cpu_start에서의pcpu_base_addr의 옵셋);
        // })
        rq = cpu_rq(i);
        // rq: (&(runqueues) + (pcpu_unit_offsets[0] + __per_cpu_start에서의pcpu_base_addr의 옵셋)

        // NOTE:
        // rq: (&(runqueues) + (pcpu_unit_offsets[0] + __per_cpu_start에서의pcpu_base_addr의 옵셋)
        // 의 주석을 간결하게 하기 위해 [pcp0] 를 변수 앞에 추가하고 rq: (&runqueue) 로 사용함

        // [pcp0] &rq->lock: &(&runqueues)->lock
        raw_spin_lock_init(&rq->lock);
        // [pcp0] &rq->lock: &(&runqueues)->lock 을 사용한 spinlock 초기화 수행

        // [pcp0] rq->nr_running: (&runqueues)->nr_running
        rq->nr_running = 0;
        // [pcp0] rq->nr_running: (&runqueues)->nr_running: 0

        // [pcp0] rq->calc_load_active: (&runqueues)->calc_load_active
        rq->calc_load_active = 0;
        // [pcp0] rq->calc_load_active: (&runqueues)->calc_load_active: 0

        // [pcp0] rq->calc_load_update: (&runqueues)->calc_load_update,
        // jiffies: -30000 (0xFFFFFFFFFFFF8AD0): vmlinux.lds.S 에 있음, LOAD_FREQ: 501
        rq->calc_load_update = jiffies + LOAD_FREQ;
        // [pcp0] rq->calc_load_update: (&runqueues)->calc_load_update: -29499 (0xFFFFFFFFFFFF8CC5)

        // [pcp0] &rq->cfs: &(&runqueues)->cfs
        init_cfs_rq(&rq->cfs);
        // init_cfs_rq 에서 한일:
        // (&(&runqueues)->cfs)->tasks_timeline: (struct rb_root) { NULL, }
        // (&(&runqueues)->cfs)->min_vruntime: 0xFFFFFFFFFFF00000
        // (&(&runqueues)->cfs)->min_vruntime_copy: 0xFFFFFFFFFFF00000
        // (&(&runqueues)->cfs)->decay_counter: 1
        // (&(&runqueues)->cfs)->removed_load: 0

        // [pcp0] &rq->rt: &(&runqueues)->rt, rq: (&runqueues)
        init_rt_rq(&rq->rt, rq);
        // init_rt_rq 에서 한일:
        // (&(&(&runqueues)->rt)->active)->bitmap의 0 ... 99 bit를 클리어
        // (&(&(&runqueues)->rt)->active)->queue[0 ... 99] 의 리스트 초기화
        // (&(&(&runqueues)->rt)->active)->bitmap의 100 bit를 1로 세팅
        // (&(&runqueues)->rt)->rt_runtime_lock 을 사용한 spinlock 초기화
        // (&(&runqueues)->rt)->rt_runtime: 0
        // (&(&runqueues)->rt)->rt_throttled: 0
        // (&(&runqueues)->rt)->rt_time: 0
        // (&(&(&runqueues)->rt)->pushable_tasks)->node_list 리스트 초기화
        // (&(&runqueues)->rt)->overloaded: 0
        // (&(&runqueues)->rt)->rt_nr_migratory: 0
        // (&(&runqueues)->rt)->highest_prio.next: 100
        // (&(&runqueues)->rt)->highest_prio.curr: 100

// 2014/08/30 종료
// 2014/09/13 시작

#ifdef CONFIG_FAIR_GROUP_SCHED // CONFIG_FAIR_GROUP_SCHED=n
        root_task_group.shares = ROOT_TASK_GROUP_LOAD;
        INIT_LIST_HEAD(&rq->leaf_cfs_rq_list);
        /*
         * How much cpu bandwidth does root_task_group get?
         *
         * In case of task-groups formed thr' the cgroup filesystem, it
         * gets 100% of the cpu resources in the system. This overall
         * system cpu resource is divided among the tasks of
         * root_task_group and its child task-groups in a fair manner,
         * based on each entity's (task or task-group's) weight
         * (se->load.weight).
         *
         * In other words, if root_task_group has 10 tasks of weight
         * 1024) and two child groups A0 and A1 (of weight 1024 each),
         * then A0's share of the cpu resource is:
         *
         *  A0's bandwidth = 1024 / (10*1024 + 1024 + 1024) = 8.33%
         *
         * We achieve this by letting root_task_group's tasks sit
         * directly in rq->cfs (i.e root_task_group->se[] = NULL).
         */
        init_cfs_bandwidth(&root_task_group.cfs_bandwidth);
        init_tg_cfs_entry(&root_task_group, &rq->cfs, NULL, i, NULL);
#endif /* CONFIG_FAIR_GROUP_SCHED */

        // [pcp0] rq->rt.rt_runtime: (&runqueues)->rt.rt_runtime,
        // def_rt_bandwidth.rt_runtime: 950000000
        rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
        // [pcp0] rq->rt.rt_runtime: (&runqueues)->rt.rt_runtime: 950000000

#ifdef CONFIG_RT_GROUP_SCHED // CONFIG_RT_GROUP_SCHED=n
        INIT_LIST_HEAD(&rq->leaf_rt_rq_list);
        init_tg_rt_entry(&root_task_group, &rq->rt, NULL, i, NULL);
#endif

        // CPU_LOAD_IDX_MAX: 5
        for (j = 0; j < CPU_LOAD_IDX_MAX; j++)
            // [pcp0] j: 0, rq->cpu_load[0]: (&runqueues)->cpu_load[0]
            rq->cpu_load[j] = 0;
            // [pcp0] j: 0, rq->cpu_load[0]: (&runqueues)->cpu_load[0]: 0
            // j: 1 .. 4 까지 수행

        // [pcp0] rq->last_load_update_tick: (&runqueues)->last_load_update_tick
        // jiffies: -30000 (0xFFFFFFFFFFFF8AD0): vmlinux.lds.S 에 있음
        rq->last_load_update_tick = jiffies;
        // [pcp0] rq->last_load_update_tick: (&runqueues)->last_load_update_tick: -30000 (0xFFFFFFFFFFFF8AD0)

#ifdef CONFIG_SMP // CONFIG_SMP=y
        // [pcp0] rq->sd: (&runqueues)->sd
        rq->sd = NULL;
        // [pcp0] rq->sd: (&runqueues)->sd: NULL

        // [pcp0] rq->rd: (&runqueues)->rd
        rq->rd = NULL;
        // [pcp0] rq->rd: (&runqueues)->rd: NULL

        // [pcp0] rq->cpu_power: (&runqueues)->cpu_power, SCHED_POWER_SCALE: 0x400
        rq->cpu_power = SCHED_POWER_SCALE;
        // [pcp0] rq->cpu_power: (&runqueues)->cpu_power: 0x400

        // [pcp0] rq->post_schedule: (&runqueues)->post_schedule
        rq->post_schedule = 0;
        // [pcp0] rq->post_schedule: (&runqueues)->post_schedule: 0

        // [pcp0] rq->active_balance: (&runqueues)->active_balance
        rq->active_balance = 0;
        // [pcp0] rq->active_balance: (&runqueues)->active_balance: 0

        // [pcp0] rq->next_balance: (&runqueues)->next_balance,
        // jiffies: -30000 (0xFFFFFFFFFFFF8AD0): vmlinux.lds.S 에 있음
        rq->next_balance = jiffies;
        // [pcp0] rq->next_balance: (&runqueues)->next_balance: -30000 (0xFFFFFFFFFFFF8AD0)

        // [pcp0] rq->push_cpu: (&runqueues)->push_cpu
        rq->push_cpu = 0;
        // [pcp0] rq->push_cpu: (&runqueues)->push_cpu: 0

        // [pcp0] rq->cpu: (&runqueues)->cpu, i: 0
        rq->cpu = i;
        // [pcp0] rq->cpu: (&runqueues)->cpu: 0

        // [pcp0] rq->online: (&runqueues)->online
        rq->online = 0;
        // [pcp0] rq->online: (&runqueues)->online: 0

        // [pcp0] rq->idle_stamp: (&runqueues)->idle_stamp
        rq->idle_stamp = 0;
        // [pcp0] rq->idle_stamp: (&runqueues)->idle_stamp: 0

        // [pcp0] rq->avg_idle: (&runqueues)->avg_idle,
        // sysctl_sched_migration_cost: 500000UL
        rq->avg_idle = 2*sysctl_sched_migration_cost;
        // [pcp0] rq->avg_idle: (&runqueues)->avg_idle: 1000000UL

        // [pcp0] rq->max_idle_balance_cost: (&runqueues)->max_idle_balance_cost,
        // sysctl_sched_migration_cost: 500000UL
        rq->max_idle_balance_cost = sysctl_sched_migration_cost;
        // [pcp0] rq->max_idle_balance_cost: (&runqueues)->max_idle_balance_cost: 500000UL

        // [pcp0] &rq->cfs_tasks: &(&runqueues)->cfs_tasks
        INIT_LIST_HEAD(&rq->cfs_tasks);
        // [pcp0] &rq->cfs_tasks: &(&runqueues)->cfs_tasks 의 list 초기화 수행

        // [pcp0] rq: &runqueues
        rq_attach_root(rq, &def_root_domain);
        // rq_attach_root에서 한일:
        // (&def_root_domain)->span: 1
        // (&runqueues)->rd: &def_root_domain
        // &(&runqueues)->refcount: 1

#ifdef CONFIG_NO_HZ_COMMON // CONFIG_NO_HZ_COMMON=y
        // [pcp0] rq->nohz_flags: (&runqueues)->nohz_flags
        rq->nohz_flags = 0;
        // [pcp0] rq->nohz_flags: (&runqueues)->nohz_flags: 0
#endif
#ifdef CONFIG_NO_HZ_FULL // CONFIG_NO_HZ_FULL=n
        rq->last_sched_tick = 0;
#endif
#endif
        // [pcp0] rq: &runqueues
        init_rq_hrtick(rq);
        // init_rq_hrtick에서 한일:
        // (&runqueues)->hrtick_csd_pending: 0
        // (&runqueues)->hrtick_csd.flags: 0
        // (&runqueues)->hrtick_csd.func: __hrtick_start
        // (&runqueues)->hrtick_csd.info: &runqueues
        // (&runqueues)->hrtick_timer의 값을 0으로 초기화
        // (&(&runqueues)->hrtick_timer)->base: &hrtimer_bases->clock_base[0]
        // RB Tree의 (&(&(&runqueues)->hrtick_timer)->node)->node 를 초기화
        // &rq->hrtick_timer.function: &(&runqueues)->hrtick_timerhrtick_timer.function: hrtick

        // [pcp0] &rq->nr_iowait: &(&runqueues)->nr_iowait
        atomic_set(&rq->nr_iowait, 0);
        // [pcp0] &rq->nr_iowait: &(&runqueues)->nr_iowait: 0

        // i: 1 .. 3 까지 루프 수행
    }

    set_load_weight(&init_task);
    // set_load_weight에서 한일:
    // (&(&init_task)->se.load)->weight: 1024
    // (&(&init_task)->se.load)->inv_weight: 4194304

#ifdef CONFIG_PREEMPT_NOTIFIERS // CONFIG_PREEMPT_NOTIFIERS=n
    INIT_HLIST_HEAD(&init_task.preempt_notifiers);
#endif

#ifdef CONFIG_RT_MUTEXES // CONFIG_RT_MUTEXES=y
    plist_head_init(&init_task.pi_waiters);
    // plist_head_init에서 한일:
    // (&init_task.pi_waiters)->node_list 리스트 초기화
#endif

    /*
     * The boot idle thread does lazy MMU switching as well:
     */
    // init_mm.mm_count: 1
    atomic_inc(&init_mm.mm_count);
    // init_mm.mm_count: 2

    // current: init_task
    enter_lazy_tlb(&init_mm, current); // null function

    /*
     * Make us the idle thread. Technically, schedule() should not be
     * called from this thread, however somewhere below it might be,
     * but because we are the idle thread, we just pick up running again
     * when this runqueue becomes "idle".
     */
    // current: &init_task, smp_processor_id(): 0
    init_idle(current, smp_processor_id());
    // init_idle에서 한일:
    // (&init_task)->on_rq: 0
    // (&init_task)->se.on_rq: 0
    // (&init_task)->se.exec_start: 0
    // (&init_task)->se.sum_exec_runtime: 0
    // (&init_task)->se.prev_sum_exec_runtime: 0
    // (&init_task)->se.nr_migrations: 0
    // (&init_task)->se.vruntime: 0
    // &(&init_task)->se.group_node의 리스트 초기화
    // &(&init_task)->rt.run_list의 리스트 초기화
    // (&init_task)->state: TASK_RUNNING: 0
    // (&init_task)->se.exec_start: 0
    // (&init_task)->cpus_allowed->bits[0]: 1
    // (&init_task)->nr_cpus_allowed: 1
    // ((struct thread_info *)(&init_task)->stack)->cpu: 0
    // (&init_task)->wake_cpu: 0
    // [pcpu0] (&runqueues)->curr: &init_task
    // [pcpu0] (&runqueues)->idle: &init_task
    // (&init_task)->on_cpu: 1
    // ((struct thread_info *)(&init_task)->stack)->preempt_count: PREEMPT_ENABLED: 0
    // (&init_task)->sched_class: &idle_sched_class
    // (&init_task)->comm: "swapper/0"

    // jiffies: -30000 (0xFFFFFFFFFFFF8AD0): vmlinux.lds.S 에 있음, LOAD_FREQ: 501
    calc_load_update = jiffies + LOAD_FREQ;
    // calc_load_update: -29499 (0xFFFFFFFFFFFF8cc5)

    /*
     * During early bootup we pretend to be a normal task:
     */
    // current->sched_class: (&init_task)->sched_class
    current->sched_class = &fair_sched_class;
    // current->sched_class: (&init_task)->sched_class: &fair_sched_class

#ifdef CONFIG_SMP // CONFIG_SMP=y
    // GFP_NOWAIT: 0
    zalloc_cpumask_var(&sched_domains_tmpmask, GFP_NOWAIT);
    // sched_domains_tmpmask.bits[0]: 0

// 2014/09/13 종료
// 2014/09/20 시작

    /* May be allocated at isolcpus cmdline parse time */
    // cpu_isolated_map: NULL
    if (cpu_isolated_map == NULL)
        // cpu_isolated_map: NULL, GFP_NOWAIT: 0
        zalloc_cpumask_var(&cpu_isolated_map, GFP_NOWAIT);
        // cpu_isolated_map.bits[0]: 0

    idle_thread_set_boot_cpu();
    // [pcp0] idle_threads: &init_task
#endif
    init_sched_fair_class();
    // init_sched_fair_class가 한일:
    // softirq_vec[7].action: run_rebalance_domains
    // nohz.next_balance: -30000 (0xFFFFFFFFFFFF8AD0)
    // nohz.idle_cpus_mask.bits[0]: 0
    // (&cpu_chain)->head: sched_ilb_notifier_nb 포인터 대입
    // (&sched_ilb_notifier_nb)->next은 (&slab_notifier)->next로 대입

    scheduler_running = 1;
    // scheduler_running: 1
}

sched_init()에서 호출하는 주요 서브 함수

  • sched_init()->init_defrootdomain()
    • // def_root_domain의 맴버 값을 초기화 수행
    • // (&(&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 101])->count: 0
    • // (&(&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 101])->mask.bit[0]: 0
    • // (&(&def_root_domain)->cpupri)->pri_to_cpu[0 ... 3]: -1
    • // &def_root_domain.refcount: 1
  • sched_init()->init_rt_bandwidth(&def_rt_bandwidth, global_rt_period(), global_rt_runtime());
    • // init_rt_bandwidth에서 한일:
    • // (&def_rt_bandwidth)->rt_period: 1000000000
    • // (&def_rt_bandwidth)->rt_runtime: 950000000
    • // &(&def_rt_bandwidth)->rt_runtime_lock을 사용한 spinlock 초기화
    • // (&def_rt_bandwidth)->rt_period_timer의 값을 0으로 초기화
    • // &(&def_rt_bandwidth)->rt_period_timer)->base: &hrtimer_bases->clock_base[0]
    • // (&(&(&def_rt_bandwidth)->rt_period_timer)->node)->node의 RB Tree의 초기화
    • // &(&def_rt_bandwidth)->rt_period_timer.function: sched_rt_period_timer
  • sched_init()::for_each_possible_cpu(i) { ... }
  • sched_init()->set_load_weight()
    • // (&(&init_task)->se.load)->weight: 1024
    • // (&(&init_task)->se.load)->inv_weight: 4194304
  • sched_init()->plist_head_init()
    • // (&init_task.pi_waiters)->node_list 리스트 초기화
  • sched_init()->init_idle()
    • // (&init_task)->on_rq: 0
    • // (&init_task)->se.on_rq: 0
    • // (&init_task)->se.exec_start: 0
    • // (&init_task)->se.sum_exec_runtime: 0
    • // (&init_task)->se.prev_sum_exec_runtime: 0
    • // (&init_task)->se.nr_migrations: 0
    • // (&init_task)->se.vruntime: 0
    • // &(&init_task)->se.group_node의 리스트 초기화
    • // &(&init_task)->rt.run_list의 리스트 초기화
    • // (&init_task)->state: TASK_RUNNING: 0
    • // (&init_task)->se.exec_start: 0
    • // (&init_task)->cpus_allowed->bits[0]: 1
    • // (&init_task)->nr_cpus_allowed: 1
    • // ((struct thread_info *)(&init_task)->stack)->cpu: 0
    • // (&init_task)->wake_cpu: 0
    • // pcpu0->curr: &init_task
    • // pcpu0->idle: &init_task
    • // (&init_task)->on_cpu: 1
    • // ((struct thread_info *)(&init_task)->stack)->preempt_count: PREEMPT_ENABLED: 0
    • // (&init_task)->sched_class: &idle_sched_class
    • // (&init_task)->comm: "swapper/0"
  • sched_init()->zalloc_cpumask_var()
    • // sched_domains_tmpmask.bits[0]: 0
  • sched_init()->idle_thread_set_boot_cpu();
    • // [pcp0] idle_threads: &init_task
  • sched_init()->init_sched_fair_class();
    • // softirq_vec[7].action: run_rebalance_domains
    • // nohz.next_balance: -30000 (0xFFFFFFFFFFFF8AD0)
    • // nohz.idle_cpus_mask.bits[0]: 0
    • // (&cpu_chain)->head: sched_ilb_notifier_nb 포인터 대입
    • // (&sched_ilb_notifier_nb)->next은 (&slab_notifier)->next로 대입

cpumask.h::zalloc_cpumask_var()

// ARM10C 20140913
// &sched_domains_tmpmask, GFP_NOWAIT: 0
static inline bool zalloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
{
    // *mask: sched_domains_tmpmask
    cpumask_clear(*mask);
    // sched_domains_tmpmask.bits[0]: 0

    return true;
}

smpboot.c::idle_thread_set_boot_cpu()

// ARM10C 20140920
void __init idle_thread_set_boot_cpu(void)
{
    // smp_processor_id(): 0,
    // per_cpu(idle_threads, 0):
    // ({
    //      do {
    //      const void __percpu *__vpp_verify = (typeof(&(idle_threads)))NULL;
    //      (void)__vpp_verify;
    //      } while (0)
    //      (&(idle_threads) + (pcpu_unit_offsets[0] + __per_cpu_start에서의pcpu_base_addr의 옵셋);
    // }),
    // current: &init_task
    per_cpu(idle_threads, smp_processor_id()) = current;
    // [pcp0] idle_threads: &init_task

}
// NOTE: // 주석의 편의를 위해 // (&(idle_threads) + (pcpu_unit_offsets[0] + __per_cpu_start에서의pcpu_base_addr의 옵셋) 를 // [pcp0] idle_threads 로 표시

main.c::start_kernel()

  • sched_init();
    • scheduler가 사용하는 자료 구조 초기화, idle_threads를 init_task로 세팅
  • preempt_disable();
    • preempt count를 증가시켜 preemption 못하도록 막음
  • irqs_disabled(): 1
  • idr_init_cache();
    • integer ID management로 사용하는 idr_layer_cache에 kmem_cache#21 을 생성 및 초기화 후 할당
  • rcu_init();
    • 분석 진행 중
asmlinkage void __init start_kernel(void)
{
...

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

    /*
     * Disable preemption - early bootup scheduling is extremely
     * fragile until we cpu_idle() for the first time.
     */
    preempt_disable();
    // preempt count를 증가시켜 preemption 못하도록 막음

    // irqs_disabled(): 1
    if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
        local_irq_disable();

    idr_init_cache();
    // integer ID management로 사용하는 idr_layer_cache에 kmem_cache#21 을 생성 및 초기화 후 할당

    rcu_init();

preempt.h::preempt_disabled()

#define preempt_disable()/*ARM10C this*/    \
do { \
    preempt_count_inc(); \
    barrier(); \
} while (0)

idr.c::idr_init_cache()

  • idr_layer_cache: kmem_cache#21
// ARM10C 20140920
void __init idr_init_cache(void)
{
    // sizeof(struct idr_layer): 1076 bytes, SLAB_PANIC: 0x00040000UL
    // kmem_cache_create("idr_layer_cache", 1076, 0, SLAB_PANIC: 0x00040000UL, NULL): kmem_cache#21
    idr_layer_cache = kmem_cache_create("idr_layer_cache",
                sizeof(struct idr_layer), 0, SLAB_PANIC, NULL);
    // idr_layer_cache: kmem_cache#21
}

tree.c::rcu_init()

// ARM10C 20140920
void __init rcu_init(void)
{
    int cpu;

    rcu_bootup_announce();
    rcu_init_geometry();
    // rcu_init_geometry에서 한일:
    // jiffies_till_first_fqs: 1
    // jiffies_till_next_fqs: 1

// 2014/09/20 종료

2014년 9월 19일 금요일

How to build the Linux kernel & u-boot for BeableBone Black.


BeagleBone Black

Linux Kernel & u-boot for the beagleboard.org boards
reference:
Please see https://github.com/beagleboard/linux for the latest official kernels.

kernel

kernel patchset usage

  • beaglebone-3.11 patchset:
git checkout origin/3.11 -b 3.11
./patch.sh
  • beaglebone-3.12 patchset:
git checkout origin/3.12 -b 3.12
./patch.sh
  • beaglebone-3.13 patchset:
git checkout origin/3.13 -b 3.13
./patch.sh
  • beaglebone-3.14 patchset:
git checkout origin/3.14 -b 3.14
./patch.sh

u-Boot

Source code download

~/bbb/ $ git clone git:://git.denx.de/u-boot.git
~/bbb/ $ git u-boot/

Source code patch

~/bbb/u-boot $ git checkout v2014.07 -b tmp
~/bbb/u-boot $ wget -c https://raw.githubusercontent.com/eewiki/u-boot-patches/master/v2014.07/0001-am335x_evm-uEnv.txt-bootz-n-fixes.patch
~/bbb/u-boot $ patch -p1 < 0001-am335x_evm-uEnv.txt-bootz-n-fixes.patch

config & build

~/bbb/u-boot $ make -j2 ARCH=arm CROSS_COMPILE=armv7a-hardfloat-linux-gnueabi- distclean
~/bbb/u-boot $ make -j2 ARCH=arm CROSS_COMPILE=armv7a-hardfloat-linux-gnueabi- am335x_evm_config
Configuring for am335x_evm - Board: am335x_evm, Options: SERIAL1,CONS_INDEX=1,NAND
~/bbb/u-boot $ make -j2 ARCH=arm CROSS_COMPILE=armv7a-hardfloat-linux-gnueabi-

check images

~/u-boot $ ls -l MLO
-rw-r--r-- 1 mscho mscho 83700  9월 19 13:42 MLO
~/u-boot $ ls -l u-boot.img
-rw-r--r-- 1 mscho mscho 450168  9월 19 13:42 u-boot.img

dtb

dtc : device tree compiler
$ wget -c https://github.com/hephaex/toolchain/tree/master/arm/dtc.sh
$ chmod +x dtc.sh
$ ./dtc.sh

microSD card install

  1. Insert the microSD Card into build machine.
    • ls /dev/sdX
  2. Begin partioning the microSD Card by typing fdisk /dev/sdX
    • fdisk /dev/sdX
    • a. Initialize a new partition table by selecting o, then verify the partition table is empty by selecting p.
    • b. Create a boot partition by selecting n for ‘new’, then p for ‘primary’, and 1 to specify the first partition. Press enter to accept the default first sector and specify 4095 for the last sector.
    • c. Change the partition type to FAT16 by selecting t for ‘type’ and e for ‘W95 FAT16 (LBA)’.
    • d. Set the partition bootable by selecting a then 1.
    • e. Create the data partition for the root filesystem by selecting n for ‘new’, then p for ‘primary’,and 2 to specify the second partition. Accept the default values for the first and last sectors by pressing enter twice.
    • f. Press p to ‘print’ the partition table.
    • g. Finally, commit the changes by selecting w to ‘write’ the partition table and exit fdisk.
root@ubuntu-armhf:/home/ubuntu# fdisk /dev/mmcblk1

Command (m for help): o
Building a new DOS disklabel with disk identifier 0xef30879d.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
      e   extended
      Select (default p): p
      Partition number (1-4, default 1): 1
      First sector (2048-31116287, default 2048):
      Using default value 2048
      Last sector, +sectors or +size{K,M,G} (2048-31116287, default 31116287): 4095

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): e
Changed system type of partition 1 to e (W95 FAT16 (LBA))

Command (m for help): a
Partition number (1-4): 1

Command (m for help): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
      e   extended
      Select (default p): p
      Partition number (1-4, default 2):
      Using default value 2
      First sector (4096-31116287, default 4096):
      Using default value 4096
      Last sector, +sectors or +size{K,M,G} (4096-31116287, default 31116287):
      Using default value 31116287

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: If you have created or modified any DOS 6.x
partitions, please see the fdisk manual page for additional
information.
Syncing disks.
  1. Format the Partitions
    • Format partition 1 as FAT
mkfs.vfat /dev/sdX1
  • Format partition 2 as ext4
mkfs.ext4 /dev/sdX2
  1. install u-boot to the microSD card
mkdir boot
mount /dev/sdX1 boot
tar xJvf uboot.tar.xz -C boot
umount boot
  1. Install the desired root filesystem to the microSD Card.
mkdir rootfs
mount /dev/sdX2 rootfs
tar xJvf ubuntu-trusty-14.04-rootfs-3.14.4.1.tar.xz -C rootfs
umount rootfs
  1. The microSD Card is ready to boot.

2014년 9월 17일 수요일

vagrant 기본 사용법

vagrant 란?

가상 환경 (VirtualBox 등)에서 가상 머신을 동작을 제어하는 도구

용어

공급자

  • 가상 환경을 말함. 
  •  ex.) VirtualBox와 VM Ware, EC2 등

프로비저닝

  • 미들웨어의 구성과 설치를 하는 도구 도구
  •  ex.) 쉘 스크립트 Chef (chef-solo, chef-client) Puppet 등

Box 파일

  • 가상 머신 시작시 기반이되는 이미지 파일
  • 가상 환경을 만드는데 필요함
  • 일반적으로 OS 이미지에서 작성한다.
  • Vagrant 를 사용할 때 최소한의 설정 조건
    • Vagrant 사용자
    • sshd 시작
    • 프로비저닝 도구

Vagrantfile

  • 가상 머신의 사양이나 프로비저닝 도구의 지정 등 가상 머신의 구성
  • Ruby 기반으로 작성
  • 기본적으로 파일과 프로비저닝 도구 설정로 2개의 파일이 있으면 동일한 환경을 구성할 수 있음

기본 명령어

버전 확인

$ vagrant -v

도움말

$ vagrant -h

가상 환경의 기반이되는 Box 파일을 준비

  • 다음의 명령으로 box를 받아서 로컬에 추가한다.
  • Vagrantfile의 config.vm.box 디렉토리로 지정
    $ vagrant box add NAME URL
    
$ vagrant box add centos64 http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130427.box
  • Box 파일은 Veewee으로 만들 수도있다. 참조

Box 목록 확인

$ vagrant box list

Vagrantfile 만들기(초기화)

$ vagrant init BOX_NAME
$ vagrant init centos64

Vagrantfile 설정

$ Vagrant.configure (VAGRANTFILE_API_VERSION) do | config |
     config.vm.box = "centos64"

     // 네트워크 설정
     config.vm.network : private_network, ip : "192.168.33.10"

     // GUI 모드의 설정
     config.vm.provider : virtualbox do | vb |
         vb.gui = true
     end
end
  • 호스트 온리 네트워크
    • 호스트 OS와 게스트 OS간에서만 통신을 할 수있는 네트워크.
    • 예시에서는 게스트에 192.168.33.10 (선택)을 할당

가상 머신의 시작

  • Vagrantfile과 같은 디렉토리에서 실행
    $ vagrant up
    
  • GUI 모드라면 VirtualBox를 시작할 때 id / pass를 모두 "vagrant" 로그인

ssh에 로그인

  • 가상 머신에서 sshd가 시작되어 있어야 사용할 수 있음
    $ vagrant ssh
    
  • 일반적인으로 ssh를 사용해서 vagrant를 사용한다.
$ ssh 192.168.33.10
  • 호스트에서 ssh 접속을 위한 설정방법
    # ~/.ssh/config
    Host 192.168.33. *
    IdentityFile ~ / .vagrant.d / insecure_private_key
    User vagrant
    
  • 가상 서버에 ssh 설정을 이용해서 로그인 할 수있다.
    $ vagrant ssh-config --host melody
    $ vagrant ssh-config --host melody >> ~ / .ssh / config
    
$ ssh melody

ssh 로그인시 설정 확인

$ vagrant ssh-config

status 확인

$ vagrant status
Current machine states :
default running (virtualbox)
(가상 머신 이름) (status)

가상 머신의 정지

$ vagrant halt

가상 컴퓨터 삭제

$ vagrant destroy

가상 머신 내보내기

$ vagrant package
  • package.box라는 Box 파일이 생성
이것을 배포 받는 쪽에서는 다음을 입력한다.
$ vagrant box add new_box package.box