100 likes | 359 Views
Irq 가 disable 되어 있는지 확인하고 이미 enable 되어있다면 “start_kernel(): bug: interrupts were enabled *very* early, fixing it” 을 출력하고 local_irq_disable 함수를 호출하여 인터럽트를 disable 시킨다. init/main.c. if (!irqs_disabled()).
E N D
Irq 가 disable 되어 있는지 확인하고 이미 enable 되어있다면 “start_kernel(): bug: interrupts were enabled *very* early, fixing it” 을 출력하고 local_irq_disable 함수를 호출하여 인터럽트를 disable 시킨다. init/main.c if (!irqs_disabled()) CONFIG_MMU 가 정의 될경우 __ex_table 섹션에 저장된 주소(어떤 명령어의 주소가 담겨있음) 를 기준으로 정렬한다. CONFIG_MMU 가 정의 안되어있을경우 의미 없음.- 예) copy_from_user.S 에서 섹션 정의 하는 부분 찾을수 있음. sort_main_extable() ARM 에서는 아무 것도 하지 않음. trap_init rcu_barrier_cpu_hotplug에 대한 notifier block 을 하나 만들고 해당 block에 rcu_barrier_cpu_hotplug 함수를 등록 하고 priority를 0으로 셋팅한다. 마지막으로 notifier block을 cpu_chain의 알맞은 위치에 추가한다. rcu_init __rcu_init rcu_online_cpu를 호출해서 해당 cpu의 rcu자료구조를 초기화하고 RCU_SOFTIRQ에 대한 콜백함수를 등록한다. 마지막으로 cpu_chain의 알맞은 위치에 notifier block 인 nb를 추가 한다. hotcpu_notifier early_irq_init
Memory Types Todo: RCU 개념 정리
전역 변수인 struct irq_desc irq_desc{[NR_IRQS] 를 초기화 하는 작업을 한다. 전역 변수 irq_default_affinity 에 메모리를 할당 하고 nr_cpu_ids-1 만큼의 비트를 모두 set 한다. irq_desc 를 ARRAY_SIZE 만큼 순회 하면서 멤버변수 affinity 에 필요한 메모리를 할당 받고 kstat_irqs 를 이미 정적으로 선언한 kstat_irqs_all 로 설정 한다. init/main.c early_irq_init irq_desc 를 NR_IRQ 만큼순회하면서 status 를 IRQ_NOREQUEST IRQ_NOPROBE 로 설정하고 CONFIG_SMP 가 설정 되어 있을 경우 bad_irq_desc.affnity 를 nr_cpu_ids-1 비트만큼 set 한다. 그리고 setup_arch 에서 mdesc->init_irq 가 설정된 init_arch_irq 를 호출 하여 각 해당 플랫폼에 맞는 인터럽트 초기화 과정을 거친다. init_IRQ init_arch_irq 인터럽트 컨트롤러를 초기화하고 각 irq에 해당 하는 irq_desc[irq] 의 멤버 변수 handle_irq (핸들러 함수) 를 플랫폼에 맞는 함수로 설정 한다. s3c6410 의 경우 s3c6410_init_irq To Do 하드웨어 의존적이기는 하나 좀더 자세한 분석 필요. pid_hash_init
irq_desc 구조체와 irq_desc[NR_IRQS]_ 정적 구조체 배열 include/linux/irq.h kernel/handle.c struct irq_desc { unsigned int irq; struct timer_rand_state *timer_rand_state; unsigned int *kstat_irqs; #ifdef CONFIG_INTR_REMAP struct irq_2_iommu *irq_2_iommu; #endif irq_flow_handler_t handle_irq; struct irq_chip *chip; struct msi_desc *msi_desc; void *handler_data; void *chip_data; struct irqaction *action; /* IRQ action list */ unsigned int status; /* IRQ status */ unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ unsigned int irq_count; /* For detecting broken IRQs */ unsigned long last_unhandled; /* Aging timer for unhandled count */ unsigned int irqs_unhandled; spinlock_t lock; #ifdef CONFIG_SMP cpumask_var_t affinity; unsigned int cpu; #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_var_t pending_mask; #endif #endif atomic_t threads_active; wait_queue_head_t wait_for_threads; #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; #endif const char *name; } ____cacheline_internodealigned_in_smp; struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { [0 ... NR_IRQS-1] = { .status = IRQ_DISABLED, .chip = &no_irq_chip, .handle_irq = handle_bad_irq, .depth = 1, .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock), } }; early_irq_init 함수와 init_IRQ 함수에서 참조되는 구조체 이다. 인터럽트 처리를 위해 중요한 자료 구조 이다.
1.커널 페이지의 크기를 megabyte단위로 구함 2. 시스템의 메모리를 기준으로 해쉬테이블의 slot 개수를 16~4096개로 설정한다. 1기가 바이트 이상이면 해쉬테이블의 slot 을 4096 개로 설정. 3. 해쉬테이블을 위해 메모리를 할당하고 각 엔트리의 first 멤버들을 NULL 로 초기화 init/main.c pid_hash_init 4가지의 action 타입에 따라 처리하는 일이 달라진다. init_timers CPU_UP_PREFARE: CPU_UPPREFARE_FROZEN: CPU_DEAD: CPU_DEAD_FROZEN: timer_cpu_notify register_cpu_notifier init_timer_cpu migrate_timers struct tvec_base { spinlock_t lock; struct timer_list *running_timer; unsigned long timer_jiffies; // 타이머에 등록된 핸들러는 타이머 간격에 따라 // 시간별로 분류하여 다섯 개의 타이머 벡터에 // 분할해서 등록한다 tv1는 타이머 간격이 1초 // 이내인, tv2는 1분 이내인 핸들러가 등록되는 // 방식이다 struct tvec_root tv1; // 약 1초 struct tvec tv2; // 약 1분 struct tvec tv3; // 약 1시간 struct tvec tv4; // 약 3일 struct tvec tv5; // 약 199일 } ____cacheline_aligned; boot_done을 1로 초기화하고 전역 으로 선언된 struct tvec_base boot_tvec_bases 의 timer_jiffies 값을 시스템의 jiffies 값으로 설정하고 구조체내의 값들을 초기화함.boot time이 아닐 때 호출된 경우에는 base를 할당 받아서 동일 과정을 처리한다. 주어진 cpu의 tvec_base를 현재 cpu의 tvec_base로 migration
init/main.c init_timers timer_cpu_notify init_timer_cpu migrate_timers static struct notifier_block __cpuinitdata timers_nb = { .notifier_call = timer_cpu_notify, }; 전역으로 선언된 위 구조체 timers_nb를 notify_block 의 헤더인 cpu_chain 에 추가한다. register_cpu_notifier open_softirq 전역 자료구조 softirq_vec 배열에서 TIMER_SOFTIRQ 번째 엔트리 값의 action 변수를 runtimer_softirq로 설정한다. TIMER_SOFTIRQ 발생시 설정한 함수가 호출 될 것이다. include/linux/interrupt.h struct softirq_action { void (*action)(struct softirq_action *); }; kernel/softirq.c struct softirq_action softirq_vec[NR_SOFTIRQS] ; hrtimers_init
init/main.c 6가지의 action 타입에 따라 처리하는 일이 달라진다. init_hrtimers CPU_UP_PREFARE: CPU_UPPREFARE_FROZEN: CPU_DYING: : CPU_DYING_FROZEN: CPU_DEAD: CPU_DEAD_FROZEN: hrtimer_cpu_notify 2개의 clock base(CLOCK_REALTIME과 CLOCK_MONOTONIC)를 초기화 high resolution mode를 비활성화, 만료시간 설정 CLOCK_REALTIME:현재 시간을 기준으로 하여 시간을 계산(시스템 시간 변경 시 영향을 받음) CLOCK_MONOTONIC:커널이 동작한 시간을 기준으로 하여 시간을 계산(외부의 영향을 받지 않음) init_hrtimer_cpu clockevents_notify clockevents_notify checkevent_chain에 등록된 모든 콜백함수를 호출한다 그 중 tick_notify()는 reason이 CLOCK_EVT_NOTIFY_CPU_DYING일 경우 tick_handover_do_timer()를 호출한다. 이 함수는 tick_do_timer_cpu변수를 세팅하여 online되어 있는 가장첫번째 cpu가 jiffies값을 업데이트하도록 한다 (jiffies 업데이트를 담당하던 cpu가 죽으면 다른 cpu가 맡도록 한다) migrate_hrtimers sched_timer가 수행이 끝날 때까지 기다렸다가 rbtree에서 삭제하고 상태를 inactive로 변경한다 그리고 expired time을 재설정한다 timer의 state를 Migrate로 바꾸고 base를 new_base로 바꾼다 base를 바꾸면 타이머를 처리할 cpu가 바뀐다 timer를 rb tree에 추가하고 state에 HRTIMER_STATE_ENQUEUED를 추가한다 state에서 migrate를 제거한다 rb-tree에서 soft expired된 timer들의 노드를 삭제하고 타이머의 이벤트핸들러를 호출해서 처리한다 register_cpu_notifier
init/main.c init_hrtimers hrtimer_cpu_notify init_hrtimer_cpu clockevents_notify clockevents_notify register_cpu_notifier migrate_hrtimers static struct notifier_block __cpuinitdata timers_nb = { .notifier_call = hrtimer_cpu_notify, }; 전역으로 선언된 위 구조체 hrtimers_nb를 notify_block 의 헤더인 cpu_chain 에 추가한다. open_softirq HRTIMER_SOFTIRQ가 raise되면 run_hrtimer_softirq를 호출하도록 등록한다 softirq_init
init/main.c 해당 CPU의 tasklet_vec,tasklet_hi_vec, softirq_work_list를 초기화 cpu_chain의 알맞은 위치에 remote_softirq_cpu_notifier를 추가한다. 각각 해당 softirq발생시 호출될 콜백함수를 등록한다 softirq_init NTP 상태 변수를 초기화하고 leap_timer가 현재시간을 기준으로 시간을 계산하는 CLOCK_REALTIME에 기반하여 절대시간을 사용하도록 초기화한다 timekeeping_init ntp_init clocksource_register()를 호출하여 next_clocksource를 초기화하기 전까지는 계속 clocksource_jiffies를 리턴한다 clocksource_get_next clocksource_enable clock_source가 jiffies일 경우 이미 jffies값이 동작하고 있으므로 enable할 필요가 없다. clocksource_calculate_interval NTP_INTERVAL_LENGTH를 이용해서 clock의 멤버변수인 xtime_interval, low_interval, cycle_interval을 세팅한다. NTP_INTERVAL_LENGTH -> jiffies값이 1증가될 때 걸리는 nano sec단위 값 set_normalized_timespec time_init wall_to_monotonic은시스템부팅완료시에 0이 된다. 현재는 부팅중이므로 음수값을 가진다 . 세번째 인자가 음수가 넘겨지더라도 wall_to_monotonic의 멤버변수인 tv_nsec은 양수값을 가진다
init/main.c time_init setup_arch 에서 system_timer 에 설정한 함수를 호출한다. 각 플랫폼 마다 다르게 정의 되어있다. hardware timer 클럭 초기화 하고 인터럽트를 등록한다. s3c6410 의 경우 s3c2410_timer_init To Do 하드웨어 의존적이기는 하나 좀더 자세한 분석 필요. sched_clock_init