Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[라즈베리파이] Soft IRQ 소개 [라즈베리파이]인터럽트후반부

Soft IRQ는 리눅스 커널 시스템 핵심 기능 중 하나입니다. 리눅스 커널의 타이머, 스케쥴링은 물론 네트워크 시스템과 연관돼 있죠. 그만큼 Soft IRQ는 구조가 복잡해서 전체적인 큰 그림을 그리며 개념을 익히기 힘듭니다.

하지만 Soft IRQ 핵심 개념은 뭐니해도 인터럽트 후반부 처리라고 할 수 있습니다. 그래서 이번 절에서는 Soft IRQ를 인터럽트 후반부 처리 중심으로 알아볼 예정입니다. 이 개념을 정확히 익힌 다음 Soft IRQ에서 프로세스, 타이머 혹은 네트워크 시스템을 어떻게 처리하는지 알아보는 것이 좋습니다.  

Soft IRQ에 대해 알아보기 전에 우선 Soft IRQ를 왜 알아야 하는지 생각해 봅시다.
1. 리눅스 커널 입문자를 벗어나 중급 수준 개발자가 되려면 Soft IRQ가 뭔지 알아야 합니다. 반응 속도에 민감한 네트워크 패킷 처리나 고속 그래픽 처리 및 스토리지(UFS) 드라이버들은 Soft IRQ 핸들러를 이용해서 구현됐기 때문입니다.

2. 우리는 인터럽트가 발생하면 일하던 프로세스를 멈추고 인터럽트 핸들러를 실행한다고 알고 있습니다. 그런데 Soft IRQ는 인터럽트 핸들러가 수행하면 일하던 프로세스로 돌아가지 않고 바로 Soft IRQ 실행을 시작합니다. Soft IRQ 에서 실행 속도가 늦으면 시스템 반응 속도가 늦어집니다. 그래서 시스템 전반을 책임지는 개발자는 Soft IRQ를 잘 알아야 합니다.

3. 커널 타이머를 제대로 이해하려면 Soft IRQ 구조를 알아야 합니다. 드라이버에서 요청한 로컬 타이머들은 타이머 인터럽트가 발생한 다음 Soft IRQ 서비스로 실행하기 때문입니다.

Soft IRQ를 IRQ Thread 기법과 비교하면 어떤 차이점이 있을까요? IRQ Thread 기법은 인터럽트 후반부 처리를 해야 할 때 IRQ Thread를 깨우고 조금 후 프로세스 레벨에서 IRQ Thread 핸들러에서 남은 인터럽트에 대한 처리를 하는 구조입니다. 그래서 인터럽트 후반부를 처리하는 코드 설계를 할 때 아주 많은 고민을 할 필요는 없습니다. 다음 그림은 IRQ Thread 전체 흐름도인데 “irq=92” mmc1인터럽트가 발생했을 때 이 인터럽트의 후반부를 처리하는 “irq/92-mmc1” IRQ Thread가 실행하는 과정을 나타냅니다.


Soft IRQ는 IRQ Thread 기법보다 구조가 더 복잡합니다. 또한 Soft IRQ는 IRQ Thread에 비해 훨씬 반응 속도에 민감한 리눅스 커널 시스템에서 사용합니다. 10개 Soft IRQ를 서비스를 인터럽트 핸들러에서 요청하면 프로세스 레벨에서 Soft IRQ 서비스에서만 실행하는 것이 아닙니다. Soft IRQ 전체 구조를 알아보기 전에 잠깐 Soft IRQ 관련 용어를 소개할게요.

Soft IRQ 서비스
우선 Soft IRQ 서비스란 용어를 소개합니다. 리눅스 커널에서는 다음과 같이 10가지 Soft IRQ 서비스를 정의해 놨는데, 부팅할 때 open_softirq 란 함수를 써서 등록합니다.
[include/linux/interrupt.h]
const char * const softirq_to_name[NR_SOFTIRQS] = {
"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL",
"TASKLET", "SCHED", "HRTIMER", "RCU"
};

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
};

