커널 분석

[bottom half] softirq 등록과 처리과정

iamyooon 2018. 9. 8. 22:32

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)).