中断产生流程

 中断产生流程

中断向量表

entry.S (arch\arm64\kernel)

ENTRY(vectors)
    kernel_ventry    1, sync_invalid            // Synchronous EL1t
    kernel_ventry    1, irq_invalid            // IRQ EL1t
    kernel_ventry    1, fiq_invalid            // FIQ EL1t
    kernel_ventry    1, error_invalid        // Error EL1t

    kernel_ventry    1, sync                // Synchronous EL1h
    kernel_ventry    1, irq                // IRQ EL1h
    kernel_ventry    1, fiq_invalid            // FIQ EL1h
    kernel_ventry    1, error_invalid        // Error EL1h

    kernel_ventry    0, sync                // Synchronous 64-bit EL0
    kernel_ventry    0, irq                // IRQ 64-bit EL0
    kernel_ventry    0, fiq_invalid            // FIQ 64-bit EL0
    kernel_ventry    0, error_invalid        // Error 64-bit EL0

#ifdef CONFIG_COMPAT
    kernel_ventry    0, sync_compat, 32        // Synchronous 32-bit EL0
    kernel_ventry    0, irq_compat, 32        // IRQ 32-bit EL0
    kernel_ventry    0, fiq_invalid_compat, 32    // FIQ 32-bit EL0
    kernel_ventry    0, error_invalid_compat, 32    // Error 32-bit EL0
#else
    kernel_ventry    0, sync_invalid, 32        // Synchronous 32-bit EL0
    kernel_ventry    0, irq_invalid, 32        // IRQ 32-bit EL0
    kernel_ventry    0, fiq_invalid, 32        // FIQ 32-bit EL0
    kernel_ventry    0, error_invalid, 32        // Error 32-bit EL0
#endif
END(vectors)

以el1_irq为例

el1_irq:
    kernel_entry 1
    enable_dbg
#ifdef CONFIG_TRACE_IRQFLAGS
    bl    trace_hardirqs_off
#endif

    irq_handler

#ifdef CONFIG_PREEMPT
    ldr    w24, [tsk, #TSK_TI_PREEMPT]    // get preempt count
    cbnz    w24, 1f                // preempt count != 0
    ldr    x0, [tsk, #TSK_TI_FLAGS]    // get flags
    tbz    x0, #TIF_NEED_RESCHED, 1f    // needs rescheduling?
    bl    el1_preempt
1:
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
    bl    trace_hardirqs_on
#endif
    kernel_exit 1
ENDPROC(el1_irq)

从上kernel_entry 保存现场, irq_handler是中断处理函数,kernel_exit是恢复现场

中间CONFIG_PREEMPT宏包含一段抢占调度的代码,下面介绍一下irq_handler函数

    .macro    irq_handler
    ldr_l    x1, handle_arch_irq
    mov    x0, sp
    irq_stack_entry  //栈指针切换,切换到中断栈空间
    blr    x1              //跳到handle_arch_irq函数执行
    irq_stack_exit  //栈指针切换,退出中断栈空间
    .endm

handle_arch_irq是通过set_handle_irq赋值的

路径:entry.S (kernel4.14\arch\arm64\kernel)

void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
    if (handle_arch_irq)
        return;

    handle_arch_irq = handle_irq;
}

此set_handle_irq函数是在gic_init_bases函数中调用的 set_handle_irq(gic_handle_irq);

gic_handle_irq函数路径:Irq-gic-v3.c (kernel4.14\drivers\irqchip)

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqnr;

    do {
        irqnr = gic_read_iar();//读取硬件中断号

        if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
            int err;

            if (static_key_true(&supports_deactivate))
                gic_write_eoir(irqnr);
            else
                isb();

            err = handle_domain_irq(gic_data.domain, irqnr, regs);
            if (err) {
                WARN_ONCE(true, "Unexpected interrupt received!\n");
                if (static_key_true(&supports_deactivate)) {
                    if (irqnr < 8192)
                        gic_write_dir(irqnr);
                } else {
                    gic_write_eoir(irqnr);
                }
            }
            continue;
        }
        if (irqnr < 16) {//主要用于核间通信的中断
            gic_write_eoir(irqnr);
            if (static_key_true(&supports_deactivate))
                gic_write_dir(irqnr);
#ifdef CONFIG_SMP
            /*
             * Unlike GICv2, we don't need an smp_rmb() here.
             * The control dependency from gic_read_iar to
             * the ISB in gic_write_eoir is enough to ensure
             * that any shared data read by handle_IPI will
             * be read after the ACK.
             */
            handle_IPI(irqnr, regs);
#else
            WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
            continue;
        }
    } while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}

