Embedded : : Linux/: : Linux Kernel

[인터럽트 하반부] Soft IRQ 서비스

Jay.P Morgan 2024. 10. 12. 01:10

 

 

 

 

Soft IRQ란?

 

 

 

 

1. Soft IRQ 서비스

 

 Soft IRQ 실행한다 의미는 Soft IRQ 서비스를 요청을 받아 이를 처리하는 과정이라고 볼 수 있습니다.

  

1) Soft IRQ 서비스 소개

 

 리눅스 커널에서 10가지 정도의 Soft IRQ 서비스를 지원합니다.

 

 linux# vim kernel/softirq.c

 

 

 

우선순위 Soft IRQ 서비스 설  명
0 HI_SOFTIRQ 가장 우선 순위가 높으며 TASKET_HI로 적용
1 TIMER_SOFTIRQ 동적 타이머 사용
2 NET_TX_SOFTIRQ 네트워크 패킷 송신용으로 사용
3 NET_RX_SOFTIRQ 네트워크 패킷 수신용으로 사용
4 BLOCK_SOFTIRQ 블록 디바이스에서 사용
5 IRQ_POLL_SOFTIRQ IRQ_POLL 연관 동작
6 TASKLET_SOFTIRQ 일반 테스크릿으로 사용
7 SCHED_SOFTIRQ 스케줄러에서 사용
8 HRTIMER_SOFTIRQ 현재 사용하지 않으며 하위 호환성 위해 남겨둠
9 RCU_SOFTIRQ RCU 처리용으로 사용

 

 

2) Soft IRQ 서비스의 라이프 사이클

 

 

1단계 : 부팅 과정

 - 부팅과정에서 open_softirq() 함수를 호출해서 Soft IRQ 서비스를 등록합니다

 

2단계 : 인터럽트 처리

 - 인터럽트 핸들러(인터럽트 컨텍스트)나 인터럽트 핸들러 내에서 호출한 서브 함수에서 raise_softirq() 함수를 호출해 Soft IRQ 서비스를 요청합니다. 

 

3단계 : Soft IRQ 컨텍스트

 - __do_softirq() 함수에서 이미 요청한 Soft IRQ 서비스를 실행합니다. 

 - 인터럽트 핸들링이 끝나고 Soft IRQ 서비스를 바로 실행하는 동작을 Soft IQR 컨텍스트라 함

 

 

3) 언제 후반부 기법으로 Soft IRQ를 사용하고 왜 알아야 하는가?

 

(1) 언제

 Soft IRQ 기업은 인터럽트 발생 빈도가 높거나  인터럽트 후반부를 빨리 처리해야 할 때 사용합니다. 이유는 인터럽트 핸들러 호출 이후 바로 Soft IRQ 서비스를 실행하기 때문입니다.

 

(2) 왜

 반응 속에 민감한 네트워크 패킷 처리나 고속 그래픽 처리 및 스토리지(UFS: Universal Flash Storage) 드라이버들은 Soft IRQ 서비스를 이용해서 구현되어 있습니다.

 

 인터럽트가 발생하면 실행 중이던 프로세스를 멈추고 인터럽트를 실행합니다. 그런데 Soft IRQ는 인터럽트 핸들러가 수행하면 실행 중이던 프로세스로 돌아가지 않고 바로 Soft IRQ를 시작합니다. 그렇기 때문에 Soft IRQ 쪽에서 시간이 오래 걸리면 시스템 반응 속도가 늦어질 수 있으므로 점검 포인터가 됩니다.

 

 드라이버에서 요청한 동적 타이머들은 타이머 인터럽트가 발생한 다음 Soft IRQ 서비스가 실행되기 때문에 이해하고 있어야 합니다. 

 

 커널에서는 Soft IRQ를 디바이스 드라이버 레벨에서 쓸 수 있는 태스크릿이라는 인터페이스 환경을 제공하기 때문에도 이해하고 있어야 합니다.

 

 

