• ARM64 linux 中断处理--架构


    中断的发展背景

    在计算机的发展过程中,外设的速度长期是低于CPU速度的,CPU 不能长期处于等待外设的状态,因此提供了外设通知CPU的方式--中断。

    中断描述架构

    中断架构分为三部分:

    1、中断源,一般由外设产生,称之为 peripheral,是中断发送方;

    2、中断控制器, 一般称之为 GIC Controller,负责对中断进行优先级,屏蔽等管理,并根据一定规则路由到CPU;

    3、CPU,这是中断的接收方 ,负责处理中断;

    中断控制器

    1. struct irq_chip {
    2. const char *name;
    3. void (*irq_enable)(struct irq_data *data);
    4. void (*irq_disable)(struct irq_data *data);
    5. void (*irq_mask)(struct irq_data *data);
    6. void (*irq_unmask)(struct irq_data *data);
    7. void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
    8. void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
    9. ...
    10. };

    linux 使用 struct irq_chip 来描述中断控制器,它提供了对于中断的 enable, disable, mask, unmask等操作。

    中断描述

    1. struct irq_desc {
    2. const char *name;
    3. unsigned int depth;
    4. struct irqaction *action;
    5. struct irq_data irq_data;
    6. struct cpumask *percpu_enabled;
    7. ...
    8. };

    对于每个中断,linux 使用 irq_desc 来描述,它提供了中断 name, 处理函数, action以及目标cpu.

    中断数据

    1. struct irq_data {
    2. struct irq_chip *chip;
    3. struct irq_domain *domain;
    4. unsigned int irq;
    5. unsigned long hwirq;
    6. ...
    7. };

    irq_chip"结构体中的每个函数指针,都会携带一个指向struct irq_data的指针作为参数,可见这个"irq_data"与中断控制必定关系密切。也就是说,在"irq_desc"的定义中,与中断控制器紧密联系的这部分被单独提取出来,构成了"irq_data"结构体.

    三者关系

    中断初始化 

    在启动过程中:

    1. irq 解析 dts 中包含 interrupt-controller 的 node, 确定为 intc, 然后分配 irq_chip 来描述
    2. irq 会对每一个从 dtb 中解析的 irq 都分配 irq_desc 来描述
    3. 通过 irq_domain 来建立 hwirq 和 irq 的映射,并确定处理函数
    4. 在 gic_init_bases 通过 irq_domain_create_tree 构建.

    中断映射

    irq domain 创建好之后, irq 和 hwirq 映射关系是空的。因此需要提供映射的方式。

    典型映射方式

    1. 线性映射:线性映射其实就是一个lookup_table, 进行线性映射的方式要求 hwirq 不能太多。使用接口irq_domain_add_linear。
    2. radix映射:radix是建立一个 radix-tree,然后以 hwirq 为 index,irq 为 value 添加。使用 irq_domain_add_tree 进行创建映射。
    3. no-map: 有些中断控制器很强,可以通过寄存器配置HW interrupt ID而不是由物理连接决定的,在这种情况下,不需要进行映射,我们直接把IRQ number写入HW interrupt ID配置寄存器就OK了,这时候,生成的HW interrupt ID就是IRQ number,也就不需要进行mapping了。对于这种类型的映射,其接口API是 irq_domain_add_nomap.

    linux 目前大多采用 radix-tree 方式进行映射

    映射流程 

     首先,每个 hwirq 的信息描述通过 dts 进行描述

    其次,每个 irq 需要对应一个 struct irq_desc

    最后,建立 hwirq 到 irq 的映射

    中断处理

    中断上下文

     hardirq context 更新

    1. 进入中断
    1. /* arm64/kernel/entry-common.c */
    2. void __el1_irq(void)
    3. {
    4. enter_from_kernel_mode(regs);
    5. irq_enter_rcu();
    6. do_interrupt_handler(regs, handler);
    7. irq_exit_rcu();
    8. }
    9. /* kernel/softirq.c */
    10. void irq_enter_rcu(void) {
    11. __irq_enter_raw();
    12. }
    13. #define __irq_enter_raw() \
    14. do { \
    15. preempt_count_add(HARDIRQ_OFFSET); \
    16. lockdep_hardirq_enter(); \
    17. } while (0)

    在进入 hardirq 时, hardirq count 加1.

    2.退出中断

    1. /* kernel/softirq.c */
    2. void irq_exit_rcu(void)
    3. {
    4. __irq_exit_rcu();
    5. /* must be last! */
    6. lockdep_hardirq_exit();
    7. }
    8. static inline void __irq_exit_rcu(void)
    9. {
    10. ...
    11. preempt_count_sub(HARDIRQ_OFFSET);
    12. }

    在退出 hardirq 时,hardirq count 减1.

    hardirq context 解决的是当前是否在中断中,但是中断的竞争的解决是通过 irq_enable,irq_disable 解决的。

    softirq context 更新

    1. 进入 softirq
    1. static inline void softirq_handle_begin(void)
    2. {
    3. __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
    4. }

          2. 退出 softirq

    1. static inline void softirq_handle_end(void)
    2. {
    3. __local_bh_enable(SOFTIRQ_OFFSET);
    4. }

    对于当前 cpu, 当触发softirq 时,只需要更新 SOFTIRQ_OFFSET, 也就是表示是否在中断上下文中。

    参考

    https://zhuanlan.zhihu.com/p/83709066

    中断子系统 - 蜗窝科技

  • 相关阅读:
    Linux 命令(194)—— ethtool 命令
    verilog 实现常用加法器
    GaN场效应晶体管LMG3422R030RQZR,LMG3425R030RQZR 工业电源
    Windows10系统鼠标移出屏幕右侧
    Linux基本命令简单介绍
    MQ高级-服务异步通信
    SVM——支持向量机(二)
    kubernetes-Pod
    用 Python 自动创建 Markdown 表格 - 每天5分钟玩转 GPT 编程系列(4)
    Apache Calcite 快速入门指南
  • 原文地址:https://blog.csdn.net/kakaBack/article/details/126573387