run_timer_softrq, run_timers(), call_timer, collect expired timers()
1단계: 동적 타이머 초기화
2단계: 동적 타이머 등록
3단계: 동적 타이머 실행
#define timer_setup(timer, callback, flags)
__init_timer((timer), (callback), (flags))
void timer_setup(struct timer_list *timer, void *func, unsigned int flags);
struct timer_list {
struct hlist_node entry;
unsigned long expires; // 지피스 만료 시각이 1700이면 1701에 동적 타이머 핸들러 실행
void (*function)(struct timer_list *);
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
int bcm2835_sdhost_add_host(struct bcm2835_host *host)
{
struct mmc_host *mmc;
...
tasklet_init(&host->finish_tasklet,
bcm2835_sdhost_tasklet_finish, (unsigned long)host);
INIT_WORK(&host->cmd_wait_wq, bcm2835_sdhost_cmd_wait_work);
timer_setup(&host->timer, bcm2835_sdhost_timeout, 0);
crash64> timer
JIFFIES
4295945208 // 지피스값
TIMER_BASES[0][BASE_STD]: ffffff80fb779d00
EXPIRES TTE TIMER_LIST FUNCTION
4295945270 62 ffffff8042042910 ffffffd174aa8c00 <input_repeat_key> // 만료되지않은 동적타이머 정보, 5271이 되면 만료된다.
kdt_crash> struct timer_list ffffff8042042910
struct timer_list {
entry = {
next = 0x0,
pprev = 0xffffff80fb779fa8
},
expires = 4295945270, // 지피스값 271이 되면 동적 타이머 호출
function = 0xffffffd174aa8c00 <input_repeat_key>, // 동적타이머 핸들러 함수
flags = 297795584
}
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/time/timer.c
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
__mod_timer(timer, timer->expires, MOD_TIMER_NOTPENDING);
}
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/time/timer.c
int mod_timer(struct timer_list *timer, unsigned long expires)
{
return __mod_timer(timer, expires, 0);
}
EXPORT_SYMBOL(mod_timer);
타이머 인터럽트 처리
raise_softirq_irqoff
raise_softirq
update_process_times
tick_sched_handle
tick_sched_timer
__hrtimer_run_queues
hrtimer_interrupt
arch_timer_handler_phys
handle_percpu_devid_irq
generic_handle_domain_irq
gic_handle_irq
call_on_irq_stack
do_interrupt_handler
el1_interrupt
el1h_64_irq_handler
el1h_64_irq // 인터럽트
SOFT IRQ 서비스 실행
run_timer_softirq
do_softirq
__do_softirq
call_on_irq_stack+0x
do_softirq_own_stack
irq_exit_rcu
동적 타이머 핸들러 호출
call_timer_fn
expire_timers
run_timer_softirq() 함수는 1초에 HZ 만큼 실행
만료될 동적 타이머가 있는지 체크한 후 동적 타이머 핸들러 호출
__run_timers() 함수
동적 타이머의 만료 조건을 체크하고 예외 처리 수행
expire_timers() 함수를 호출
collect_expired_timers() 함수
__collect_expired_timers() 함수 호출
__collect_expired_timers() 함수
현재 커널 타이머 해시 테이블에 등록된 동적 타이머 개수를 반환
expire_timers() 함수
만료된 동적 타이머의 timer_list 구조체 주소를 로딩해 call_timer_fn() 함수 호출
call_timer_fn() 함수
동적 타이머 핸들러 함수를 직접 호출
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/time/timer.c
static void call_timer_fn(struct timer_list *timer,
void (*fn)(struct timer_list *),
unsigned long baseclk)
{
int count = preempt_count();
...
trace_timer_expire_entry(timer, baseclk);
fn(timer); // 포인터를 처리하는 구문 fn은 동적타이머 핸들러의 주소를 담고있음
trace_timer_expire_exit(timer);
kworker/u9:2-807 [000] d..1. 1215.509638: timer_start: timer=000000002a7014b5
function=brcmf_sdio_watchdog [brcmfmac] expires=4295196172 [timeout=3] cpu=0 idx=13 // 만료시간 172ㅡ 173되는 순간 동적 타이머 실행 flags=
...
<idle>-0 [000] d.s2. 1215.525068: timer_cancel: timer=000000002a7014b5
<idle>-0 [000] ..s1. 1215.525070: timer_expire_entry: timer=000000002a7014b5 function=brcmf_sdio_watchdog
[brcmfmac] now=4295196173 baseclk=4295196173 // 173이면 동적 타이머가 실행이 됨
<idle>-0 [000] ..s1. 1215.525072: brcmf_sdio_watchdog+0x4/0x70 [brcmfmac] <-call_timer_fn+0x38/0x1d0
<idle>-0 [000] ..s1. 1215.525074: <stack trace> // 동적타이머 실행시 스택 트레이스
=> brcmf_sdio_watchdog+0x8/0x70 [brcmfmac]
=> call_timer_fn+0x38/0x1d0
=> run_timer_softirq+0x284/0x520
=> __do_softirq+0x1a0/0x4d8
=> ____do_softirq+0x18/0x28
=> call_on_irq_stack+0x2c/0x54
=> do_softirq_own_stack+0x24/0x30
…
<idle>-0 [000] ..s1. 1215.525081: timer_expire_exit: timer=000000002a7014b5