2. Soft IRQ 서비스

 

1) Soft IRQ 서비스 란

 

 Soft IRQ 서비스는 Soft IRQ를 실행하는 단위입니다. 위에서 본 내용처럼 Soft IRQ 서비스의 종류를 확인하려면 softirq_to_name 전역변수를 확인하면 됩니다.

 

 Soft IRQ는 Soft IRQ 서비스를 실행하기 위해 존재하며, 커널은 이를 위한 환경을 제공합니다.

 

 

2) Soft IRQ 서비스 핸들러 등록 과정

 

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

 open_softirq(Soft IRQ 서비스의 아이디, Soft IRQ 핸들러)

 

 실제 실제 함수 인자를 보면 형식이 조금 다르게 되어 있지만 내용은 같은 내용입니다.

 

 첫 번째 인자는 정수형 'Soft IRQ 서비스 인덱스'를, 두 번째 인자는 Soft IRQ 서비스 핸들러 함수의 주소를 저장합니다. 

 조금 다르게 설명하자면, softirq_vec 배열의 nr 인덱스에 해당하는 원소의 action 필드에 Soft IRQ 서비스 핸들러를 할당한다는 의미입니다.

 

linux# vim kernel/softirq.c

 

 

linux# vim include/linux/interrupt.h

 

action 필드에 Soft IRQ 서비스 핸들러 주소를 저장합니다.

 

 

 

3. 라즈베리 파이에서 Soft IRQ 서비스 핸들러 등록 과정 확인

 

1) 소스 코드 추가

 

 라즈베리 파이에서 open_softirq() 함수가 언제 실행되는지 콜 스택과 함께 확인하기 위해서 dump_stack() 함수 코드 한 줄 추가합니다.

 

저장하고 커널 빌드, 설치한 후 재부팅하고 로그를 확인합니다.

 

linux# vim kernel/softirq.c

 

 

 

2) 커널 로그에서 서비스 과정 확인

 

부팅 시 부분의 로그를 확인합니다.

 

# dmesg

 

처음 부분의 콜 스택 중 init_sched_fair_class() 함수호출을 볼 수 있습니다.

 

 

 init_sched_fair_class()  함수코드를 보면 SCHED_SOFTIRQ라는 Soft IRQ 서비스 아이디로 Soft IRQ 서비스 핸들러 함수인 run_rebalance_domain()을 등록합니다.

 

linux# vim kernel/sched/fair.c

 

 

  

 위와 같이 커널 로그에서 전체 Soft IRQ 서비스가 등록하는 것을 알 수 있으며, 콜 스택과 커널 코드를 확인해 보면 아래와 핸들러 함수와 같이 등록되어 있습니다.

 

Soft IRQ 서비스 아이디 Soft IRQ 서비스 핸들러
HI_SOFTIRQ tasklet_hi_action()
TIMER_SOFTIRQ run_timer_softirq()
NET_TX_SOFTIRQ net_tx_action()
NET_RX_SOFTIRQ net_rx_action()
BLOCK_SOFTIRQ blk_done_softirq()
TASKLET_SOFTIRQ tasklet_softirq()
SCHED_SOFTIRQ run_rebalance_domains()
RCU_SOFTIRQ rcu_process_callbacks()

 

 

 

 

감사합니다.

 

 

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p447~459, wikibook

 

 

 

 

 

6.8 Soft IRQ 서비스 요청 시점

 

 

1. Soft IRQ 서비스 요청의 전체 흐름

 

 전체 흐름을 보면서 Soft IRQ 서비스 요청하는 시점을 알아보겠습니다. 아래 그림은 Soft IRQ 서비스 요청 시의 자료구조입니다.

 

  

 

