ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [리눅스 커널] 커널 컨텍스트
    Linux/kernel 2023. 8. 3. 02:18

    글의 참고

    - https://linux-kernel-labs.github.io/refs/heads/master/lectures/interrupts.html

    https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&blogId=hsmnim&logNo=30085612028&viewDate=¤tPage=1&listtype=0 

    - http://egloos.zum.com/rousalome/v/9990872


    글의 전제

    - 밑줄로 작성된 글은 강조 표시를 의미한다.

    - 그림 출처는 항시 그림 아래에 표시했다.


    글의 내용

    - Interrpt Context [ 참고1 ] 

    : x86에서 예외가 아닌 `IRQ` 발생(하드웨어 인터럽트)으로 인해 CPU의 제어 로직이 인터럽트 핸들러를 점프해서 인터럽트를 처리해야 하는 환경을 인터럽트 컨택스트라고 한다. 외부 디바이스의 인터럽트로 인해 발생한 환경이라고 볼 수 있다. 

    While an interrupt is handled (from the time the CPU jumps to the interrupt handler until the interrupt handler returns - e.g. IRET is issued) it is said that code runs in "interrupt context".

    Code that runs in interrupt context has the following properties:

    - it runs as a result of an IRQ (not of an exception)
    - there is no well defined process context associated
    - not allowed to trigger a context switch (no sleep, schedule, or user memory access)

    - 참고 : https://linux-kernel-labs.github.io/refs/heads/master/lectures/interrupts.html#interrupt-context

     

    - Process Context [ 참고1 참고2 ] 

    : 프로세스 컨택스트는 현재 실행되고 있는 프로세스의 정보를 의미한다. 약간 이상하지 않나? 인터럽트 컨택스트는 정보라기 보다는 `인터럽트에 의해 발생된 환경`으로 정의를 하는데, 프로세스 컨택스트는 프로세스의 정보로 표현을 한다. 프로세스 컨택스트 또한 프로세스가 동작하고 있는 환경이라고 봐도 무방하다.

    Now that we have examined the implementation of processes and threads (tasks), how context switching occurs, how we can block, wake-up and preempt tasks, we can finally define what the process context is what are its properties:

    The kernel is executing in process context when it is running a system call.

    - In process context there is a well defined context and we can access the current process data with current
    - In process context we can sleep (wait on a condition).
    - In process context we can access the user-space (unless we are running in a kernel thread context).

    - 참고 : https://linux-kernel-labs.github.io/refs/heads/master/lectures/processes.html#process-context

     

    - Deferrable Actions

    - Defered work를 사용하는 이유는 2가지 이다.

    1. interruptr는 최대한 빠른 시간안에 처리가 되어야 한다. 그럼 왜 인터럽트를 빨리 처리해야 할까? 하드웨어 인터럽트가 발생하면, 현재 수행중인 프로세스는 무조건 선점된다. 즉, 하던 일이 도중에 끊긴 셈이다. 중요한 건 그 하던 일이 뭔지에 상관없이 선점된다는 것이다. 물론, 엄청 중요한 작업을 해야 할 경우는, 해당 작업을 수행하기 전에 인터럽트를 비활성화하고 프로세스 동기화를 수행하고 나서 작업을 수행할 것이다. 다시 돌아와서, 인터럽트가 발생하면 수행중이던 일이 멈추기 때문에 빠르게 처리해서 원래 하던일을 하게 해줘야 한다는 것이다. 이렇게 인터럽트가 독점을 하게 되면 시스템의 반응 속도가 크게 저하된다.

     

    2. interrut handler는 interrupt가 발생하면 자체 context인 interrupt context를 만들어서 실행한다. 그런데 여기서는 blocking call이 불가능하기 때문에 작업에 문제가 많다.

     

    - 인터럽트 컨텍스트란 결국 "현재 실행 중인 프로세스가 현재 인터럽트를 처리 중" 이라는 뜻이다.

    - 컨텍스트란 "프로세스 실행 그 자체"를 의미한다. 이 컨텍스트는 현재 실행 중인 프로세스 정보를 담고 있는 레지스터 세트로 표현할 수 있다.

    - 프로세스의 실행은 레지스터 세트로 표현할 수 있다. 레지스터 세트는 cpu_context_save 라는 구조체로 표현된다.

    - 리눅스 커널에서 인터럽트 컨텍스트를 정의한 이유는 뭘까? 그 이유는 인터럽트가 발생하면 이를 핸들링하는 코드는 빨리 실행돼야 하기 때문이다. 

     

    - ARM 프로세서는 인터럽트가 발생하면 익셉션을 유발해 인터럽트 벡터를 실행한다. 인터럽트 벡터에 있는 vector_irq라는 레이블에서 __irq_svc 레이블을 브랜치(호출)한다. 예를 들어, 아래와 같은 ftrace 로그가 있다고 치자.

     

    0x8010bd48 (+0x18) show_stack                                          
    0x80149f3c (+0xb8) sched_show_task                                
    0x80175d1c (+0x998) rcu_check_callbacks                         
    0x80179148 (+0x3c) update_process_times                        
    0x8018a668 (+0x50) tick_sched_handle                              
    0x8018a6bc (+0x50) tick_sched_timer                                 
    0x8017a1b0 (+0xc8) __hrtimer_run_queues                       
    0x8017a3e8 (+0xac) hrtimer_interrupt                                  
    0x80188904 (+0x34) tick_receive_broadcast                       
    0x8010e15c (+0xf0) handle_IPI                                            
    0x801014a0 (+0x90) gic_handle_irq                                     
    0x8010c74c (+0x6c) __irq_svc         // __irq_svc를 기준으로 위쪽 함수들은 모두 인터럽트 컨텍스트에서 동작한다. 이 아래는 프로세스 컨텍스트라고 보면 된다.
    0x804a51d8 (+0x1c) cpuidle_enter
    0x8015e684 (+0x28) call_cpuidle
    0x8015e8cc (+0x140) cpu_startup_entry
    0x80651c48 (+0x8c) rest_init
    0x80800c54 (+0x350) start_kernel

     

    - 현재 실행중인 코드가 인터럽트 컨텍스트 구간인지는 어떻게 알 수 있을까? in_interrupt() 함수가 이 정보를 알려준다. in_interrup()가 true를 반환하면 인터럽트 컨텍스트이고, false를 반환하면 프로세스 컨텍스트를 의미한다.

     

    - 그런데 현재 실행 중인 코드가 인터럽트 컨텍스트인지 왜 알려고 하는걸까? 그 이유는 인터럽트 서비스 루틴은 실행 중인 프로세스를 멈추고 동작하기 때문이다. 그래서 인터럽트 핸들러의 서브루틴으로 실행중이라면 되도록 빨리 실행을 마무리 해야 한다. 사실 이 말을 위해하기 위해서는 운영체제 이론에서 "인터럽트 컨텍스트 스위치"에 대한 내용을 좀 이해하고 있어야 한다.

     

    - 리눅스에는 BUG_ON() 이라는 매크로 함수가 있다. 이 함수에 전달되는 인자가 true이면 커널 패닉을 유발한다. 커널 패닉이 유도되면 시스템이 자동으로 리셋 루틴을 밟게 된다. 왜 이런 사용자 입장에서 불편한 루틴을 만들것일까? 그 이유는 바로 BUG_ON() 함수에 전달된 인자가 true일 경우, 시스템에 굉장히 치명적인 문제를 야기할 수 있기 때문이다. 시스템에 치명적인 오류가 감지되면 차라리 리셋시켜 예상치 못한 오류를 방지하겠다는 의도다. 리눅스 커널에는 치명적인 시스템 오동작을 유발할 수 있는 에러 조건을 유발하는 코드가 많다. 리눅스 시스템이 다양한 오동작을 보이기 전에 발생한 시점에서 커널 패닉으로 코어 덤프를 받아 근본 원인을 해결하려는 의도이다.

     

    - 인터럽트 컨텍스트에서는 사용할 수 있는 함수가 제한되어 있다. 만약 인터럽트 컨텍스트에서 스케줄링을 지원하는 커널 함수를 호출하면 커널 패닉이 발생할 수 있다. 

     

    - 인터럽트 컨텍스트에서 스케줄링 관련 함수를 호출하면 안되는 이유는 무엇일까? 스케줄링 관련 함수를 호출하면 커널 내부에서 많은 연산을 수행하므로 실행 시간이 오래 걸린다. 짧은 시간에 인터럽트 핸들러를 실행하고 인터럽트 발생으로 실행을 멈춘 코드로 돌아가야 하는데, 프로세스가 휴면 상태에 진입하면 시스템에 오동작할 수 있다. 아래의 커널 패닉 사례를 분석해보자.

     

    Hello,

    syzbot found the following issue on:

    HEAD commit: 04a55c94 Merge tag 'mac80211-for-net-2020-10-30' of git://..
    git tree: net
    console output: https://syzkaller.appspot.com/x/log.txt?x=12079c3a500000
    kernel config: https://syzkaller.appspot.com/x/.config?x=803dd96a0e492ac1
    dashboard link: https://syzkaller.appspot.com/bug?extid=d5a9416c6cafe53b5dd0
    compiler: gcc (GCC) 10.1.0-syz 20200507
    syz repro: https://syzkaller.appspot.com/x/repro.syz?x=13714d8a500000
    C reproducer: https://syzkaller.appspot.com/x/repro.c?x=155a5792500000

    The issue was bisected to:

    commit dcd479e10a0510522a5d88b29b8f79ea3467d501
    Author: Johannes Berg <johann...@intel.com>
    Date: Fri Oct 9 12:17:11 2020 +0000

    mac80211: always wind down STA state

    bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=12d3158a500000
    final oops: https://syzkaller.appspot.com/x/report.txt?x=11d3158a500000
    console output: https://syzkaller.appspot.com/x/log.txt?x=16d3158a500000

    IMPORTANT: if you fix the issue, please add the following tag to the commit:
    Reported-by: syzbot+d5a941...@syzkaller.appspotmail.com
    Fixes: dcd479e10a05 ("mac80211: always wind down STA state")

    wlan0: Created IBSS using preconfigured BSSID 50:50:50:50:50:50
    wlan0: Creating new IBSS network, BSSID 50:50:50:50:50:50
    wlan1: Created IBSS using preconfigured BSSID 50:50:50:50:50:50
    wlan1: Creating new IBSS network, BSSID 50:50:50:50:50:50
    BUG: sleeping function called from invalid context at net/mac80211/sta_info.c:1962
    in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 8, name: kworker/u4:0
    4 locks held by kworker/u4:0/8:
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: arch_atomic64_set arch/x86/include/asm/atomic64_64.h:34 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: atomic64_set include/asm-generic/atomic-instrumented.h:856 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: atomic_long_set include/asm-generic/atomic-long.h:41 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: set_work_data kernel/workqueue.c:616 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: set_work_pool_and_clear_pending kernel/workqueue.c:643 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: process_one_work+0x821/0x15a0 kernel/workqueue.c:2243
    #1: ffffc90000cd7da8 ((work_completion)(&sdata->work)){+.+.}-{0:0}, at: process_one_work+0x854/0x15a0 kernel/workqueue.c:2247
    #2: ffff888024610d00 (&wdev->mtx){+.+.}-{3:3}, at: sdata_lock net/mac80211/ieee80211_i.h:1021 [inline]
    #2: ffff888024610d00 (&wdev->mtx){+.+.}-{3:3}, at: ieee80211_ibss_work+0x93/0xe80 net/mac80211/ibss.c:1683
    #3: ffffffff8b337160 (rcu_read_lock){....}-{1:2}, at: sta_info_insert_finish net/mac80211/sta_info.c:644 [inline]
    #3: ffffffff8b337160 (rcu_read_lock){....}-{1:2}, at: sta_info_insert_rcu+0x680/0x2ba0 net/mac80211/sta_info.c:732
    Preemption disabled at:
    [<ffffffff88e616bf>] __mutex_lock_common kernel/locking/mutex.c:955 [inline]
    [<ffffffff88e616bf>] __mutex_lock+0x10f/0x10e0 kernel/locking/mutex.c:1103
    CPU: 0 PID: 8 Comm: kworker/u4:0 Not tainted 5.10.0-rc1-syzkaller #0
    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
    Workqueue: phy4 ieee80211_iface_work
    Call Trace:
    __dump_stack lib/dump_stack.c:77 [inline]
    dump_stack+0x107/0x163 lib/dump_stack.c:118
    ___might_sleep.cold+0x1e8/0x22e kernel/sched/core.c:7298
    sta_info_move_state+0x32/0x8d0 net/mac80211/sta_info.c:1962
    sta_info_free+0x65/0x3b0 net/mac80211/sta_info.c:274
    sta_info_insert_rcu+0x303/0x2ba0 net/mac80211/sta_info.c:738
    ieee80211_ibss_finish_sta+0x212/0x390 net/mac80211/ibss.c:592
    ieee80211_ibss_work+0x2c7/0xe80 net/mac80211/ibss.c:1700
    ieee80211_iface_work+0x82e/0x970 net/mac80211/iface.c:1476
    process_one_work+0x933/0x15a0 kernel/workqueue.c:2272
    worker_thread+0x64c/0x1120 kernel/workqueue.c:2418
    kthread+0x3af/0x4a0 kernel/kthread.c:292
    ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:296

    =============================
    [ BUG: Invalid wait context ]
    5.10.0-rc1-syzkaller #0 Tainted: G W
    -----------------------------
    kworker/u4:0/8 is trying to lock:
    ffff88801bb429d0 (&local->chanctx_mtx){+.+.}-{3:3}, at: ieee80211_recalc_min_chandef+0x49/0x140 net/mac80211/util.c:2740
    other info that might help us debug this:
    context-{4:4}
    4 locks held by kworker/u4:0/8:
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: arch_atomic64_set arch/x86/include/asm/atomic64_64.h:34 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: atomic64_set include/asm-generic/atomic-instrumented.h:856 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: atomic_long_set include/asm-generic/atomic-long.h:41 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: set_work_data kernel/workqueue.c:616 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: set_work_pool_and_clear_pending kernel/workqueue.c:643 [inline]
    #0: ffff88801b994138 ((wq_completion)phy4){+.+.}-{0:0}, at: process_one_work+0x821/0x15a0 kernel/workqueue.c:2243
    #1: ffffc90000cd7da8 ((work_completion)(&sdata->work)){+.+.}-{0:0}, at: process_one_work+0x854/0x15a0 kernel/workqueue.c:2247
    #2: ffff888024610d00 (&wdev->mtx){+.+.}-{3:3}, at: sdata_lock net/mac80211/ieee80211_i.h:1021 [inline]
    #2: ffff888024610d00 (&wdev->mtx){+.+.}-{3:3}, at: ieee80211_ibss_work+0x93/0xe80 net/mac80211/ibss.c:1683
    #3: ffffffff8b337160 (rcu_read_lock){....}-{1:2}, at: sta_info_insert_finish net/mac80211/sta_info.c:644 [inline]
    #3: ffffffff8b337160 (rcu_read_lock){....}-{1:2}, at: sta_info_insert_rcu+0x680/0x2ba0 net/mac80211/sta_info.c:732
    stack backtrace:
    CPU: 0 PID: 8 Comm: kworker/u4:0 Tainted: G W 5.10.0-rc1-syzkaller #0
    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
    Workqueue: phy4 ieee80211_iface_work
    Call Trace:
    __dump_stack lib/dump_stack.c:77 [inline]
    dump_stack+0x107/0x163 lib/dump_stack.c:118
    print_lock_invalid_wait_context kernel/locking/lockdep.c:4489 [inline]
    check_wait_context kernel/locking/lockdep.c:4550 [inline]
    __lock_acquire.cold+0x310/0x3a2 kernel/locking/lockdep.c:4787
    lock_acquire kernel/locking/lockdep.c:5442 [inline]
    lock_acquire+0x1af/0x8b0 kernel/locking/lockdep.c:5407
    __mutex_lock_common kernel/locking/mutex.c:956 [inline]
    __mutex_lock+0x134/0x10e0 kernel/locking/mutex.c:1103
    ieee80211_recalc_min_chandef+0x49/0x140 net/mac80211/util.c:2740
    sta_info_move_state+0x3cf/0x8d0 net/mac80211/sta_info.c:2019
    sta_info_free+0x65/0x3b0 net/mac80211/sta_info.c:274
    sta_info_insert_rcu+0x303/0x2ba0 net/mac80211/sta_info.c:738
    ieee80211_ibss_finish_sta+0x212/0x390 net/mac80211/ibss.c:592
    ieee80211_ibss_work+0x2c7/0xe80 net/mac80211/ibss.c:1700
    ieee80211_iface_work+0x82e/0x970 net/mac80211/iface.c:1476
    process_one_work+0x933/0x15a0 kernel/workqueue.c:2272
    ...
    ...

    - 참고 : https://groups.google.com/g/syzkaller-bugs/c/auBo8LaHW6g/m/myZLkxoMCgAJ

    - 인터럽트 컨텍스트에서 뮤텍스 획득 시도를 했다가, 커널 패닉을 맞은 사례다. 왜 인터럽트 컨텍스트에서 뮤텍스 획득을 시도하면 커널 패닉이 발생할까? 뮤텍스는 스켈줄링을 지원하는 함수기 때문이다.

Designed by Tistory.