커널

19. 라즈베리파이 - 인터럽트 컨텍스트

corin13 2025. 7. 16. 21:10

인터럽트 컨텍스트란 현재 실행 중인 프로세스가 현재 인터럽트를 처리 중이라는 것을 의미

  • 현재 실햄중인 함수가 인터럽트 핸들러
  • 핸들러에서 호출한 함수

 

컨텍스트의 의미

  • 프로세스 실행 자체(스냅샷)
  • 현재 실행 중인 프로세스 정보를 담고 있는 레지스터 세트

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 짝을 이루는 것을 있음