setitimer 手続きの実装を掘削してみる (4)

我慢できず臨時朝練。run_timer_sftirq という手続きについて。
timer.c の init_timers 手続きにてソフト割り込み登録されてます。

        open_softirq(TIMER_SOFTIRQ, run_timer_softirq);

ざっくり探したところでは raise してるのは以下らしい。

./kernel/timer.c:       raise_softirq(TIMER_SOFTIRQ);
./kernel/time/tick-sched.c:     raise_softirq_irqoff(TIMER_SOFTIRQ);

timer.c では run_local_timers 手続き。コールグラフてきに以下なカンジ?

update_process_times -> run_local_timers

update_process_times のコメントが以下。

 * Called from the timer interrupt handler to charge one tick to the current
 * process.  user_tick is 1 if the tick is user time, 0 for system.

タイマ割り込みから云々、なのかな。ただ今 grep 中。なんとなくアタリなのが以下ら辺なのかどうなのか。

./kernel/time/tick-sched.c:      * time we slept as update_process_times does only a 1 tick
./kernel/time/tick-sched.c:     update_process_times(user_mode(regs));
./kernel/time/tick-sched.c:             update_process_times(user_mode(regs));
./kernel/time/tick-common.c:    update_process_times(user_mode(get_irq_regs()));

それぞれ

  • tick-sched.c の tick_nohz_handler 手続き
  • tick-sched.c の tick_sched_timer 手続き
  • tick-common.c の tick_periodic 手続き

というカンジ。なんとなく tick_periodic 手続きが本流な感触です。

話を元に戻します

run_timer_softirq 手続きです。__run_timers 手続きを呼び出してるんですが、そこから割り込みなナニを呼び出している模様。てか、ここでようやく tv2 とか tv3 とかってソレが出てきました。

static inline void __run_timers(struct tvec_base *base)
{
        struct timer_list *timer;

        spin_lock_irq(&base->lock);
        while (time_after_eq(jiffies, base->timer_jiffies)) {
                struct list_head work_list;
                struct list_head *head = &work_list;
                int index = base->timer_jiffies & TVR_MASK;

                /*
                 * Cascade timers:
                 */
                if (!index &&
                        (!cascade(base, &base->tv2, INDEX(0))) &&
                                (!cascade(base, &base->tv3, INDEX(1))) &&
                                        !cascade(base, &base->tv4, INDEX(2)))
                        cascade(base, &base->tv5, INDEX(3));

ええと、struct tvec_base 型ッスか。定義は kernel/timer.c ってことはこの中に閉じちゃってるんですね。

struct tvec_base {
	spinlock_t lock;
	struct timer_list *running_timer;
	unsigned long timer_jiffies;
	unsigned long next_timer;
	unsigned long active_timers;
	struct tvec_root tv1;
	struct tvec tv2;
	struct tvec tv3;
	struct tvec tv4;
	struct tvec tv5;
} ____cacheline_aligned;

struct tvec 型も直上で定義されてます。

struct tvec {
	struct list_head vec[TVN_SIZE];
};

うーん、参考書持ってくるの忘れた。

もう少し

setitimer 手続きの実装を掘削してみる (3) でタイマ割り込みの根拠の部分までは行きついてますね
ただ、global_clock_event な抽象化で足が止まっているのか。で、結局ここが分からないので、TIMER_SOFTIRQ を raise しているはずの update_process_times がどこから呼ばれてるかが分からない??
とゆーことで再度 grep 祭りがアレ。
global_clock_event な出力が以下 (一部のみ)。

./System.map:ffffffff81ddb448 B global_clock_event
./arch/x86/include/asm/time.h:extern struct clock_event_device *global_clock_event;
./arch/x86/kernel/hpet.c:       global_clock_event = &hpet_clockevent;
./arch/x86/kernel/apb_timer.c:          global_clock_event = &adev->timer->ced;
./arch/x86/kernel/apb_timer.c:                 global_clock_event->name);
./arch/x86/kernel/i8253.c:struct clock_event_device *global_clock_event;
./arch/x86/kernel/i8253.c:      global_clock_event = &i8253_clockevent;
./arch/x86/kernel/apic/apic.c:  real_handler = global_clock_event->event_handler;
./arch/x86/kernel/apic/apic.c:  global_clock_event->event_handler = lapic_cal_handler;
./arch/x86/kernel/apic/apic.c:  global_clock_event->event_handler = real_handler;
./arch/x86/kernel/time.c:       global_clock_event->event_handler(global_clock_event);

当り前ですが gtags で見える以上のものは無い。そして update_process_times な出力が以下 (これもバイナリは除去)。

./include/linux/sched.h:extern void update_process_times(int user);
./.tmp_System.map:ffffffff81067f70 T update_process_times
./System.map:ffffffff81067f70 T update_process_times
./kernel/timer.c:void update_process_times(int user_tick)
./kernel/time/tick-sched.c:      * time we slept as update_process_times does only a 1 tick
./kernel/time/tick-sched.c:     update_process_times(user_mode(regs));
./kernel/time/tick-sched.c:             update_process_times(user_mode(regs));
./kernel/time/tick-common.c:    update_process_times(user_mode(get_irq_regs()));
./arch/cris/arch-v10/kernel/time.c:     update_process_times(user_mode(regs));
./arch/cris/arch-v32/kernel/time.c:     update_process_times(user_mode(regs));
./arch/alpha/kernel/smp.c:              update_process_times(user);
./arch/alpha/kernel/time.c:             update_process_times(user_mode(get_irq_regs()));
./arch/frv/kernel/time.c:       update_process_times(user_mode(get_irq_regs()));
./arch/ia64/kernel/time.c:              update_process_times(user_mode(get_irq_regs()));
./arch/parisc/kernel/time.c:            update_process_times(user_mode(get_irq_regs()));
./arch/blackfin/kernel/time.c:  update_process_times(user_mode(get_irq_regs()));
./arch/m68k/sun3/sun3ints.c:    update_process_times(user_mode(get_irq_regs()));
./arch/m68k/kernel/time.c:      update_process_times(user_mode(get_irq_regs()));
./arch/xtensa/kernel/time.c:            update_process_times(user_mode(get_irq_regs()));
./arch/openrisc/kernel/time.c:   * update_process_times() expects us to have called irq_enter().
./arch/h8300/kernel/time.c:     update_process_times(user_mode(get_irq_regs()));
./arch/mn10300/kernel/time.c:   update_process_times(user_mode(get_irq_regs()));
./arch/x86/kernel/apic/apic.c:   * update_process_times() expects us to have done irq_enter().
./arch/arm/kernel/time.c:       update_process_times(user_mode(get_irq_regs()));
./arch/m32r/kernel/smp.c:               update_process_times(user);
./arch/m32r/kernel/time.c:      update_process_times(user_mode(get_irq_regs()));
./Documentation/timers/highres.txt:      - cpu local update_process_times
./Documentation/timers/highres.txt:and calls update_process_times and profiling. The implementation of the hrtimer

とりあえず update_process_times 手続きはタイマ割り込みから呼び出される、ってことにしときます。あ、あと上の grep 出力に出てきている Documentation/timers/highres.txt も確認の方向です。