인터럽트 핸들러나 인터럽트 핸들러 서브루틴에서 호출하는 함수의 동작의미 합니다.

  __raise_softirq_irqoff는 Soft IRQ 서비스를 요청하는 동작을 하며, or_softirq_pending 함수로 irq_stat[cpu].__softirq_pending에 Soft IRQ 서비스 비트를 활성화하는 역할을 합니다.

 

 irq_stat는 배열이 percpu 타입의 변수로 CPU 코어의 개수만큼 존재합니다.

  

 

2. raise_softirq 함수 확인

 

linux# vim kernel/time/timer.c

 

 TIMER_SOFTIRQ 서비스를 요청한다면, raise_softirq(TIMER_SOFTIRQ) 열거형으로 정의된 정수형 1이 전달됩니다.

 

 

 

linux# vim kernel/time/softirq.c

   

 raise_softirq() 함수를 호출하기 위해서는 정수형 인자가 필요하다는 것을 알 수 있습니다.

 함수의 내용은 자신에게 전달된 인자를 전달하면서 raise_softirq_irqoff() 함수를 호출합니다.

 

 그리고 위아래 local_irq_save(), local_irq_restore() 함수는 CPU 라인의 인터럽트를 비활성화합니다. 이는 Soft IRQ 서비스를 요청하는 중에 인터럽트가 다시 발생하는 상황을 막기 위해서입니다.

 

 

  

이어서, raise_softirq_irqoff() 함수를 보면, 

__raise_softirq_irqoff() 함수를 호출하고 현재 실행코드가 인터럽트 컨텍스트인지 점검합니다. 인터럽트 컨텍스트가 아니면 wakeup_softirqd() 함수를 호출해 ksoftirqd 스레드를 깨웁니다.

 이것으로 인터럽트 컨텍스트가 아닐 때도 Soft IRQ 서비스 요청을 할 수 있다는 것입니다.

 

 

 

 __raise_softirq_irqoff()를 따라가 보면 ftrace 로그를 출력할 수 있는 함수와 입력 인자인 nr을 왼쪽으로 비트 시프트 연산한 결괏값을 or_softirq_pending() 함수에 전달합니다.

 

 

 

3. irq_stat 변수 확인

 

 or_softirq_pending() 함수를 분석해서 irq_stat[cpu].__softirq_pending 변수가 변경되는 확인 해보겠습니다.

 

  or_softirq_pending() 함수는 (__this_cpu_or(local_softirq_pending_ref, (x))) 매크로로 치환됩니다.

 __this_cpu_or() 함수 첫 번째 인자로 지정한 percpu 타입 변수와 두 번째 인자에 대해 |= 연산을 합니다.

 

  즉, or_softirq_pending() 함수를 호출하면 percpu 타입의 irq_stat 변수에 인자와 OR 연산한 결과를 더합니다. 

 irq_stat[cpu].__softirq_pending |= x;

 

 

 

예로,

CPU 번호 10진수 2진수 비트 연산자 Soft IRQ 서비스의 아이디
3 6 110 (1<<2)|(1<<1) NET_TX_SOFTIRQ | TIMER_SOFTIRQ

 

 

4. Soft IRQ 서비스를 요청을 확인 방법

 

 커널은 Soft IRQ 서비스 요청 여부를 알려주는 local_softirq_pending() 함수를 제공합니다. true면 Soft IRQ 서비스를 요청했다고 판단할 수 있습니다. 

 

local_softirq_pending() 함수를 커널 어느 코드에서 호출하는지 확인할 수 있습니다. Soft IRQ 서비스 요청 여부는 두 군데서 확인합니다.

 - 인터럽트 핸들러 처리를 마무리한 호출 하는 irq_exit() 함수

 - ksoftirqd 스레드핸들러 함수인 run_ksoftirqd() 함수

 

 

linux# vim kernel/softirq.c

 

 411번 줄에 보면, 

