23.11.24 최초 작성
23.11.27 ftrace message, irq_desc 내용 추가
Interrupt Handler
수행 (Interrupt Service Routine
)Armv8
의 경우 Exception
의 한 종류로 처리하며 Exception Table
을 바탕으로 처리 함 (링크)Exception
표의 offset의 값을 더해 Exception Vector Table
의 주소로 분기user mode
와 kernel mode
에서 Interrupt Vector
의 처리 방식이 다름Interrupt
발생EL0
에서 EL1
로 변경EL0 Exception Vector
로 분기arch/arm64/kernel/entry-common.c
arch/arm64/kernel/entry.S
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
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
//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 이면 인터럽트 컨텍스트
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
Interrupt Context
중 실행시간이 오래걸리면 안됨sleep()
지원하는 커널 함수 사용 제약mutex_lock()
schedule()
Interrupt
와 Interrupt 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 : 인터럽트 핸들러에 전달하는 매개변수
//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;
...
}