static inline int handle_domain_irq(struct irq_domain *domain,
                    unsigned int hwirq, struct pt_regs *regs)
{
    return __handle_domain_irq(domain, hwirq, true, regs);
}

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
            bool lookup, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    unsigned int irq = hwirq;
    int ret = 0;

    irq_enter();//进入中断上下文

#ifdef CONFIG_IRQ_DOMAIN
    if (lookup)
        irq = irq_find_mapping(domain, hwirq);//根据硬件中断号找到对应的软件中断号

#endif

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(!irq || irq >= nr_irqs)) {
        ack_bad_irq(irq);
        ret = -EINVAL;
    } else {
        generic_handle_irq(irq);//进入一个中断的处理函数
    }

    irq_exit();//退出中断上下文
    set_irq_regs(old_regs);
    return ret;
}‘

generic_handle_irq函数如下:

int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
        return -EINVAL;
    generic_handle_irq_desc(desc);
    return 0;
}

static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
    desc->handle_irq(desc);
}
 

desc->handle_irq通过gic_irq_domain_map初始化的,根据不同的中断号走不同的路径

gic_irq_domain_map’如下:

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
                  irq_hw_number_t hw)
{
    struct irq_chip *chip = &gic_chip;

    if (static_key_true(&supports_deactivate))
        chip = &gic_eoimode1_chip;

    /* SGIs are private to the core kernel */
    if (hw < 16)
        return -EPERM;
    /* Nothing here */
    if (hw >= gic_data.irq_nr && hw < 8192)
        return -EPERM;
    /* Off limits */
    if (hw >= GIC_ID_NR)
        return -EPERM;

    /* PPIs *///PPI(Private Peripheral Interrupts)是指专用私有中断,通常用于CPU本地时钟等应用场景
    if (hw < 32) {
        irq_set_percpu_devid(irq);
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_percpu_devid_irq, NULL, NULL);
        irq_set_status_flags(irq, IRQ_NOAUTOEN);
    }
    /* SPIs */ //共享中断源允许多个处理器共享同一个中断源。SPI 中断主要用于处理外部设备引发的中断事件,如网络接口卡、硬盘控制器、串口控制器等外设的中断。
    if (hw >= 32 && hw < gic_data.irq_nr) {
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_fasteoi_irq, NULL, NULL);
        irq_set_probe(irq);
    }
    /* LPIs *///特定于位置的外设中断(lpi)总是基于消息的,可以来自外设,也可以来自PCle根复核。
    if (hw >= 8192 && hw < GIC_ID_NR) {
        if (!gic_dist_supports_lpis())
            return -EPERM;
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_fasteoi_irq, NULL, NULL);
    }

    return 0;
}

 

通过gic_irq_domain_map函数可以看出desc->handle_irq(desc);是handle_percpu_devid_irq和handle_fasteoi_irq二选一

handle_percpu_devid_irq如下:

