Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


[리눅스커널][SoftIRQ] 라즈베리파이에서 Soft IRQ 서비스 핸들러 등록 과정 파악하기 6. Bottom-Half

6.6.3 라즈베리파이에서 Soft IRQ 서비스 핸들러 등록 과정 파악하기

이번에는 라즈베리파이에서 Soft IRQ 서비스 등록 과정을 커널 로그로 살펴보는 실습을 하겠습니다. 

실습 패치 소개

먼저 패치 코드를 소개합니다.
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 3f3fbc230..b0e75e8d0 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -433,6 +439,8 @@ void __raise_softirq_irqoff(unsigned int nr)
 void open_softirq(int nr, void (*action)(struct softirq_action *))
 {
  softirq_vec[nr].action = action;
+
+ dump_stack();
 }

open_softirq() 함수 3 번째 줄 다음에 dump_stack() 함수를 추가합시다. dump_stack() 이란 함수는 자신을 호출한 콜스택 정보를 알려 줍니다.

위 패치를 적용하고 커널 빌드를 한 다음 라즈베리안에 설치합니다. 이후 라즈베리파이를 재부팅하고 커널 로그를 받읍시다. 커널 로그를 보면 부팅 과정에서 Soft IRQ 서비스를 등록하는 흐름을 확인할 수 있습니다.

커널 로그를 보겠습니다.
[0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.14.39-v7+ #16
[0.000000] Hardware name: BCM2835
[0.000000] (unwind_backtrace) from (show_stack+0x20/0x24)
[0.000000] (show_stack) from (dump_stack+0xc8/0x10c)
[0.000000] (dump_stack) from (open_softirq+0x24/0x28)
[0.000000] (open_softirq) from (init_sched_fair_class+0x24/0x48)
[0.000000] (init_sched_fair_class) from (sched_init+0x3c8/0x41c)
[0.000000] (sched_init) from (start_kernel+0x21c/0x3e0)
[0.000000] (start_kernel) from  (0x807c)

가장 먼저 init_sched_fair_class() 함수에서 SCHED_SOFTIRQ Soft IRQ 서비스 아이디로 run_rebalance_domains() 이란 Soft IRQ 서비스 핸들러 함수를 등록합니다. 

위 로그에서 open_softirq() 함수를 호출하는 코드는 다음과 같습니다.
[kernel/sched/fair.c]
__init void init_sched_fair_class(void)
{
#ifdef CONFIG_SMP
open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);

커널 로그에서 Soft IRQ 전체 서비스 등록 과정 파악하기

이어서 Soft IRQ 서비스 아이디 별로 커널 로그의 콜스택과 이를 실행한 커널 코드를 같이 확인해 봅시다. 

RCU_SOFTIRQ: rcu_process_callbacks
로그 
[0.000000] (open_softirq) from (rcu_init+0x300/0x374)
[0.000000] (rcu_init) from (start_kernel+0x254/0x3e0)
[0.000000] (start_kernel) from [<0000807c>] (0x807c)

코드: [kernel/rcu/tree.c]
void __init rcu_init(void)
{
...
__rcu_init_preempt();
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); 

TIMER_SOFTIRQ: run_timer_softirq
로그 
[0.000000] (open_softirq) from (init_timers+0xa0/0xa4)
[0.000000] (init_timers) from (start_kernel+0x268/0x3e0)
[0.000000] (start_kernel) from [<0000807c>] (0x807c)

코드: [kernel/time/timer.c]
void __init init_timers(void)
{
...
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);

TASKLET_SOFTIRQ, HI_SOFTIRQ Soft IRQ 서비스는 softirq_init 함수에서 초기화합니다
TASKLET_SOFTIRQ: tasklet_action
HI_SOFTIRQ: tasklet_hi_action
로그 
[0.000000] (softirq_init) from [<80b00cc4>] (start_kernel+0x270/0x3e0)
[0.000000] (start_kernel) from [<0000807c>] (0x807c)

[0.000000] (softirq_init) from (start_kernel+0x270/0x3e0)
[0.000000] (start_kernel) from (0x807c)

코드 [kernel/softirq.c]
void __init softirq_init(void)
{
...
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

BLOCK_SOFTIRQ: blk_done_softirq
로그 
[0.059227] (open_softirq) from (blk_softirq_init+0x78/0xac)
[0.059245] (blk_softirq_init) from (do_one_initcall+0x54/0x17c)
[0.059263] (do_one_initcall) from (kernel_init_freeable+0x224/0x2b8)

코드 [block/blk-softirq.c]
static __init int blk_softirq_init(void)
{
...
open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);

NET_TX_SOFTIRQ: net_tx_action
NET_RX_SOFTIRQ: net_rx_action
로그 
[0.070773] (open_softirq) from (net_dev_init+0x1ac/0x214)
[0.070790] (net_dev_init) from (do_one_initcall+0x54/0x17c)
[0.070807] (do_one_initcall) from (kernel_init_freeable+0x224/0x2b8)
[0.070824] (kernel_init_freeable) from (kernel_init+0x18/0x124)
[0.070840] [<8079bfd0>] (kernel_init) from (ret_from_fork+0x14/0x28)

[0.070911] (open_softirq) from (net_dev_init+0x1bc/0x214)
[0.070925] (net_dev_init) from (do_one_initcall+0x54/0x17c)
[0.070940] (do_one_initcall) from (kernel_init_freeable+0x224/0x2b8)
[0.070954] (kernel_init_freeable) from (kernel_init+0x18/0x124)
[0.070968]  (kernel_init) from (ret_from_fork+0x14/0x28)

코드: [net/core/dev.c]
static int __init net_dev_init(void)
{
...
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);

커널 로그에 담겨 있는 콜스택 정보로 라즈베리안이 부팅하는 과정에서 다음 순서로 Soft IRQ 서비스들을 등록한다는 사실을 알게 됐습니다.
Soft IRQ 서비스 아이디  Soft IRQ 서비스 핸들러
SCHED_SOFTIRQ run_rebalance_domains
RCU_SOFTIRQ rcu_process_callbacks
TIMER_SOFTIRQ run_timer_softirq
TASKLET_SOFTIRQ tasklet_action
HI_SOFTIRQ tasklet_hi_action
BLOCK_SOFTIRQ blk_done_softirq
NET_TX_SOFTIRQ net_tx_action
NET_RX_SOFTIRQ net_rx_action

이렇게 라즈베리안이 부팅하고 난 다음 Soft IRQ 벡터인 softirq_vec 전역 변수는 어떤 모습일까요? Trace32 프로그램으로 라즈베리안의 Soft IRQ 벡터를 확인하니 다음과 같네요.
v.v %t %d %i %y %l softirq_vec 
  (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))

위 정보는 Trace32로 본 softirq_vec 전역 변수입니다. 각 배열에 Soft IRQ 서비스 핸들러 함수가 저장돼 있습니다. 이후 Soft IRQ 서비스를 실행할 때는 softirq_vec 이란 Soft IRQ 벡터에 등록된 함수를 호출합니다.

이번 절에서는 라즈베리안에서 Soft IRQ 서비스를 어떻게 등록하는지 커널 로그를 통해 확인했습니다. 그리고 실제로 8개의 Soft IRQ 서비스를 등록하고 부팅 후 Soft IRQ 벡터에 등록된 함수 목록도 봤습니다.

다음 절에 이어서 Soft IRQ 서비스를 어떻게 요청하는지 살펴보겠습니다.



    핑백

    덧글

    댓글 입력 영역