인터럽트 컨텍스트란 현재 실행 중인 프로세스가 현재 인터럽트를 처리 중이라는 것을 의미
- 현재 실햄중인 함수가 인터럽트 핸들러
- 핸들러에서 호출한 함수
컨텍스트의 의미
- 프로세스 실행 그 자체(스냅샷)
- 현재 실행 중인 프로세스 정보를 담고 있는 레지스터 세트
ex. 커널이 schdule 함수를 실행하고 있는데, 이 함수의 주소가 0xC000D000일 경우 프로그램 카운터의 값은 0xC000D000이다
- 프로그램 카운터 레지스터를 포함한 레지스터 세트로 현재 실행중인 프로세스의 상태를 의미
레지스터의 정보가 컴퓨터가 실행 중인 현재 순간을 의미
컨텍스트 스위칭이 일어날 때 레지스터의 정보를 스택에 저장
스택에 저장하는 포맷


컨텍스트는 "프로세스가 실행 중인" 상태라 할 수 있음
인터럽트 컨텍스트 : 인터럽트를 처리중
왜 구분했을까?
- 인터럽트는 빨리 실행되어야 하기 때문.(하던 걸 멈추고 다른 작업하는 거니까)
- 인터럽트 처리 중에 호출되는 함수인지 아닌지를 구분하는 게 중요
bcm2835-mailbox.c를 linux/drivers/mailbox에 넣기
bcm2835-mailbox.c
| // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010,2015 Broadcom * Copyright (C) 2013-2014 Lubomir Rintel * Copyright (C) 2013 Craig McGeachie * * Parts of the driver are based on: * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/ * linux.git * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/ * mailbox/bcm2835-ipc.c * - documentation available on the following web site: * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/mailbox_controller.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/spinlock.h> /* Mailboxes */ #define ARM_0_MAIL0 0x00 #define ARM_0_MAIL1 0x20 /* * Mailbox registers. We basically only support mailbox 0 & 1. We * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about * the placement of memory barriers. */ #define MAIL0_RD (ARM_0_MAIL0 + 0x00) #define MAIL0_POL (ARM_0_MAIL0 + 0x10) #define MAIL0_STA (ARM_0_MAIL0 + 0x18) #define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) #define MAIL1_WRT (ARM_0_MAIL1 + 0x00) #define MAIL1_STA (ARM_0_MAIL1 + 0x18) /* On ARCH_BCM270x these come through <linux/interrupt.h> (arm_control.h ) */ #ifndef ARM_MS_FULL /* Status register: FIFO state. */ #define ARM_MS_FULL BIT(31) #define ARM_MS_EMPTY BIT(30) /* Configuration register: Enable interrupts. */ #define ARM_MC_IHAVEDATAIRQEN BIT(0) #endif struct bcm2835_mbox { void __iomem *regs; spinlock_t lock; struct mbox_controller controller; }; static struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link) { return container_of(link->mbox, struct bcm2835_mbox, controller); } static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id) { struct bcm2835_mbox *mbox = dev_id; struct device *dev = mbox->controller.dev; struct mbox_chan *link = &mbox->controller.chans[0]; while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) { u32 msg = readl(mbox->regs + MAIL0_RD); dev_dbg(dev, "Reply 0x%08X\n", msg); mbox_chan_received_data(link, &msg); } return IRQ_HANDLED; } static void interrupt_debug_irq_desc(int irq_num) { struct irqaction *action; struct irq_desc *desc; desc = irq_to_desc(irq_num); if (!desc ) { pr_err("invalid desc at %s line: %d\n", __func__, __LINE__); return; } action = desc->action; if (!action ) { pr_err("invalid action at %s line:%d \n", __func__, __LINE__); return; } printk("[+] irq_desc debug start \n"); printk("irq num: %d name: %8s \n", action->irq , action->name); printk("dev_id:0x%x \n", (unsigned int)action->dev_id); if (action->handler) { printk("interrupt handler: %pF \n", action->handler); } printk("[-] irq_desc debug end \n"); } static int bcm2835_send_data(struct mbox_chan *link, void *data) { struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); u32 msg = *(u32 *)data; spin_lock(&mbox->lock); writel(msg, mbox->regs + MAIL1_WRT); dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg); spin_unlock(&mbox->lock); return 0; } static int bcm2835_startup(struct mbox_chan *link) { struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); /* Enable the interrupt on data reception */ writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF); return 0; } static void bcm2835_shutdown(struct mbox_chan *link) { struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); writel(0, mbox->regs + MAIL0_CNF); } static bool bcm2835_last_tx_done(struct mbox_chan *link) { struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); bool ret; spin_lock(&mbox->lock); ret = !(readl(mbox->regs + MAIL1_STA) & ARM_MS_FULL); spin_unlock(&mbox->lock); return ret; } static const struct mbox_chan_ops bcm2835_mbox_chan_ops = { .send_data = bcm2835_send_data, .startup = bcm2835_startup, .shutdown = bcm2835_shutdown, .last_tx_done = bcm2835_last_tx_done }; static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox, const struct of_phandle_args *sp) { if (sp->args_count != 0) return ERR_PTR(-EINVAL); return &mbox->chans[0]; } static int bcm2835_mbox_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret = 0; struct bcm2835_mbox *mbox; mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); if (mbox == NULL) return -ENOMEM; spin_lock_init(&mbox->lock); ret = devm_request_irq(dev, platform_get_irq(pdev, 0), bcm2835_mbox_irq, 0, dev_name(dev), mbox); interrupt_debug_irq_desc(31); //ojw 추가. if (ret) { dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", ret); return -ENODEV; } mbox->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mbox->regs)) { ret = PTR_ERR(mbox->regs); return ret; } mbox->controller.txdone_poll = true; mbox->controller.txpoll_period = 5; mbox->controller.ops = &bcm2835_mbox_chan_ops; mbox->controller.of_xlate = &bcm2835_mbox_index_xlate; mbox->controller.dev = dev; mbox->controller.num_chans = 1; mbox->controller.chans = devm_kzalloc(dev, sizeof(*mbox->controller.chans), GFP_KERNEL); if (!mbox->controller.chans) return -ENOMEM; ret = devm_mbox_controller_register(dev, &mbox->controller); if (ret) return ret; platform_set_drvdata(pdev, mbox); dev_info(dev, "mailbox enabled\n"); return ret; } static const struct of_device_id bcm2835_mbox_of_match[] = { { .compatible = "brcm,bcm2835-mbox", }, {}, }; MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match); static struct platform_driver bcm2835_mbox_driver = { .driver = { .name = "bcm2835-mbox", .of_match_table = bcm2835_mbox_of_match, }, .probe = bcm2835_mbox_probe, }; static int __init bcm2835_mbox_init(void) { return platform_driver_register(&bcm2835_mbox_driver); } arch_initcall(bcm2835_mbox_init); static void __init bcm2835_mbox_exit(void) { platform_driver_unregister(&bcm2835_mbox_driver); } module_exit(bcm2835_mbox_exit); MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); MODULE_DESCRIPTION("BCM2835 mailbox IPC driver"); MODULE_LICENSE("GPL v2"); |
build.sh && ./install.sh && reboot 수행

