[bottom half] softirq 등록과 처리과정
1. softirq란?
2. softirq 핸들러 등록과정
softirq는 커널 버전 4.13 기준으로 총 10개가 존재한다.
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
NR_SOFTIRQS
};
softirq 등록은 open_softirq()을 호출해서 처리한다.
<kernel/softirq.c>
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
위 함수를 통해 각각의 softirq에 등록된 핸들러 정보는 다음과 같다.
block/blk-softirq.c <<blk_softirq_init>>
open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);
kernel/rcu/tiny.c <<rcu_init>>
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
kernel/rcu/tree.c <<rcu_init>>
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
kernel/sched/fair.c <<init_sched_fair_class>>
open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);
kernel/softirq.c <<softirq_init>>
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
kernel/softirq.c <<softirq_init>>
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
kernel/time/timer.c <<init_timers>>
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
lib/irq_poll.c <<irq_poll_setup>>
open_softirq(IRQ_POLL_SOFTIRQ, irq_poll_softirq);
net/core/dev.c <<net_dev_init>>
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
net/core/dev.c <<net_dev_init>>
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
이렇게 등록된 핸들러는 softirq 처리가 필요할 때 호출된다. pending softirq라고 하며 아래와
3. pending softirq 설정
4. softirq 처리과정
인터럽트 처리를 위해 호출되는 함수는 handle_domain_irq()이다. 이 함수는 바로 __handle_domain_irq()을 호출하도록 구현되어 있다. 올바른 인터럽트 번호라면 결국 generic_handle_irq()을 호출해서 등록된 인터럽트 핸들러를 호출하게 된다. softirq는 호출된 인터럽트 핸들러가 종료된 이후 호출되는 irq_exit()에서 처리될 수가 있다.
<kernel/softirq.c> - irq_exit()
/*
* Exit an interrupt context. Process softirqs if needed and possible:
*/
void irq_exit(void)
{
...
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
invoke_softirq()는 아래 조건을 모두 만족할 경우 호출되어 pending된 softirq를 처리한다.
- NMI,IRQ,SoftIRQ 컨텍스트이거나 BH disabled
- current cpu에 pending된 softirq가 있음
<kernel/softirq.c> - invoke_softirq()
static inline void invoke_softirq(void)
{
if (ksoftirqd_running())
return;
if (!force_irqthreads) {
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
__do_softirq();
#else
do_softirq_own_stack();
#endif
} else {
wakeup_softirqd();
}
}
invoke_softirq()에서는 네 경우로 나눠서 pending된 softirq를 처리한다
- softirqd가 이미 실행중인 경우
- softirqd가 미실행중 && force threading of a irq handler가 활성화 && 인터럽트 스택이 존재
- softirqd가 미실행중 && force threading of a irq handler가 활성화 && 인터럽트 스택이 없음
- softirqd가 미실행중 && force threading of a irq handler가 비활성화
2,3번의 경우는 결국 __do_softirq()을 호출하게 된다.
<kernel/softirq.c> - __do_softirq()
asmlinkage __visible void __softirq_entry __do_softirq(void)
{
pending = local_softirq_pending();
...
while ((softirq_bit = ffs(pending))) {
...
h += softirq_bit - 1;
...
h->action(h);
...
h++;
...
pending >>= softirq_bit;
__do_softirq()에서는 먼저 current cpu의 pending softirq 정보를 구한다. 이 정보는 총 32개의 비트로 구성되어 있다.
<include/linux/irq_cpustat.h>
#define local_softirq_pending() \
__IRQ_STAT(smp_processor_id(), __softirq_pending)
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
while()에서는 비트맵에 설정된 비트 개수만큼 반복해서 해당 pending softirq의 handler를 호출한다(h->action(h)).