void handle_percpu_devid_irq(struct irq_desc *desc)
{
    struct irq_chip *chip = irq_desc_get_chip(desc);
    struct irqaction *action = desc->action;
    unsigned int irq = irq_desc_get_irq(desc);
    irqreturn_t res;

    /*
     * PER CPU interrupts are not serialized. Do not touch
     * desc->tot_count.
     */
    __kstat_incr_irqs_this_cpu(desc);

    if (chip->irq_ack)
        chip->irq_ack(&desc->irq_data);

    if (likely(action)) {
        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));//此处调用的就是注册中断时传递的中断处理函数
        trace_irq_handler_exit(irq, action, res);
    } else {
        unsigned int cpu = smp_processor_id();
        bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);

        if (enabled)
            irq_percpu_disable(desc, cpu);

        pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n",
                enabled ? " and unmasked" : "", irq, cpu);
    }

    if (chip->irq_eoi)
        chip->irq_eoi(&desc->irq_data);
}

handle_fasteoi_irq如下:

void handle_fasteoi_irq(struct irq_desc *desc)
{
    struct irq_chip *chip = desc->irq_data.chip;

    raw_spin_lock(&desc->lock);

    if (!irq_may_run(desc))
        goto out;

    desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

    /*
     * If its disabled or no action available
     * then mask it and get out of here:
     */
    if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
        desc->istate |= IRQS_PENDING;
        mask_irq(desc);
        goto out;
    }

    kstat_incr_irqs_this_cpu(desc);
    if (desc->istate & IRQS_ONESHOT)
        mask_irq(desc);

    preflow_handler(desc);
    handle_irq_event(desc);

    cond_unmask_eoi_irq(desc, chip);

    raw_spin_unlock(&desc->lock);
    return;
out:
    if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
        chip->irq_eoi(&desc->irq_data);
    raw_spin_unlock(&desc->lock);
}

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
    irqreturn_t ret;

    desc->istate &= ~IRQS_PENDING;
    irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);//设置中断状态
    raw_spin_unlock(&desc->lock);

    ret = handle_irq_event_percpu(desc);

    raw_spin_lock(&desc->lock);
    irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);//清除中断状态
    return ret;
}
 

irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
{
    irqreturn_t retval;
    unsigned int flags = 0;

    retval = __handle_irq_event_percpu(desc, &flags);

    add_interrupt_randomness(desc->irq_data.irq, flags);

    if (!noirqdebug)
        note_interrupt(desc, retval);
    return retval;
}

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
    irqreturn_t retval = IRQ_NONE;
    unsigned int irq = desc->irq_data.irq;
    struct irqaction *action;

    for_each_action_of_desc(desc, action) {//处理中断描述中的每一个action
        irqreturn_t res;

        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, action->dev_id);//此处调用的就是注册中断时传递的中断处理函数
        trace_irq_handler_exit(irq, action, res);

        if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
                  irq, action->handler))
            local_irq_disable();

        switch (res) {
        case IRQ_WAKE_THREAD:
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */
            if (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break;
            }

            __irq_wake_thread(desc, action);//此处中断线程化唤醒线程

            /* Fall through to add to randomness */
        case IRQ_HANDLED:
            *flags |= action->flags;
            break;

        default:
            break;
        }

        retval |= res;
    }

    return retval;
}
 

handle_IPI后续再写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/326368.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

keil移植MQTT时GNU语法报错

keil移植MQTT时GNU语法报错 目录 keil移植MQTT时GNU语法报错一、原因分析二、解决方法方法1方法2 背景描述&#xff1a; STM32F103C8T6ESP8266 WIFI模块&#xff0c;使用CubeMX生成简单的FreeRTOS代码&#xff0c;调通UART1和UART3&#xff0c;UART3与WIFI模块连接&#xff0c;…

FineBI:简介

1 介绍 FineBI 是帆软软件有限公司推出的一款商业智能&#xff08;Business Intelligence&#xff09;产品。 FineBI 是定位于自助大数据分析的 BI 工具&#xff0c;能够帮助企业的业务人员和数据分析师&#xff0c;开展以问题导向的探索式分析。 2 现阶段数据分析弊端 现阶…

PySimpleGUI图形界面实例|PDF表格转换Excel文件