bcm2835_mbox_irq 추적 가능한지 확인

set_ftrace6.sh 아래와 같이 수정
| #!/bin/bash # tracing을 끄고 1초 쉬기(0) echo 0 > /sys/kernel/debug/tracing/tracing_on sleep 1 echo "tracing_off" #이벤트 추적 끄기(0) echo 0 > /sys/kernel/debug/tracing/events/enable sleep 1 echo "events disabled" #함수 추적 echo function > /sys/kernel/debug/tracing/current_tracer sleep 1 echo "function tracer enabled" #echo rpi_get_interrupt_info > /sys/kernel/debug/tracing/set_ftrace_filter #sleep 1 #echo "set_ftrace_filter enabled" #echo copy_process __arm64_sys_clone > /sys/kernel/debug/tracing/set_ftrace_filter echo bcm2835_mbox_irq > /sys/kernel/debug/tracing/set_ftrace_filter sleep 1 echo "set_ftrace_filter enabled" #echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable #echo "sched_switch event enabled" #인터럽트 핸들러 시작지점, 끝나는 지점에 켜기(1) echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_exit/enable echo "event enabled" #콜스택 보겠다(1) echo 1 > /sys/kernel/debug/tracing/options/func_stack_trace echo "function stack trace enabled" #함수 간 간격을 볼 수 있음(1) echo 1 > /sys/kernel/debug/tracing/options/sym-offset echo "sys-offset trace enabled" #트레이싱 켜기 echo 1 > /sys/kernel/debug/tracing/tracing_on echo "tracing_on" |
ftrace 추적 켜기

잠깐 기다렸다 끄고 저장

메일박스?
BMC2835 메일반스 인터페이스 주요 기능
- 하드웨어 컨트롤 및 상태 정보 교환 : CPU와 GPU 사이의 설정 정보, 상태 업데이트, 명령 실행 결과 등을 교환함
메일박스라는 용어가 하드웨어 및 소프트웨어 통신에서 사용되는 이유는 이 메커니즘의 동작 방식이 실제 메일 시스템과 유사하기 때문
irq_test1.c 열기

/mail로 검색 시 함수의 콜 스택을 볼 수 있음
<idle>일 때

task가 kworker일 때

노란 박스 부분이 인터럽트가 일어나는 시점 495 => el1h_64_irq가 핸들러를 부른다
노란 박스 부분이 in_interrupt 상태 -> 인터럽트 컨텍스트 상태