현재 실행 중인 코드가 인터럽트 컨텍스트 상태인지와 Soft IRQ 서비스 요청이 있는지 조건을 확인 후 Soft IRQ 서비스를 실행할지 결정합니다.

 

 

 

 640번 줄에 run_ksofirqd() 함수에서 보면 local_softirq_neding() 함수를 호출해서 Soft IRQ 서비스 요청이 있었는지 점검합니다.

 

 

 

 이렇게 Soft IRQ 서비스가 언제 요청하는지 확인해 보았습니다.

 

 

감사합니다. 

 

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p461 ~ 472, wikibook

 

 

6.9 Soft IRQ 서비스 처리 시점

 

1. Soft IRQ 서비스 실행 요청 점검

 

 이전 내용을 확인하면, Soft IRQ 서비스는 인터럽트 핸들링을 마무리한 후 처리했고 커널에서 인터럽트 처리를 시작하고 종료하는 __handle_domain_irq() 함수를 보면 내용을 확인할 수 있었습니다.

 

  __handle_domain_irq() 내에서 generic_handle_irq() 함수 호출(인터럽트 핸들러) 처리를 마치고 irq_exit() 함수를 호출합니다. 바로 Soft IRQ 서비스를 처리하는 시작점이라고 볼 수 있습니다. 

 

1) irq_exit() 함수 확인

 

 Soft IRQ 서비스 실행의 시작점인 내용으로 생각하고 확인해 보겠습니다.

 

 

 410번 줄을 보면 preempt_count_sub() 함수를 호출해 프로세스의 thread_info 구조체의 preempt_count 필드에서 HARDIRQ_OFFSET을 빼는 연산을 합니다. 이는 인터럽트 컨텍스트가 아니라고 설정하는 것입니다.

 

 

 411번 줄은 Soft IRQ 서비스를 요청한 적이 있는지 점검합니다. 이후 invoke_softirq() 함수를 호출합니다. 

  

linux# vim kernel/softirq.c

 

 

 

2) invoke_softirq() 함수 확인

 

 

 362줄 내용을 보면 ksoftirqd가 실행 중인데 Soft IRQ 서비스를 요청한 내용이 내역을 검사해서 실행함수를 종료합니다. 

 

 365줄은 force_irqthread 변수를 1로 설정했을 wakeup_softirqd() 함수를 호출해서 ksoftirqd 스레드를 깨웁니다. 

 

 

  

 

 force_irqthread 변수는 리눅스 커널을 부팅하기 전 부트로더에서 threadirqs 커멘드라인으로 전달하면 설정됩니다. 35번 줄에 threadirqs 설정되어 있는 것을 확인할 수 있습니다.

 

linux# vim kernel/irq/manage.c

 

 

 

 

 invoke_softirq() 함수로 다시 돌아가서 366번 줄의 CONFIG 조건에 따라 컴파일됩니다. 그런 보통 라즈베리 파이 커널에서는 CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK 비활성화 돼 있어 do_softirq_own_stack() 함수를 호출합니다.

 

linux# vim include/linux/interrupt.h

 

 

 

2. Soft IRQ 서비스 실행

 

 Soft IRQ 서비스를 실행하다는 것은 Soft IRQ 서비스 핸들러를 호출한다는 의미이며, __do_softirq() 함수에서 이를 처리합니다.

 

1) __do_softirq() 함수 확인

  

 251줄의 변수를 보면, jiffies에 MAX_SOFTIRQ_TIME을 더해 end지역변수에 저장합니다. 

 MAX_SOFTIRQ_TIME 매크로는 msecs_to_jiffies(2) 코드로 치환됩니다.

 

  msecs_to_jiffies() 함수는 밀리초 단위의 상수를 입력으로 받아 1/HZ 단위의 시간 정보로 바꾸는 기능입니다. 즉, 2 밀리초를 1/HZ 단위의 시간 정보로 바꾸는 목적의 코드입니다.

 

 234번 줄은 현재 시각을 기준으로 2밀리 초에 해당하는 시간 정보를 end 지역변수에 저장합니다. end 변수는 다음 조건에 따라 _do_softirq() 함수의 실행 시간을 제한하려는 용도입니다.

 

 이렇게 시간을 설정하는 것은 __do_softirq() 함수에서 Soft IRQ 서비스 핸들러를 실행한 시간이 end를 초과하면 함수 실행을 종료하기 위해서입니다.

 