实例要求&#xff1a; 使用PySimpleGUI做一个把单位考勤系统导出的pdf文件合并输出Excel的应用&#xff0c;故事出自&#xff1a;https://hannyang.blog.csdn.net/article/details/135395946 当时时间紧&#xff0c;没有好好做界面且输出csv文件了事。今天趁周六休息&#xf…

初中数学:几何题的相关解题原则总结

一、多问类型的几何题 我们做题&#xff0c;应该都遇到过这类几何题目&#xff0c;就是&#xff0c;三个小问&#xff0c;每个小问对应一个几何图像&#xff0c;而且&#xff0c;渐渐复杂。这种题目&#xff0c;大多数有一个变化的条件&#xff0c;比如&#xff0c;动点、角度…

从零开始:教你如何规划和开发一款优质的教育网校APP

本篇文章&#xff0c;笔者将从规划和开发的角度&#xff0c;详细介绍如何从零开始打造一款优质的教育网校APP。 第一步&#xff1a;明确需求和目标 在开始规划之前&#xff0c;我们首先需要明确教育网校APP的目标受众是谁&#xff0c;提供哪些课程&#xff0c;以及期望达到的…

基于SpringBoot+Vue人力资源管理系统(前后端分离)

该项目完全免费 系统介绍 基于 SpringBootVue 实现的人力资源管理系统是为了提高企业人力资源管理水平而开发的。主要目标是通过对员工 及人力资源活动信息&#xff08;考勤、工资 ) 等的编制来提高企业效率。 系统一共分为五大菜单项&#xff0c;分别是首页、薪资管理、权…

【总线接口】1.以Xilinx开发板为例,直观的认识硬件板卡和接口

初接触硬件&#xff0c;五花八门的总线、接口一定会让你有些疑惑&#xff0c;我尝试用一系列文章来解开你的疑惑 系列文章 【总线接口】1.以Xilinx开发板为例&#xff0c;直观的认识硬件接口 【总线接口】2.学习硬件这些年接触过的硬件接口、总线 大汇总 【总线接口】…

Qt/QML编程学习之心得:QSocketNotifier(二十一)

QSocketNotifier在Qt中怎么使用? QSocketNotifier使Qt的事件循环与其他基于文件描述符的事件循环集成成为可能。在Qt的主事件循环(QCoreApplication::exec())中检测到文件描述符操作。 使用低级(通常是特定于平台的)API打开设备后,可以创建一个套接字通知程序来监视文…

【自学笔记】01Java基础-09Java关键字详解

介绍java&#xff08;基于java11&#xff09;中所有关键字&#xff0c;以及主要重要的关键字详解。 1 Java 11中的关键字&#xff1a; 1.1 类型声明与变量定义 boolean&#xff1a;声明布尔类型变量&#xff0c;只有两个可能值 true 或 false。byte&#xff1a;声明一个8位有…

软件测试|SQL中的null值,该如何理解?

深入理解SQL中的Null值&#xff1a;处理缺失数据的重要概念 简介 Null值在SQL中是用于表示缺失或未知数据的特殊值。本文将深入探讨Null值的概念、处理方法和注意事项&#xff0c;以帮助读者更好地理解和处理SQL中的缺失数据。 在SQL数据库中&#xff0c;Null值是一种特殊的…

第九节HarmonyOS 常用基础组件10-TextClock

1、描述 TextClock组件通过文本将当前系统时间显示在设备上。支持不同时区的时间显示&#xff0c;最高精度到秒级。 2、接口 TextClock(options?: {timeZoneOffset?: number, controller?: TextClockController}) 3、参数 参数名称 参数类型 必填 描述 timeZoneOffs…

同步检查继电器 DT-1/200 100V 板后接线 面板安装 JOSEF约瑟

系列型号 DT-1/200同步检查继电器; DT-1/160同步检查继电器; DT-1/130同步检查继电器; DT-1/120同步检查继电器; DT-1/90同步检查继电器; DT-1/254同步检查继电器; 一、用途 DT-1型同步检查继电器用于两端供电线路的自动重合闸线路中&#xff0c;其作用在于检查线路上电压的存…