el1h_64_irq 함수
- ARM 아키텍처 기반의 시스템에서 사용되는 예외 처리 메커니즘의 일부
- ARMv8 아키텍처에서 el1h는 Exception Level 1을 의미 -> 이 부분의 코드는 커널 모드에 해당
- _64는 64bit 모드를 의미
- irq는 Interrupt Request의 약어로 인터럽트를 요청하는 역할
- el1h_64_irq 함수의 주 역할은 시스템이 인터럽트 요청을 받았을 때 해당 인터럽트를 적절하게 처리하는 것
entry-common.c 열기

el1h_64_irq_handler 함수

579, el1_interrupt() 호출 -> 566, el1_interrupt -> if else로 가는데 else부분인 __el1_irq는 553번에 있음
__elf_interrupt의 if
- __el1_pnmi : nm 마스킹을 할 수 없다 i 인터럽트를 => 시스템에서 중요해서 지금 무조건 처리해야 하면 위 부분을 탐
__elf_interrupt의 else
- __el1_irq : 마킹 후 나중에 처리 가능

- 동일하게 do_interrupt_handler 호출함
-----
in_interrupt 함수
- 현재 실행 중인 함수가 인터럽트 컨텍스트 인지 아닌지 구분

mmc_blk_ioctl_copy_from_user

- 커널 내의 MultiMediaCard(MMC) 블록 드라이버와 관련된 함수
- MMC는 플래시 메모리 카드의 한 형태로 장치에서 데이터 저장용으로 사용됨
- 함수의 목적은 사용자 공간에서 커널 공간으로 데이터를 복사하는 것
zsmalloc.c 열기

아래처럼 사용

어떻게 in_interrupt 안에 들어가 있는지 알까?
preempt.h 열기

143, #define in_interrupt() (irq_count())

- irq_count()가 시작지점
# define irq_count() ((preempt_count() & (NMI_MASK | HARDIRQ_MASK)) | softirq_cou
# define irq_count() (preempt_count() & (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_MASK))

- preempt_count와 mask 연산을 해서 셋 중 하나라도 true면 in_interrupt가 ture인 걸 알 수 있음
인터럽트 종류 3가지
NMI(Non-Maskable Interrupt)
- 중요한 시스템 이벤트나 오류 처리를 위한 마스킹(무시하거나 차단하는 것)이 불가능한 인터럽트
리눅스 커널에서는 하드웨어 인터럽트 처리를 위해 hardirq와 softirq라는 두 유형 인터럽트를 구분
hardirq
- 직접적인 하드웨어 인터럽트를 처리하며 최대한 빠르게 처리되어야 하는 작업을 담당
softirq
- hardirq 처리가 완료된 후에 비교적 우선순위가 낮은 작업을 처리하는데 사용
인터럽트 마스킹을 누가 체크해주냐?
softirq.c 열기

irq_enter

- irq_enter_rcu 호출
irq_enter_rcu

- __irq_enter_raw 호출
irq_enter_raw는 hardirq.h에 위치

__irq_enter_raw

- preempt_count_add가 보임
preempt_count_add는 preempt.h에 위치


__preempt_count_add는 아래에 위치


irq_enter vs irq_enter_rcu
RCU(Read-Copy Update)와 선점 가능 상태를 다루는 방식에서 차이
irq_enter
- 인터럽트 처리를 시작할 때 호출되며, 커널이 인터럽트 컨텍스트에 진입했음을 시스템에 알림
- 선점 카운터를 조작하여 현재 실행 중인 컨텍스트가 선점되지 않도로 함
irq_enter_rcu
- irq_enter와 유사한 역할을 하지만, RCU 관련 컨텍스트를 추가로 설정함
- RCU 읽기 섹션 내에서 안전하게 인터럽트 처리 코드를 실행할 수 있도록 함
- 컨테스트 선점을 허용한다?
set_ftrace6.sh 열기

아래 부분 추가

실행

끄기

열기

많은 인터럽트가 일어나는 것을 볼 수 있음

해제는 irq_exit_rcu에서 수행 -> ftrace로 보자

set_ftrace6.sh 수정(irq_exit, irq_exit_rcu 추가)

ftrace 실행

잠시후 끄기

파일 열기

enter와 exit가 짝을 이루는 것을 볼 수 있음

'커널' 카테고리의 다른 글
| 21. 라즈베리파이 - ArmV7 익셉션 (0) | 2025.07.17 |
|---|---|
| 20. 라즈베리파이 - 익셉션 (0) | 2025.07.16 |
| 18. 라즈베리파이 - 인터럽트 (0) | 2025.07.14 |
| 17. 라즈베리파이 - 프로세스(current) (0) | 2025.07.14 |
| 16. 라즈베리파이 - thread_info 구조체 초기화 코드 (0) | 2025.07.13 |