253번 줄을 보면 MAX_SOFTIRQ_RESTART 매크로로 대치됩니다. 이 매크로는 10으로 되어 있습니다. 이는 restart 레이블을 실행할 때마다 --max_restart; 연산으로 max_restart 감소시킵니다. 0이 되면 restart 레이블은 실행되지 못하고 __do_softirq() 함수는 실행 종료 됩니다.

 

위의 시간과 횟수 체크를 한다는 것을 알아 둡니다.

 

 

 

 266줄을 보면 percpu 타입의 irq_stat 변수에서 __softirq_pending값을 지역변수로 읽어 옵니다.

 __do_softirq() 함수가 실행되는 동안 irq_stat이라는 percpu 타입의 전역변수가 업데이트될 수 있기 때문에 전역변수는 지역변수에 저장한 후 연산하게 해서 동기화 문제를 피하게 합니다.

 

 

 

 280번 줄 while문을 조금 유념해서 봅니다. 커널에서 제공하는 ffs라는 라이브러리 함수를 사용해서 pending 변수에서 Soft IRQ 서비스를 설정한 비트를 찾습니다.

 

 ffs() 함수는 비트 위치를 1~32 범위로 계산해 반환합니다. 예로 ffs(20)이라면 2진수로는 10100이 됩니다. 여기서 가장 먼저 1이 세팅된 비트위치인 3을 알려줍니다.

 

 

 284번 줄에서 softirq_bit에서 1만큼 뺀 결과를 저장합니다. 이유는 Soft IRQ 서비스 핸들러 함수를 저장한 soft_irq_vec 배열 위치보다 1만큼 크기 때문입니다.

 

 

 

 이전 글에서 본 softirq_vec를 참조할 수 있습니다.

 

 

 292번 줄에서 softirq_vec 변수에서 저장된 Soft IRQ 서비스의 인덱스에 해당하는 Soft IRQ 서비스 핸들러 함수를 호출합니다. ftrace를 설정했다면 291,293번 줄에 의해 ftrace 메시지를 출력합니다.

 

 

 

300,301번 줄은 pending에서 1로 설정된 비트를 없애서 다음에 1로 설정된 비트를 읽을 수 있게 합니다.

 

 

 

 307번 줄에서 pending 된 Soft IRQ 서비스 있었는지 확인하는데, 이유는 __do_softirq() 함수에서 Soft IRQ서비스 핸들러를 호출하는 동안 누군가 Soft IRQ 서비스를 요청할 수도 있기 때문입니다.

 

 309번 줄에서  jiffies와 end를 인자로 time_before() 타이머 함수를 호출해서 __do_softirq() 함수를 실행한 시간이 2밀리 초를 넘지 않았으면 true, 2초를 넘었으면 false를 반환합니다.

 

 위에서 10번 restart 레이블 카운트한다고 했습니다. 여기서 1 감소와 10번 횟수 검사를 합니다.

 

 313번 줄에서 wakeup_softirqd() 함수에서 ksoftirqd 스레드를 깨웁니다.

 

 

 

3. ksoftirqd 스레드 깨우기

 

  wakeup_softirqd() 함수와 ksoftirqd 스레드 핸들러인 run_ksoftirqd() 함수를 확인합니다.

 

1) wakeup_softirqd() 함수 확인

 

74번 줄에서 CPU별로 생성된 ksoftirqd 스레드의 태스크 디스크립터를 가져옵니다.

