Linux Interrupt

EEEFFEE·2023년 11월 28일
0

Armv8 Architecture

목록 보기
5/15

23.11.24 최초 작성
23.11.27 ftrace message, irq_desc 내용 추가

1. Interrupt

  • 하드웨어 신호를 감지해 외부 입력으로 전달되는 전기 신호
  • 발생 시 소프트웨어는 Interrupt Handler 수행 (Interrupt Service Routine)
  • 각 CPU 구조에 따라 인터럽트를 처리하는 방식이 다름

2. Armv8

  • Armv8의 경우 Exception의 한 종류로 처리하며 Exception Table을 바탕으로 처리 함 (링크)
  • 인터럽트 유발 시 Exception 표의 offset의 값을 더해 Exception Vector Table의 주소로 분기
  • user modekernel mode에서 Interrupt Vector의 처리 방식이 다름

2.1 user mode의 실행 흐름

  1. 애플리케이션에서 Interrupt 발생
  2. EL0에서 EL1로 변경
  3. EL0 Exception Vector로 분기

arch/arm64/kernel/entry-common.c
arch/arm64/kernel/entry.S

2.2 ftrace message 분석


chromium-browse-4516 [000] d.h.. 5364.217529: irq_handler_entry: irq=37 name=mmc1
	chromium-browse-4516 [000] d.h.. 5364.217532: <stack trace>
=> __traceiter_irq_handler_entry+0x4c/0x70
=> __handle_irq_event_percpu+0x178/0x280
=> handle_irq_event_percpu+0x20/0x68
=> handle_irq_event+0x50/0xa8
=> handle_fasteoi_irq+0xe4/0x198
=> generic_handle_domain_irq+0x34/0x50
=> gic_handle_irq+0xa0/0xd8
=> call_on_irq_stack+0x2c/0x54
=> do_interrupt_handler+0xe0/0xf8
=> el0_interrupt+0x50/0xf0
=> __el0_irq_handler_common+0x18/0x28
=> el0t_64_irq_handler+0x10/0x20
=> el0t_64_irq+0x18c/0x190

인터럽트 발생 (Interrupt Vector 주소로 이동)

  • chromium-browse-4516 [000] d.h.. 5364.217529: irq_handler_entry: irq=37 name=mmc1

    • 이름이 mmc1인 16번 인터럽트 핸들러가에 진입
  • VBAR_EL_0 + 0x480 (Lower EL using AArch64/IRQ) 주소로 분기

  • arch/arm64/kernel/entry-common.c

    • el0t_64_irq
    • el0t_64_irq_handler
    • __el0_irq_handler_common
    • el0_interrupt
    • do_interrupt_handler
  • arch/arm64/kernel/entry.S

    • call_on_irq_stack
  • arch/arm64/kernel/entry-common.c

    • gic_handle_irq
    • handle_domain_irq

IRQ 서브시스템 (인터럽트 컨텍스트 활성화, Interrupt Handler 호출)

3. Interrupt Context

  • 인터럽트 처리 중 (인터럽트 핸들러 호출 이후 핸들러 동작 중)

3.1 in_interrupt

  • 현재 Interrupt Context 상태임을 알려 줌

//https://elixir.bootlin.com/linux/v5.15.30/source/include/linux/preempt.h
#define nmi_count()	(preempt_count() & NMI_MASK)
#define hardirq_count()	(preempt_count() & HARDIRQ_MASK)
#ifdef CONFIG_PREEMPT_RT
# define softirq_count()	(current->softirq_disable_cnt & SOFTIRQ_MASK)
#else
# define softirq_count()	(preempt_count() & SOFTIRQ_MASK)
#define irq_count()	(nmi_count() | hardirq_count() | softirq_count())
#define in_interrupt()		(irq_count())


//preempt_count()는 thread_info 구조체의 preempt_count 필드 반환
//preempt_count가 0x10000, 0x20000 이면 인터럽트 컨텍스트

3.2.1 ftrace message 분석


int handle_domain_irq(struct irq_domain *domain,
		      unsigned int hwirq, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);
	struct irq_desc *desc;
	int ret = 0;

	irq_enter();						//인터럽트 컨텍스트 설정

	/* The irqdomain code provides boundary checks */
	desc = irq_resolve_mapping(domain, hwirq);
	if (likely(desc))
		handle_irq_desc(desc);
	else
		ret = -EINVAL;

	irq_exit();							//인터럽트 컨텍스트 해제
	set_irq_regs(old_regs);
	return ret;
}

  • /source/kernel/softirq.c
    • void irq_enter : 인터럽트 컨텍스트 상태에 진입
    • irq_enter_rcu
  • /source/include/linux/hardirq.h
    • __irq_enter_raw
  • /source/arch/arm64/include/asm/preempt.h
    • __preempt_count_add

3.2 제약사항

3.2.1 구현 루틴

  • Interrupt Context 중 실행시간이 오래걸리면 안됨
  • sleep()지원하는 커널 함수 사용 제약
    • mutex_lock()
    • schedule()
    • 너무 긴 while 루프 지양

4. Interrupt Handler 등록

  • 부팅 과정에서 InterruptInterrupt Handler 등록해야 함
  • request_irq : 인터럽트를 초기화하는 함수
///source/include/linux/interrupt.h

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);

///
irq : 인터럽트 번호
handler : 호출될 인터럽트 핸들러의 주소
flags : 인터럽트의 속성 플래그
name : 인터럽트 이름
dev : 인터럽트 핸들러에 전달하는 매개변수

5. irq_desc

  • 인터럽트를 관리하는 자료구조

//https://elixir.bootlin.com/linux/v5.15.30/source/include/linux/irqdesc.h
struct irq_desc {
	struct irq_common_data	irq_common_data;
	struct irq_data		irq_data;
	unsigned int __percpu	*kstat_irqs;
	irq_flow_handler_t	handle_irq;
	struct irqaction	*action;	/* IRQ action list */
	unsigned int		status_use_accessors;
	unsigned int		core_internal_state__do_not_mess_with_it;
	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		tot_count;
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	atomic_t		threads_handled;
	int			threads_handled_last;
	raw_spinlock_t		lock;
	struct cpumask		*percpu_enabled;
	const struct cpumask	*percpu_affinity;
    ...
}

0개의 댓글