Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

5363
1898
209234


[리눅스커널][SoftIRQ] Soft IRQ 컨택스트에 대해서 - in_softirq() 6. 인터럽트 후반부 처리

6.10 Soft IRQ 컨택스트에 대해서

인터럽트가 발생한 후 인터럽트 핸들러가 실행 중인 상태를 인터럽트 컨택스트라고 합니다. 이와 마찬가지로 Soft IRQ 서비스를 실행 중인 상태를 Soft IRQ 컨택스트라고 부릅니다. 이번 절에서는 Soft IRQ 컨택스트 시작 시점과 관련 자료구조에 대해서 살펴보겠습니다.

6.10.1 Soft IRQ 컨택스트 시작점은 어디인가?

Soft IRQ 컨택스트는 Soft IRQ 서비스를 실행할 때 활성화됩니다. 
Soft IRQ 서비스는 __do_softirq() 함수에서 실행하니 다음 코드를 보면서 Soft IRQ 컨택스트 활성화 시점을 알아봅시다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/softirq.c]
01 asmlinkage __visible void __softirq_entry __do_softirq(void)
02 {
...
03 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
04 in_hardirq = lockdep_softirq_start();
05
06 restart:
...
07
08 while ((softirq_bit = ffs(pending))) {
...
09 trace_softirq_entry(vec_nr);
10 h->action(h);
11 trace_softirq_exit(vec_nr);
...
12 pending >>= softirq_bit;
13 }
...
14 lockdep_softirq_end(in_hardirq);
15 account_irq_exit_time(current);
16 __local_bh_enable(SOFTIRQ_OFFSET);
...
17 }

위 코드는 __do_softirq() 함수에서 가장 중요한 부분만 가져온 것입니다.

03~16 번째 코드 구간은 Soft IRQ 서비스를 실행하는 블록입니다. 

3 번째 줄 코드에서 Soft IRQ 컨택스트를 활성화하고 16 번째 줄에서는 Soft IRQ 컨택스트를 비활성화합니다. 
03 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
... Soft IRQ 서비스 실행 루틴 ...
16 __local_bh_enable(SOFTIRQ_OFFSET);

Soft IRQ 컨택스트가 활성화되는 시점은 __do_softirq() 함수에서 Soft IRQ 서비스를 실행할 때입니다.

6.10.2 Soft IRQ 컨택스트는 언제 시작할까?

Soft IRQ 컨택스트를 활성화할 때 변경하는 자료구조는 무엇일까요?
각각 프로세스의 스택 최상단 주소에 있는 struct thread_info 구조체 preempt_count 필드입니다.

Soft IRQ 컨택스트일 때는 struct thread_info 구조체 preempt_count 필드가 0x100 으로 바꾸고 Soft IRQ 컨택스트가 아닐 때는 0x100을 Clear합니다. 이 필드가 바뀌는 전체 흐름도는 다음과 같습니다.

Soft IRQ 인터럽트를 활성화 및 비활성화는 다음 단계로 동작합니다.
1. Soft IRQ 컨택스트 시작: __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
2. ... Soft IRQ 서비스 실행 루틴 ...
3. Soft IRQ 컨택스트 끝: __local_bh_enable(SOFTIRQ_OFFSET);

먼저 __local_bh_disable_ip() 함수와 __local_bh_enable() 함수에 전달하는 SOFTIRQ_OFFSET 플래그에 대해 알아봅시다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/preempt.h]
#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)

SOFTIRQ_SHIFT 플래그가 8이니, 1 << 8 연산의 결과인 0x100(256)가 SOFTIRQ_OFFSET입니다.

Soft IRQ 컨택스트 시작: __local_bh_disable_ip() 함수 분석

__local_bh_disable_ip() 함수 코드를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/softirq.c]
01 void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
02 {
03 unsigned long flags;
04
05 WARN_ON_ONCE(in_irq());
06
07 raw_local_irq_save(flags);
08 __preempt_count_add(cnt);

08 번째 줄 코드를 보면__local_bh_disable_ip() 함수 두 번째 인자 cnt를 그대로 __preempt_count_add() 함수에 전달합니다. int cnt 는 SOFTIRQ_OFFSET 플래그입니다.

__preempt_count_add() 함수 구현부를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/asm-generic/preempt.h]
01 static __always_inline void __preempt_count_add(int val)
02 {
03 *preempt_count_ptr() += val;
04 }

*preempt_count_ptr() 에 함수 인자인 val을 더하는 연산을 수행합니다.

preempt_count_ptr() 함수 구현부는 다음과 같습니다. 
[https://elixir.bootlin.com/linux/v4.19.30/source/include/asm-generic/preempt.h]
static __always_inline volatile int *preempt_count_ptr(void)
{
return &current_thread_info()->preempt_count;
}
current_thread_info() 함수를 통해 프로세스 스택 최상단 주소 struct thread_info 구조체 preempt_count 필드에 접근합니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/arch/arm/include/asm/thread_info.h]
static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)
(current_stack_pointer & ~(THREAD_SIZE - 1));
}

Soft IRQ 컨택스트 끝: __local_bh_enable() 함수 분석

Soft IRQ 컨택스트를 비활성화할 때 __local_bh_enable() 함수를 __do_softirq() 함수에서 호출하며 구현부는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/softirq.c]
01 static void __local_bh_enable(unsigned int cnt)
02 {
03 lockdep_assert_irqs_disabled();
...
04 __preempt_count_sub(cnt);
05 }

__preempt_count_sub() 함수 구현부는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/asm-generic/preempt.h]
static __always_inline void __preempt_count_sub(int val)
{
*preempt_count_ptr() -= val;
}

preempt_count_ptr() 함수에 접근해 프로세스 struct thread_info 구조체 preempt_count 필드에 -val 만큼 증감합니다.
__local_bh_enable() 함수를 호출 시 SOFTIRQ_OFFSET 플래그를 전달하니 struct thread_info 구조체 preempt_count 필드에서 0x100을 빼는 연산입니다.

  (struct thread_info *) (struct thread_info*)0x9B1EC000 = 0x8100C000 -> (
    (long unsigned int) flags = 0x2,
    (int) preempt_count = 0x00000102,
    (mm_segment_t) addr_limit = 0,
6.10.3 Soft IRQ 컨택스트 확인방법 

현재 실행 중인 코드가 Soft IRQ 컨택스트인지를 알려면 in_softirq() 함수를 쓰면 됩니다.

in_softirq() 함수를 호출해 true를 반환하면 현재 실행 중인 코드가 Soft IRQ 컨택스트인 것입니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/preempt.h]
#define in_softirq() (softirq_count())

* 유튜브 강의 동영상도 있으니 같이 들으시면 좋습니다.



"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.  
혹시 글을 읽고 궁금점이 있으면 댓글로 질문 남겨주세요. 상세한 답글 올려 드리겠습니다!"

# Reference 인터럽트 후반부 처리








6.9 Soft IRQ 서비스는 누가 언제 처리하나?




6.13 Soft IRQ 디버깅
6.13.1 ftrace Soft IRQ 이벤트 분석 방법
6.13.2 /proc/softirqs로 Soft IRQ 서비스 실행 횟수 확인


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2


 





핑백

덧글

댓글 입력 영역