76번 줄에서 tsk지역변수가 유효한 태스크 디스크립터 주소인지, ksoftirqd 스레드가 TASK_RUNNING 상태인지 검사해서 예외 처리하도록 합니다. 

 

 예외상황이 아니라면 wake_up_process함수를 호출해서 ksoftirqd 프로세스를 깨웁니다.

 ksoftirqd 프로세스가 깨어나면 run_ksoftirqd() 함수가 수행됩니다.

 

 

 

2) run_ksoftirqd() 함수 확인

 

650번 줄을 보면 local_softirq_pending() 함수를 호출해서 Soft IRQ 서비스 요청이 있었는지 검사한 후 __do_softirq() 함수를 호출합니다. 

 

 

 

 전체적으로 요약해 보면, 인터럽트 핸들러 수행이 끝나면 요청한 Soft IRQ 서비스가 있었는지 확인하고 __do_softirq() 함수가 2ms 이상이거나 Soft IRQ 서비스 핸들러를 10번 이상 호출했다면 ksoftirqd 스래드를 깨웁니다.

 

 irq_exit() -> invoke_softirq() -> __do_softirq() 

 

 

감사합니다.

 

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p473 ~ 490, wikibook

 

 

6.10 ksoftirqd 스레드와 Soft IRQ 컨텍스트

 

1. ksoftirqd 스레드에 대해서

 

  percpu 타입의 프로세스이며 ksoftirqd 스레드는 CPU 코어의 개수만큼 생성돼서 정해진 CPU 내에서만 실행됩니다.

  ksoftirqd 프로세스는 커널 스레드로 Soft IRQ 서비스를 레벨에서 처리하는 역할입니다.

 

 라즈베리 파이에서 ksoftirqd를 확인할 수 있습니다.

 

pi# ps axl | grep ksoftirq

  

코어가 4개인 시스템인 것을 알 수 있으며 ksoftirqd/[CPU 번호] 같은 규칙으로 이름을 생성됩니다.

 

 

 

 ksoftirqd 스레드는 swawn_ksoftirqd()함수 내 smpboot_register_percpu_thread() 함수를 호출할 때 ksoftirqd 스레드를 생성합니다.

 

 736번 줄에서 smpboot_register_percpu_thread() 함수를 호출할 때 ksoftirqd 스레드를 생성합니다. 

 ksoftirqd 스레드가 실행되면 run_ksoftirqd() 함수가 실행된다는 것은 이전 내용에 확인했습니다.

  

linux# vim kernel/softirq.c

 

 

 

 

 참고로 ksoftirqd 같은 percpu 타입의 스레드는 smp 핫플러그 스레드로 등록해서 실행됩니다. 커널에서 시스템 부하가 줄어들었을 때는 여러 개의 CPU가 동작할 필요가 없습니다. 

  smboot를 관리하는 함수는 smboot_thread_fn()인데, 이 부분은 책 부분의 내용을 넘어간다고 하여 책 저자의 블로그를 참조하라고 나와 있습니다.

 

 

 

책에 있는 링크는 접속이 되지 않고, 다른 링크가 있어 참고 삼아 남겨 놓습니다.

https://austindhkim.tistory.com/200

 

 

2. run_ksoftirqd() 함수 확인

 

 ksoftirqd 프로세스가 실행될 때 호출 되는 핸들러 함수입니다.

 650번 줄에서 local_softirq_pending() 함수를 호출해서  Soft IRQ 서비스를 호출 점검하고 요청이 있으면 __do_softirq() 함수를 호출해서 Soft IRQ 서비스 핸들러를 실행합니다.

 

 

  

 ksoftirqd 스레드를 제어하는 ksoftirqd_should_run() 함수도 보면, local_softirq_pending() 함수를 호출해서 Soft IRQ 서비스 요청이 있으면 true, 없으면 false를 반환합니다.

 

 

  

3. Soft IRQ 컨텍스트에 대해

 

 Soft IRQ 컨텍스트는 Soft IRQ와 컨텍스트의 합성어입니다. 컨텍스트는 레지스터 세트로 실행 그 자체라고 인터럽트 관련 글에서 보았습니다. 

 

 결국, 리눅스 커널에서는 Soft IRQ 서비스를 실행 중인 상태를 Soft IRQ 컨텍스트라고 정의할 수 있습니다.

 