Soft IRQ 서비스 핸들러
이번에는 Soft IRQ 서비스 핸들러입니다. 각 Soft IRQ 서비스를 실행할 함수입니다. 부팅 과정에서 open_softirq 함수로 Soft IRQ 서비스를 등록할 때 softirq_vec이란 전역 변수에 등록합니다. 다음 softirq_vec 전역 변수는 Trace32로 라즈베리안에 등록된 Soft IRQ 서비스 핸들러 정보를 확인한 것입니다. 이 함수들은 __do_softirq 함수에서 호출됩니다. 
(static struct softirq_action [10]) [D:0x80C02080] softirq_vec = (
    [0] = ((void (*)()) [D:0x80C02080] action = 0x80122888 = tasklet_hi_action),
    [1] = ((void (*)()) [D:0x80C02084] action = 0x80181270 = run_timer_softirq),
    [2] = ((void (*)()) [D:0x80C02088] action = 0x80614684 = net_tx_action),
    [3] = ((void (*)()) [D:0x80C0208C] action = 0x80615AB0 = net_rx_action),
    [4] = ((void (*)()) [D:0x80C02090] action = 0x804279B0 = blk_done_softirq),
    [5] = ((void (*)()) [D:0x80C02094] action = 0x0 = ),
    [6] = ((void (*)()) [D:0x80C02098] action = 0x8012299C = tasklet_action),
    [7] = ((void (*)()) [D:0x80C0209C] action = 0x801588EC = run_rebalance_domains),
    [8] = ((void (*)()) [D:0x80C020A0] action = 0x0 = ),
    [9] = ((void (*)()) [D:0x80C020A4] action = 0x8017ABC4 = rcu_process_callbacks))

Soft IRQ 서비스 요청
Soft IRQ 서비스 요청은 raise_softirq나 raise_softirq_irqoff 함수로 Soft IRQ 서비스 요청을 합니다. 이 때 요청할 Soft IRQ 서비스 아이디를 지정해야 합니다.
이제 Soft IRQ 전체 구조를 확인해 볼까요?

위 그림과 같이 Soft IRQ 후반부 처리는 4단계로 분류할 수 있습니다. 각 단계별로 어떤 동작을 하는지 확인해 봅시다.

[1] 단계
인터럽트가 발생하면 해당 인터럽트 핸들러에서 Soft IRQ 서비스를 요청합니다. 이를 위해  raise_softirq_irqoff 함수를 호출해야 합니다. 이 동작은 인터럽트 핸들러에서 IRQ_WAKE_THREAD를 리턴하는 동작과 유사합니다. 각 함수 호출 순서는 번호 순서이니 참고하세요.
11 raise_softirq_irqoff
10 Interrupt handler
9 __handle_irq_event_percpu
8 handle_irq_event
7 handle_level_irq
6 generic_handle_irq
5 bcm2836_chained_handle_irq
4 generic_handle_irq
3 __handle_domain_irq
2 bcm2836_arm_irqchip_handle_irq
1 __irq_svc

[2] 단계
인터럽트 서비스 루틴 동작이 끝나면 irq_exit이란 함수를 호출합니다. 여기서 Soft IRQ 서비스 요청 여부를 점검하고 요청한 Soft IRQ 서비스가 있으면 __do_softirq 함수에서 해당 Soft IRQ 핸들러를 호출합니다. 만약 Soft IRQ 서비스 요청이 없으면 irq_exit는 일을 끝냅니다. 
4 Soft IRQ Handler
3 __do_softirq
2 invoke_softirq
1 irq_exit

[3] 단계
__do_softirq 함수에서 Soft IRQ 서비스 핸들러를 호출하다가 2ms 이상 시간이 걸리거나 10번 이상 Soft IRQ 핸들러를 호출하면Soft IRQ 서비스 수행을 도중에 마치고 wakeup_softirqd 함수를 호출해서 ksoftirqd 프로세스를 깨웁니다. 프로세스 레벨로 인터럽트 후반부 처리를 하기 위해서 입니다. __do_softirq 함수 실행 시간에 제약을 건 이유는 __do_softirq 함수를 호출하는 irq_exit 함수가 프로세스가 일을 멈춘 상태에서 실행하기 때문입니다.

[4] 단계
ksoftirqd 프로세스가 깨어나 [3] 단계에서 마무리 못 한 Soft IRQ 서비스를 실행합니다.
6 Soft IRQ Handler
5 __do_softirq
4 run_ksoftirqd
3 smpboot_thread_fn
2 kthread
1 ret_from_fork

Soft IRQ 처리 과정을 알아보니 IRQ Thread 구조보다 인터럽트 후반부를 처리하는 과정이 더 복잡합니다. IRQ Thread는 인터럽트 핸들러에서 못한 일을 프로세스 레벨에서 실행합니다. 그런데 Soft IRQ서비스는 인터럽트 핸들러가 수행된 후 바로 일을 시작합니다. 그러니 Soft IRQ 서비스 핸들러는 일을 빨리 끝내야겠죠.

조금 어려운 개념이지만 코드 한줄 한줄 분석하며 라즈베리파이에서 Soft IRQ가 어떻게 동작하는지 확인하면 더 쉽게 이해할 수 있습니다. 이제 Soft IRQ 서비스를 어떻게 등록하는지 알아볼까요?



핑백

덧글

댓글 입력 영역