Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[라즈베리파이] Soft IRQ 서비스 및 서비스 핸들러 등록(코드 분석) [라즈베리파이]인터럽트후반부

Soft IRQ를 설명하면서 낯선 용어를 설명했는데, 이제부터 그 의미를 하나하나씩 살펴 보겠습니다. Soft IRQ 서비스란 용어부터 배워볼까요?

Soft IRQ 서비스를 빨리 이해하려면 코드를 먼저 봐야 합니다. Soft IRQ 서비스는 아래 enum으로 정의한 코드와 같습니다.
[include/linux/interrupt.h]
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
};

const char * const softirq_to_name[NR_SOFTIRQS] = {
"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL",
"TASKLET", "SCHED", "HRTIMER", "RCU"
};

리눅스 커널에서는 Soft IRQ 서비스 종류를 지정해 놨습니다. 위 코드를 보면 10개나 되네요. enum 타입으로 정의된 이름으로 타이머, 네트워크 그리고 블록 디바이스별로 Soft IRQ 서비스가 있습니다.

그럼 Soft IRQ 서비스는 어떻게 실행할까요? 각 Soft IRQ 서비스별로 등록된 서비스 핸들러 함수가 실행할 때 Soft IRQ 서비스를 실행하는 겁니다. Soft IRQ 서비스 핸들러는 어디서 확인할 수 있을까요? softirq_vec 변수가 Soft IRQ 서비스 핸들러 정보를 저장하고 있습니다.

다음은 ARM64 아키텍처 기반의 다른 리눅스 시스템에서 확인한 softirq_vec 전역 변수입니다.
Trace32 프로그램으로 확인한 결과인데, softirq_vec 변수는 Soft IRQ 서비스 핸들러 함수 주소를 저장하고 있습니다.
v.v %t %y %i %d softirq_vec
  (static struct softirq_action [10]) softirq_vec = (
    [0] = ((void (*)()) action = 0xFFFFFFC0000AB7F0 = tasklet_hi_action),
    [1] = ((void (*)()) action = 0xFFFFFFC00010DA9C = run_timer_softirq),
    [2] = ((void (*)()) action = 0xFFFFFFC000CFF084 = net_tx_action),
    [3] = ((void (*)()) action = 0xFFFFFFC000D022D8 = net_rx_action),
    [4] = ((void (*)()) action = 0xFFFFFFC0003190B8 = blk_done_softirq),
    [5] = ((void (*)()) action = 0xFFFFFFC0003197EC = blk_iopoll_softirq),
    [6] = ((void (*)()) action = 0xFFFFFFC0000AB938 = tasklet_action),
    [7] = ((void (*)()) action = 0xFFFFFFC0000E6CEC = run_rebalance_domains),
    [8] = ((void (*)()) action = 0xFFFFFFC0001104D8 = run_hrtimer_softirq),
    [9] = ((void (*)()) action = 0xFFFFFFC000106FB0 = rcu_process_callbacks))

Soft IRQ가 실행될 때 __do_softirq 함수에서 위에서 보이는 함수들 하나가 실행됩니다.
다음에 라즈베리안에서는 Soft IRQ 서비스는 어떤 코드에서 등록하는지 알아볼까요?

Soft IRQ 서비스 핸들러는 언제 등록할까?
Soft IRQ 서비스 핸들러 등록과 Soft IRQ 서비스 등록은 같은 의미입니다. 왜냐면 Soft IRQ 서비스 등록을 할 때 Soft IRQ 핸들러 함수를 등록해야 하기 때문입니다.

Soft IRQ 서비스 핸들러를 등록하려면 다음 규칙에 따라 open_softirq 함수를 호출해야 합니다. 
open_softirq(Soft IRQ 서비스 아이디, Soft IRQ 서비스 핸들러);

우선 TIMER_SOFTIRQ이란 Soft IRQ 서비스 아이디로 Soft IRQ 서비스 핸들러를 등록하는 코드를 살펴 보겠습니다.
1 void __init init_timers(void)
2 {
3 init_timer_cpus();
4 open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
5 }

위 코드를 보면 init_timer 함수에서 open_softirq 함수를 호출하는데, 첫 번째 인자는 Soft IRQ 서비스 아이디, 두 번째는 run_timer_softirq 함수를 지정합니다. 참고로, init_timers 함수 선언부를 보면 __init 매크로가 붙어 있으니 부팅 도중 한번 실행됩니다.

open_softirq 함수를 보면 예상한 대로 Soft IRQ 서비스 핸들러 정보를 저장하는 softirq_vec에 함수 주소를 지정합니다.
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}

리눅스 커널에서는 softirq_vec 전역 변수를 다른 말로 Soft IRQ 벡터라 말하는데 이 전역 변수는 Soft IRQ 서비스 핸들러 함수 포인터를 담고 있습니다. softirq_vec 전역 변수의 선언부를 보면 NR_SOFTIRQS 크기만큼 배열이란 정보를 알 수 있습니다.
[/kernel/softirq.c]
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

이렇게 각 Soft IRQ 서비스 아이디 별로 Soft IRQ 서비스를 등록하면 softirq_vec[nr].action 멤버에 Soft IRQ 서비스 핸들러를 볼 수 있습니다.

조금 후 살펴볼 예정이지만, Soft IRQ 서비스 핸들러는 다음 코드와 같이 __do_softirq 함수에서 호출합니다. TIMER_SOFTIRQ Soft IRQ 서비스 아이디인 경우 __do_softirq 함수에서 run_timer_softirq 함수를 호출합니다.
1 asmlinkage __visible void __softirq_entry __do_softirq(void) {
2 struct softirq_action *h;
3 h = softirq_vec;
4 h->action(h);

위에 보이는 코드는 __do_softirq 함수에서 Soft IRQ 벡터인 softirq_vec 변수를 읽어서 Soft IRQ 핸들러 함수를 호출하는 코드만 가져온 것입니다. 위 4번째 줄 코드에서 Soft IRQ 핸들러 함수를 호출합니다.

여기까지 Soft IRQ 서비스를 등록하는 방법을 알아봤습니다. Soft IRQ 서비스 아이디와 Soft IRQ 서비스 핸들러를 open_softirq 함수 인자로 호출하면 되죠.

라즈베리안에서는 Soft IRQ 서비스를 어떻게 등록하는지 알아볼까요? 코드만 봐서 Soft IRQ 서비스 전체 개수를 파악하기는 어렵습니다. 직접 라즈베리파이에서 Soft IRQ 서비스를 어떻게 등록하는지 알아 보겠습니다.



핑백

덧글

댓글 입력 영역