1) Soft IRQ 컨텍스트의 시작점 확인

 

 Soft IRQ 컨텍스트는 __do_softirq() 함수에서 Soft IRQ 서비스 핸들러를 실행하기 전에 활성화된다고 볼 수 있습니다. 어느 코드에서 Soft IRQ 컨텍스트를 활성화하는지 알아봅니다.

 

__do_softirq() 함수의 코드를 Soft IRQ 관점에서 보면 아래와 같은 흐름을 볼 수 있습니다.

 

 269번 줄은 Soft IRQ 컨텍스트 활성화

 292번 줄은 Soft IRQ 서비스 핸들러 실행

 318번 줄은 Soft IRQ 컨텍스트 비활성화

 

그렇기 때문에 Soft IRQ 컨텍스트 활성화되는 시점이라고 한다면, __do_softirq() 함수에서 Soft IRQ 서비스를 실행하기 전이라고 할 수 있습니다.

 

 

 

2) Soft IRQ 컨텍스트는 언제 시작하는 확인

 

 위에서 본 코드에서 활성화하고 비활성화하는 코드를 확인했습니다. 그중에 SOFTIRQ_OFFSET를 확인해 볼 필요가 있습니다.

 

 SOFTIRQ_OFFSET은 (1UL << SOFTIRQ_SHIFT)로 되어 있습니다. 

 매크로를 조금 따라가 가보면, SOFTIRQ_SHIFT 플래그는 8이 되므로 1 << 8 연산결과인 0x100(256)이 됩니다.

 

linux# vim include/linux/preempt.h

 

 

 

 다시 돌아와서 softirq.c 파일을 보면,

 

 Soft IRQ 컨텍스트의 시작은 __local_bh_disable_ip() 함수에서 설정합니다.

 

 124번 줄을 보면 __preempt_count_add() 함수 인자로 cnt를 그래도 전달하는데, cnt는 SOFTIRQ_OFFSET 플래그입니다.

 

 

 

 __preempt_count_add() 함수를 보면 *preempt_count_ptr() 함수의 매개변수인 val을 더하는 연산을 수행합니다.

 

linux# vim include/asm-generic/preempt.h

 

 

 

 *preempt_count_ptr()를 따라가 보면, current_thread_info() 함수를 통해 프로세스의 스택 최상단 주소에 있는 thread_info 구조체의 preempt_count 필드에 접근한다는 것을 알 수 있습니다. 

 thread_info 관련 내용을 하면서 보았던 내용이라 낯설지는 않을 겁니다. 

 

 

 

Soft IRQ 컨텍스트를 비활성화할 때는 __local_bh_enable() 함수를 호출합니다.  

 

 

 

 여기서는 __preempt_count_sub() 함수를 호출해서 preempt_count 필드의 값을 val만큼 뺍니다. 함수를 호출할 때 SOFTIRQ_OFFSET 플래그를 전달할 때 0x100을 thread_info의 preempt_count 필드에 추가했던 것을 빼는 연산을 수행합니다.

 

 

 

 Soft IRQ 컨텍스트 관점에서 보면 preempt_count 필드에 SOFTIRQ_OFFSET 플래그인 0x100을 포함하고 있으면 현재 프로세스는 Soft IRQ 컨텍스트라고 해석할 수 있습니다.

 

 

 in_interrupt() 함수처럼 in_softirq() 함수를 통해 현재 실행 중인 코드가 Soft IRQ 컨텍스트인지 판단할 수 있습니다. 함수 구현은 아래와 같습니다.

 

linux# vim include/linux/preempt.h

 

 

 

 

 

감사합니다.

 

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p490~502, wikibook

 

 

 

 

참고: https://remnant24c1.tistory.com/583