英语

Linux 通用 IRQ 处理

版权:

© 2005-2010: Thomas Gleixner

版权:

© 2005-2006: Ingo Molnar

简介

通用中断处理层旨在为设备驱动程序提供完整的中断处理抽象。它能够处理所有不同类型的中断控制器硬件。设备驱动程序使用通用 API 函数来请求、启用、禁用和释放中断。驱动程序无需了解任何中断硬件细节,因此它们可以在不同的平台上使用,而无需更改代码。

本文档提供给希望在通用 IRQ 处理层的帮助下为其架构实现中断子系统的开发人员。

原理

Linux 中中断处理的原始实现使用 __do_IRQ() 超级处理程序,该程序能够处理每种类型的中断逻辑。

最初,Russell King 确定了不同类型的处理程序,以便在 Linux 2.5/2.6 中构建 ARM 中断处理程序实现的相当通用的集合。他区分了

  • 电平类型

  • 边沿类型

  • 简单类型

在实现过程中,我们确定了另一种类型

  • 快速 EOI 类型

在 __do_IRQ() 超级处理程序的 SMP 世界中,确定了另一种类型

  • 每个 CPU 类型

这种高级 IRQ 处理程序的拆分实现使我们能够针对每种特定的中断类型优化中断处理流程。这降低了特定代码路径的复杂性,并允许对给定类型进行优化处理。

原始的通用 IRQ 实现使用 hw_interrupt_type 结构及其 ->ack->end [等等] 回调来区分超级处理程序中的流程控制。这导致流程逻辑和底层硬件逻辑的混合,并且还导致不必要的代码重复:例如,在 i386 中,有一个 ioapic_level_irq 和一个 ioapic_edge_irq IRQ 类型,它们共享许多底层细节,但具有不同的流程处理。

更自然的抽象是清晰地分离“irq 流程”和“芯片细节”。

分析一些架构的 IRQ 子系统实现表明,它们中的大多数可以使用一组通用的“irq 流程”方法,而只需要添加芯片级的特定代码。这种分离对于需要 IRQ 流程本身中特定怪癖的(子)架构也很有价值,但不需要芯片细节 - 因此提供了更透明的 IRQ 子系统设计。

每个中断描述符都分配有其自己的高级流程处理程序,通常是通用实现之一。(这种高级流程处理程序实现也使得可以简单地提供多路分解处理程序,这些处理程序可以在各种架构上的嵌入式平台中找到。)

这种分离使通用中断处理层更加灵活和可扩展。例如,(子)架构可以对“电平类型”中断使用通用 IRQ 流程实现,并添加(子)架构特定的“边沿类型”实现。

为了使向新模型的过渡更容易并防止现有实现的破坏,__do_IRQ() 超级处理程序仍然可用。这导致暂时出现某种双重性。随着时间的推移,新模型应该在越来越多的架构中使用,因为它能够实现更小、更简洁的 IRQ 子系统。它现在已被弃用三年,即将被删除。

已知错误和假设

没有(祈祷好运)。

抽象层

中断代码中存在三个主要的抽象级别

  1. 高级驱动程序 API

  2. 高级 IRQ 流程处理程序

  3. 芯片级硬件封装

中断控制流程

每个中断都由中断描述符结构 irq_desc 描述。中断由一个“无符号整数”数值引用,该数值选择描述符结构数组中相应的描述符结构。描述符结构包含状态信息以及指向分配给此中断的中断流程方法和中断芯片结构的指针。

每当发生中断时,底层架构代码通过调用 desc->handle_irq() 调用通用中断代码。此高级 IRQ 处理函数仅使用由分配的芯片描述符结构引用的 desc->irq_data.chip 原语。

高级驱动程序 API

高级驱动程序 API 由以下函数组成

有关详细信息,请参阅自动生成的功能文档。

高级 IRQ 流程处理程序

通用层提供一组预定义的 irq 流程方法

中断流程处理程序(预定义或架构特定的)由架构在启动期间或设备初始化期间分配给特定的中断。

默认流程实现

辅助函数

辅助函数调用芯片原语,并由默认流程实现使用。实现了以下辅助函数(简化摘录)

default_enable(struct irq_data *data)
{
    desc->irq_data.chip->irq_unmask(data);
}

default_disable(struct irq_data *data)
{
    if (!delay_disable(data))
        desc->irq_data.chip->irq_mask(data);
}

default_ack(struct irq_data *data)
{
    chip->irq_ack(data);
}

default_mask_ack(struct irq_data *data)
{
    if (chip->irq_mask_ack) {
        chip->irq_mask_ack(data);
    } else {
        chip->irq_mask(data);
        chip->irq_ack(data);
    }
}

noop(struct irq_data *data)
{
}

默认流程处理程序实现

默认电平 IRQ 流程处理程序

handle_level_irq 为电平触发的中断提供通用实现。

实现以下控制流程(简化摘录)

desc->irq_data.chip->irq_mask_ack();
handle_irq_event(desc->action);
desc->irq_data.chip->irq_unmask();
默认快速 EOI IRQ 流程处理程序

handle_fasteoi_irq 为只需要在处理程序末尾进行 EOI 的中断提供通用实现。

实现以下控制流程(简化摘录)

handle_irq_event(desc->action);
desc->irq_data.chip->irq_eoi();
默认边沿 IRQ 流程处理程序

handle_edge_irq 为边沿触发的中断提供通用实现。

实现以下控制流程(简化摘录)

if (desc->status & running) {
    desc->irq_data.chip->irq_mask_ack();
    desc->status |= pending | masked;
    return;
}
desc->irq_data.chip->irq_ack();
desc->status |= running;
do {
    if (desc->status & masked)
        desc->irq_data.chip->irq_unmask();
    desc->status &= ~pending;
    handle_irq_event(desc->action);
} while (desc->status & pending);
desc->status &= ~running;
默认简单 IRQ 流程处理程序

handle_simple_irq 为简单的中断提供通用实现。

注意

简单的流程处理程序不调用任何处理程序/芯片原语。

实现以下控制流程(简化摘录)

handle_irq_event(desc->action);
默认每个 CPU 流程处理程序

handle_percpu_irq 为每个 CPU 中断提供通用实现。

每个 CPU 中断仅在 SMP 上可用,并且处理程序提供了简化的版本,无需锁定。

实现以下控制流程(简化摘录)

if (desc->irq_data.chip->irq_ack)
    desc->irq_data.chip->irq_ack();
handle_irq_event(desc->action);
if (desc->irq_data.chip->irq_eoi)
    desc->irq_data.chip->irq_eoi();
EOI 边沿 IRQ 流程处理程序

handle_edge_eoi_irq 提供了边沿处理程序的畸形版本,它仅用于驯服 powerpc/cell 上严重损坏的 irq 控制器。

错误 IRQ 流程处理程序

handle_bad_irq 用于没有分配真正处理程序的虚假中断。

怪癖和优化

通用函数适用于“干净”的架构和芯片,这些架构和芯片没有平台特定的 IRQ 处理怪癖。如果架构需要在“流程”级别上实现怪癖,则可以通过覆盖高级 irq 流程处理程序来实现。

延迟中断禁用

这个每个中断可选择的特性,由 Russell King 在 ARM 中断实现中引入,在调用 disable_irq() 时,并不会在硬件层面上屏蔽中断。中断保持启用状态,并在发生中断事件时在流处理器中被屏蔽。这可以防止在硬件层面上禁用中断时,丢失那些不存储边沿中断事件的硬件上的边沿中断。当在设置了 IRQ_DISABLED 标志时发生中断,那么中断将在硬件层面上被屏蔽,并且设置 IRQ_PENDING 位。当通过 enable_irq() 重新启用中断时,会检查 pending 位,如果该位已设置,则会通过硬件或软件重新发送机制重新发送中断。(当您想要使用延迟中断禁用功能,并且您的硬件无法重新触发中断时,则必须启用 CONFIG_HARDIRQS_SW_RESEND。)延迟中断禁用是不可配置的。

芯片级硬件封装

芯片级硬件描述符结构 irq_chip 包含所有由 irq 流实现使用的直接芯片相关函数。

  • irq_ack

  • irq_mask_ack - 可选,推荐用于性能优化

  • irq_mask

  • irq_unmask

  • irq_eoi - 可选,EOI 流处理程序需要

  • irq_retrigger - 可选

  • irq_set_type - 可选

  • irq_set_wake - 可选

这些基本操作严格来说就是它们字面上的意思:ack 表示确认,masking 表示屏蔽 IRQ 线等等。由流处理程序来使用这些低级功能的基本单元。

__do_IRQ 入口点

最初的实现 __do_IRQ() 是所有类型中断的备用入口点。它已不再存在。

这个处理程序被证明并不适用于所有中断硬件,因此针对边沿/电平/简单/每个 CPU 中断进行了功能拆分重新实现。这不仅是一个功能上的优化,还缩短了中断的代码路径。

SMP 上的锁定

芯片寄存器的锁定取决于定义芯片基本操作的架构。每个中断结构由通用层的 desc->lock 保护。

通用中断芯片

为了避免重复实现相同的 IRQ 芯片,核心提供了一个可配置的通用中断芯片实现。开发人员在自己实现略有不同的相同功能之前,应仔细检查通用芯片是否适合他们的需求。

void irq_gc_noop(struct irq_data *d)

空操作函数

参数

struct irq_data *d

irq_data

void irq_gc_mask_disable_reg(struct irq_data *d)

通过禁用寄存器屏蔽芯片

参数

struct irq_data *d

irq_data

描述

芯片具有单独的启用/禁用寄存器,而不是单个屏蔽寄存器。

void irq_gc_mask_set_bit(struct irq_data *d)

通过在屏蔽寄存器中设置位来屏蔽芯片

参数

struct irq_data *d

irq_data

描述

芯片具有单个屏蔽寄存器。此寄存器的值被缓存并通过 gc->lock 保护。

void irq_gc_mask_clr_bit(struct irq_data *d)

通过在屏蔽寄存器中清除位来屏蔽芯片

参数

struct irq_data *d

irq_data

描述

芯片具有单个屏蔽寄存器。此寄存器的值被缓存并通过 gc->lock 保护。

void irq_gc_unmask_enable_reg(struct irq_data *d)

通过启用寄存器取消屏蔽芯片

参数

struct irq_data *d

irq_data

描述

芯片具有单独的启用/禁用寄存器,而不是单个屏蔽寄存器。

void irq_gc_ack_set_bit(struct irq_data *d)

通过设置位来确认挂起的中断

参数

struct irq_data *d

irq_data

int irq_gc_set_wake(struct irq_data *d, unsigned int on)

为中断设置/清除唤醒位

参数

struct irq_data *d

irq_data

unsigned int on

指示是否应设置或清除唤醒位

描述

对于那些唤醒挂起功能未在单独的寄存器中配置,并且唤醒激活状态仅存储在位掩码中的芯片。

struct irq_chip_generic *irq_alloc_generic_chip(const char *name, int num_ct, unsigned int irq_base, void __iomem *reg_base, irq_flow_handler_t handler)

分配一个通用芯片并初始化它

参数

const char *name

irq 芯片的名称

int num_ct

与此关联的 irq_chip_type 实例的数量

unsigned int irq_base

此芯片的中断基数

void __iomem *reg_base

寄存器基地址(虚拟)

irq_flow_handler_t handler

与此芯片关联的默认流处理程序

描述

返回一个初始化的 irq_chip_generic 结构。该芯片默认使用主(索引 0)irq_chip_type 和 **handler**

int irq_domain_alloc_generic_chips(struct irq_domain *d, const struct irq_domain_chip_generic_info *info)

为一个 irq 域分配通用芯片

参数

struct irq_domain *d

要为其分配芯片的 irq 域

const struct irq_domain_chip_generic_info *info

通用芯片信息

返回

成功返回 0,失败返回负错误码

void irq_domain_remove_generic_chips(struct irq_domain *d)

从中断域中移除通用芯片

参数

struct irq_domain *d

要从中移除通用芯片的中断域

int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, int num_ct, const char *name, irq_flow_handler_t handler, unsigned int clr, unsigned int set, enum irq_gc_flags gcflags)

为一个 irq 域分配通用芯片

参数

struct irq_domain *d

要为其分配芯片的 irq 域

int irqs_per_chip

每个芯片处理的中断数量(最大 32 个)

int num_ct

与此关联的 irq_chip_type 实例的数量

const char *name

irq 芯片的名称

irq_flow_handler_t handler

与这些芯片关联的默认流处理程序

unsigned int clr

在映射函数中要清除的 IRQ_* 位

unsigned int set

在映射函数中要设置的 IRQ_* 位

enum irq_gc_flags gcflags

通用芯片特定的设置标志

struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq)

获取 hw_irq 的通用芯片的指针

参数

struct irq_domain *d

中断域指针

unsigned int hw_irq

硬件中断号

void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk, enum irq_gc_flags flags, unsigned int clr, unsigned int set)

使用通用芯片设置一系列中断

参数

struct irq_chip_generic *gc

保存所有数据的通用中断芯片

u32 msk

相对于 gc->irq_base 要初始化的中断的位掩码

enum irq_gc_flags flags

初始化的标志

unsigned int clr

要清除的 IRQ_* 位

unsigned int set

要设置的 IRQ_* 位

描述

设置从 gc->irq_base 开始的最大 32 个中断。注意,这会将所有中断初始化为主要 irq_chip_type 及其关联的处理程序。

int irq_setup_alt_chip(struct irq_data *d, unsigned int type)

切换到备用芯片

参数

struct irq_data *d

此中断的 irq_data

unsigned int type

要初始化的流类型

描述

只能从 chip->irq_set_type() 回调中调用。

void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, unsigned int clr, unsigned int set)

移除芯片

参数

struct irq_chip_generic *gc

保存所有数据的通用中断芯片

u32 msk

相对于 gc->irq_base 要初始化的中断的位掩码

unsigned int clr

要清除的 IRQ_* 位

unsigned int set

要设置的 IRQ_* 位

描述

移除从 gc->irq_base 开始的最大 32 个中断。

结构体

本章包含通用 IRQ 层中使用的结构的自动生成文档。

struct irq_common_data

所有中断芯片共享的每个中断数据

定义:

struct irq_common_data {
    unsigned int            __private state_use_accessors;
#ifdef CONFIG_NUMA;
    unsigned int            node;
#endif;
    void *handler_data;
    struct msi_desc         *msi_desc;
#ifdef CONFIG_SMP;
    cpumask_var_t affinity;
#endif;
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK;
    cpumask_var_t effective_affinity;
#endif;
#ifdef CONFIG_GENERIC_IRQ_IPI;
    unsigned int            ipi_offset;
#endif;
};

成员

state_use_accessors

中断芯片函数的状态信息。 使用访问器函数来处理它

node

对平衡有用的节点索引

handler_data

用于 irq_chip 方法的每个中断数据

msi_desc

MSI 描述符

affinity

SMP 上的 IRQ 亲和性。如果这是与 IPI 相关的中断,则这是可以发送 IPI 的 CPU 的掩码。

effective_affinity

SMP 上有效的 IRQ 亲和性,因为某些中断芯片不允许多个 CPU 目标。**affinity** 的子集。

ipi_offset

**affinity** 中第一个 IPI 目标 CPU 的偏移量。可选。

struct irq_data

传递给芯片函数的每个中断芯片数据

定义:

struct irq_data {
    u32 mask;
    unsigned int            irq;
    irq_hw_number_t hwirq;
    struct irq_common_data  *common;
    struct irq_chip         *chip;
    struct irq_domain       *domain;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY;
    struct irq_data         *parent_data;
#endif;
    void *chip_data;
};

成员

mask

用于访问芯片寄存器的预先计算的位掩码

irq

中断号

hwirq

硬件中断号,对于中断域是本地的

common

指向所有中断芯片共享的数据

chip

底层中断硬件访问

domain

中断转换域;负责在 hwirq 号和 Linux 中断号之间进行映射。

parent_data

指向父 struct irq_data 的指针,以支持分层 irq_domain

chip_data

芯片方法的平台特定的每个芯片的私有数据,以允许共享芯片实现

struct irq_chip

硬件中断芯片描述符

定义:

struct irq_chip {
    const char      *name;
    unsigned int    (*irq_startup)(struct irq_data *data);
    void (*irq_shutdown)(struct irq_data *data);
    void (*irq_enable)(struct irq_data *data);
    void (*irq_disable)(struct irq_data *data);
    void (*irq_ack)(struct irq_data *data);
    void (*irq_mask)(struct irq_data *data);
    void (*irq_mask_ack)(struct irq_data *data);
    void (*irq_unmask)(struct irq_data *data);
    void (*irq_eoi)(struct irq_data *data);
    int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
    int (*irq_retrigger)(struct irq_data *data);
    int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
    int (*irq_set_wake)(struct irq_data *data, unsigned int on);
    void (*irq_bus_lock)(struct irq_data *data);
    void (*irq_bus_sync_unlock)(struct irq_data *data);
#ifdef CONFIG_DEPRECATED_IRQ_CPU_ONOFFLINE;
    void (*irq_cpu_online)(struct irq_data *data);
    void (*irq_cpu_offline)(struct irq_data *data);
#endif;
    void (*irq_suspend)(struct irq_data *data);
    void (*irq_resume)(struct irq_data *data);
    void (*irq_pm_shutdown)(struct irq_data *data);
    void (*irq_calc_mask)(struct irq_data *data);
    void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
    int (*irq_request_resources)(struct irq_data *data);
    void (*irq_release_resources)(struct irq_data *data);
    void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
    void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
    int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
    int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
    int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
    void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
    void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
    int (*irq_nmi_setup)(struct irq_data *data);
    void (*irq_nmi_teardown)(struct irq_data *data);
    unsigned long   flags;
};

成员

name

/proc/interrupts 的名称

irq_startup

启动中断(如果为 NULL,则默认为 ->enable)

irq_shutdown

关闭中断(如果为 NULL,则默认为 ->disable)

irq_enable

启用中断(如果为 NULL,则默认为 chip->unmask)

irq_disable

禁用中断

irq_ack

新中断的开始

irq_mask

屏蔽中断源

irq_mask_ack

确认并屏蔽中断源

irq_unmask

取消屏蔽中断源

irq_eoi

中断结束

irq_set_affinity

在 SMP 机器上设置 CPU 亲和性。如果 force 参数为 true,则它会告诉驱动程序无条件地应用亲和性设置。不需要针对提供的亲和性掩码进行健全性检查。这用于 CPU 热插拔,其中目标 CPU 尚未在 cpu_online_mask 中设置。

irq_retrigger

将 IRQ 重新发送到 CPU

irq_set_type

设置 IRQ 的流类型(IRQ_TYPE_LEVEL/等等)

irq_set_wake

启用/禁用 IRQ 的电源管理唤醒

irq_bus_lock

锁定对慢速总线 (i2c) 芯片访问的函数

irq_bus_sync_unlock

同步和解锁慢速总线 (i2c) 芯片的函数

irq_cpu_online

为辅助 CPU 配置中断源

irq_cpu_offline

为辅助 CPU 取消配置中断源

irq_suspend

当安装一个或多个中断时,在每次芯片暂停时从核心代码调用的函数

irq_resume

当安装一个或多个中断时,在每次芯片恢复时从核心代码调用的函数

irq_pm_shutdown

在每次芯片关闭时从核心代码调用的函数

irq_calc_mask

可选函数,用于在特殊情况下设置 irq_data.mask

irq_print_chip

可选,在 show_interrupts 中打印特殊的芯片信息

irq_request_resources

可选,在调用与此 IRQ 相关的任何其他回调之前请求资源

irq_release_resources

可选,释放使用 irq_request_resources 获取的资源

irq_compose_msi_msg

可选,为 MSI 组成消息内容

irq_write_msi_msg

可选,为 MSI 写入消息内容

irq_get_irqchip_state

返回中断的内部状态

irq_set_irqchip_state

设置中断的内部状态

irq_set_vcpu_affinity

可选,在虚拟机中指定目标 vCPU

ipi_send_single

向目标 CPU 发送单个 IPI

ipi_send_mask

向 cpumask 中的目标 CPU 发送 IPI

irq_nmi_setup

在启用 NMI 之前从核心代码调用的函数

irq_nmi_teardown

在禁用 NMI 之后从核心代码调用的函数

flags

芯片特定的标志

struct irq_chip_regs

struct irq_gci 的寄存器偏移量

定义:

struct irq_chip_regs {
    unsigned long           enable;
    unsigned long           disable;
    unsigned long           mask;
    unsigned long           ack;
    unsigned long           eoi;
    unsigned long           type;
};

成员

enable

相对于 reg_base 的使能寄存器偏移量

disable

相对于 reg_base 的禁用寄存器偏移量

mask

Mask

ack

相对于 reg_base 的确认寄存器偏移量

eoi

相对于 reg_base 的 Eoi 寄存器偏移量

type

相对于 reg_base 的类型配置寄存器偏移量

struct irq_chip_type

用于流类型的通用中断芯片实例

定义:

struct irq_chip_type {
    struct irq_chip         chip;
    struct irq_chip_regs    regs;
    irq_flow_handler_t handler;
    u32 type;
    u32 mask_cache_priv;
    u32 *mask_cache;
};

成员

chip

提供回调的实际中断芯片

regs

此芯片的寄存器偏移量

handler

与此芯片关联的流处理程序

type

芯片可以处理这些流类型

mask_cache_priv

芯片类型私有的缓存掩码寄存器

mask_cache

指向缓存掩码寄存器的指针

描述

当 irq_generic_chip 需要针对不同流类型使用不同的函数和寄存器偏移量时,它可以有多个 irq_chip_type 实例。

struct irq_chip_generic

通用 irq 芯片数据结构

定义:

struct irq_chip_generic {
    raw_spinlock_t lock;
    void __iomem            *reg_base;
    u32 (*reg_readl)(void __iomem *addr);
    void (*reg_writel)(u32 val, void __iomem *addr);
    void (*suspend)(struct irq_chip_generic *gc);
    void (*resume)(struct irq_chip_generic *gc);
    unsigned int            irq_base;
    unsigned int            irq_cnt;
    u32 mask_cache;
    u32 wake_enabled;
    u32 wake_active;
    unsigned int            num_ct;
    void *private;
    unsigned long           installed;
    unsigned long           unused;
    struct irq_domain       *domain;
    struct list_head        list;
    struct irq_chip_type    chip_types[];
};

成员

lock

保护寄存器和缓存数据访问的锁

reg_base

寄存器基地址(虚拟)

reg_readl

备用 I/O 访问器(默认为 readl,如果为 NULL)

reg_writel

备用 I/O 访问器(默认为 writel,如果为 NULL)

suspend

在每次芯片挂起时从核心代码调用的函数;即使没有使用中断,也可以使用它来处理芯片的详细信息,而不是 irq_chip::suspend

resume

在每次芯片恢复时从核心代码调用的函数;即使没有使用中断,也可以使用它来处理芯片的详细信息,而不是 irq_chip::suspend

irq_base

此芯片的中断基数

irq_cnt

此芯片处理的中断数量

mask_cache

所有芯片类型之间共享的缓存掩码寄存器

wake_enabled

中断可以从挂起状态唤醒

wake_active

中断被标记为从挂起状态唤醒的源

num_ct

可用 irq_chip_type 实例的数量(通常为 1)

private

非通用芯片回调的私有数据

installed

表示已安装中断的位域

unused

表示未使用中断的位域

domain

中断域指针

list

用于跟踪实例的列表头

chip_types

中断 irq_chip_type 的数组

描述

请注意,当我们需要为 irq_chip_generic 实例的不同 irq 线实现不同的流机制(电平/边沿)时,irq_chip_generic 可以有多个 irq_chip_type 实现,这允许在 irq_chip_generic 实例中共享和保护状态。

enum irq_gc_flags

通用 irq 芯片的初始化标志

常量

IRQ_GC_INIT_MASK_CACHE

通过读取掩码寄存器来初始化 mask_cache

IRQ_GC_INIT_NESTED_LOCK

对于需要在父级 irq 上调用 irq_set_wake() 的 irq 芯片,将 irq 的锁类设置为嵌套。通常是 GPIO 实现

IRQ_GC_MASK_CACHE_PER_TYPE

掩码缓存是芯片类型私有的

IRQ_GC_NO_MASK

不计算 irq_data->mask

IRQ_GC_BE_IO

使用大端寄存器访问(默认值:LE)

struct irq_domain_chip_generic_info

通用芯片信息结构

定义:

struct irq_domain_chip_generic_info {
    const char              *name;
    irq_flow_handler_t handler;
    unsigned int            irqs_per_chip;
    unsigned int            num_ct;
    unsigned int            irq_flags_to_clear;
    unsigned int            irq_flags_to_set;
    enum irq_gc_flags       gc_flags;
    int (*init)(struct irq_chip_generic *gc);
    void (*exit)(struct irq_chip_generic *gc);
};

成员

name

通用中断芯片的名称

handler

通用中断芯片使用的中断处理程序

irqs_per_chip

每个芯片处理的中断数量(最大 32 个)

num_ct

与每个芯片关联的 irq_chip_type 实例的数量

irq_flags_to_clear

在映射函数中要清除的 IRQ_* 位

irq_flags_to_set

在映射函数中要设置的 IRQ_* 位

gc_flags

通用芯片特定的设置标志

init

在创建每个芯片时调用的函数。允许执行一些额外的芯片初始化操作。

exit

在销毁每个芯片时调用的函数。允许执行一些芯片清理操作。

struct irqaction

每个中断操作描述符

定义:

struct irqaction {
    irq_handler_t handler;
    void *dev_id;
    void __percpu           *percpu_dev_id;
    struct irqaction        *next;
    irq_handler_t thread_fn;
    struct task_struct      *thread;
    struct irqaction        *secondary;
    unsigned int            irq;
    unsigned int            flags;
    unsigned long           thread_flags;
    unsigned long           thread_mask;
    const char              *name;
    struct proc_dir_entry   *dir;
};

成员

handler

中断处理函数

dev_id

用于标识设备的 cookie

percpu_dev_id

用于标识设备的 cookie

next

指向共享中断的下一个 irqaction 的指针

thread_fn

用于线程化中断的中断处理函数

thread

用于线程化中断的线程指针

secondary

指向辅助 irqaction 的指针(强制线程化)

irq

中断号

flags

flags(请参阅上面的 IRQF_*)

thread_flags

thread 相关的标志

thread_mask

用于跟踪 thread 活动的位掩码

name

设备的名称

dir

指向 proc/irq/NN/name 条目的指针

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

为中断线添加处理程序

参数

unsigned int irq

要分配的中断线

irq_handler_t handler

发生 IRQ 时要调用的函数。线程化中断的主处理程序。如果为 NULL,则安装默认的主处理程序

unsigned long flags

处理标志

const char *name

生成此中断的设备的名称

void *dev

传递给处理函数的一个 cookie

描述

此调用分配一个中断并建立一个处理程序;有关详细信息,请参阅 request_threaded_irq() 的文档。

struct irq_affinity_notify

用于 IRQ 亲和性更改通知的上下文

定义:

struct irq_affinity_notify {
    unsigned int irq;
    struct kref kref;
    struct work_struct work;
    void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask);
    void (*release)(struct kref *ref);
};

成员

irq

通知所应用的中断

kref

引用计数,供内部使用

work

工作项,供内部使用

notify

在更改时调用的函数。这将在进程上下文中调用。

release

在释放时调用的函数。这将在进程上下文中调用。注册后,仅当调用此函数或之后时,才能释放该结构。

struct irq_affinity

用于自动 irq 亲和性分配的描述

定义:

struct irq_affinity {
    unsigned int    pre_vectors;
    unsigned int    post_vectors;
    unsigned int    nr_sets;
    unsigned int    set_size[IRQ_AFFINITY_MAX_SETS];
    void (*calc_sets)(struct irq_affinity *, unsigned int nvecs);
    void *priv;
};

成员

pre_vectors

在 MSI(-X) 向量空间的开头,不要将亲和性应用于 pre_vectors

post_vectors

在 MSI(-X) 向量空间的末尾,不要将亲和性应用于 post_vectors

nr_sets

需要亲和性分布的中断集数量

set_size

保存每个中断集大小的数组

calc_sets

用于计算中断集数量和大小的回调

priv

calc_sets 使用的私有数据,通常是指向驱动程序/设备特定数据的指针。

struct irq_affinity_desc

中断亲和性描述符

定义:

struct irq_affinity_desc {
    struct cpumask  mask;
    unsigned int    is_managed : 1;
};

成员

mask

用于保存亲和性分配的 cpumask

is_managed

如果中断在内部管理,则为 1

int irq_update_affinity_hint(unsigned int irq, const struct cpumask *m)

更新亲和性提示

参数

unsigned int irq

要更新的中断

const struct cpumask *m

cpumask 指针(NULL 清除提示)

描述

更新亲和性提示,但不更改中断的亲和性。

int irq_set_affinity_and_hint(unsigned int irq, const struct cpumask *m)

更新亲和性提示并将提供的 cpumask 应用于中断

参数

unsigned int irq

要更新的中断

const struct cpumask *m

cpumask 指针(NULL 清除提示)

描述

更新亲和性提示,如果 m 不为 NULL,则将其作为该中断的亲和性应用。

struct irq_fwspec

通用 IRQ 说明符结构

定义:

struct irq_fwspec {
    struct fwnode_handle *fwnode;
    int param_count;
    u32 param[IRQ_DOMAIN_IRQ_SPEC_PARAMS];
};

成员

fwnode

指向固件特定描述符的指针

param_count

设备特定参数的数量

param

设备特定参数

描述

此结构直接模仿 of_phandle_args,用于传递中断的设备特定描述。

struct irq_domain_ops

用于 irq_domain 对象的方法

定义:

struct irq_domain_ops {
    int (*match)(struct irq_domain *d, struct device_node *node, enum irq_domain_bus_token bus_token);
    int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec, enum irq_domain_bus_token bus_token);
    int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
    void (*unmap)(struct irq_domain *d, unsigned int virq);
    int (*xlate)(struct irq_domain *d, struct device_node *node,const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY;
    int (*alloc)(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, void *arg);
    void (*free)(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs);
    int (*activate)(struct irq_domain *d, struct irq_data *irqd, bool reserve);
    void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
    int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *out_hwirq, unsigned int *out_type);
#endif;
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS;
    void (*debug_show)(struct seq_file *m, struct irq_domain *d, struct irq_data *irqd, int ind);
#endif;
};

成员

match

将中断控制器设备节点与主机匹配,匹配时返回 1

select

匹配中断控制器固件规范。它比 match 更通用,因为它接收一个完整的 struct irq_fwspec。因此,如果提供,则首选 select。匹配时返回 1。

映射

创建或更新虚拟中断号和硬件中断号之间的映射。对于给定的映射,此函数仅调用一次。

取消映射

处置此类映射

转换

给定设备树节点和中断说明符,解码硬件中断号和 Linux 中断类型值。

分配

virq 开始分配 nr_irqs 个中断。

释放

virq 开始释放 nr_irqs 个中断。

激活

激活硬件中的一个中断(irqd)。如果设置了 reserve,则仅保留向量。如果未设置,则分配向量(从 request_irq() 调用)。

停用

禁用一个中断 (irqd)。

翻译

给定 fwspec,解码硬件中断号 (out_hwirq) 和 Linux 中断类型值 (out_type)。这是 xlate 的通用版本(通过 struct irq_fwspec),如果提供,则首选此方法。

debug_show

用于域在 debugfs 中显示中断的特定数据。

描述

以下函数由驱动程序提供,并在创建新映射或处置旧映射时调用。然后,驱动程序可以继续执行所需的任何内部数据结构管理。从 map() 返回时,它还需要设置 irq_desc。

struct irq_domain

硬件中断号转换对象

定义:

struct irq_domain {
    struct list_head                link;
    const char                      *name;
    const struct irq_domain_ops     *ops;
    void *host_data;
    unsigned int                    flags;
    unsigned int                    mapcount;
    struct mutex                    mutex;
    struct irq_domain               *root;
    struct fwnode_handle            *fwnode;
    enum irq_domain_bus_token       bus_token;
    struct irq_domain_chip_generic  *gc;
    struct device                   *dev;
    struct device                   *pm_dev;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY;
    struct irq_domain               *parent;
#endif;
#ifdef CONFIG_GENERIC_MSI_IRQ;
    const struct msi_parent_ops     *msi_parent_ops;
#endif;
    void (*exit)(struct irq_domain *d);
    irq_hw_number_t hwirq_max;
    unsigned int                    revmap_size;
    struct radix_tree_root          revmap_tree;
    struct irq_data __rcu           *revmap[] ;
};

成员

链接

全局 irq_domain 列表中的元素。

name

中断域的名称

ops

指向 irq_domain 方法的指针

host_data

所有者使用的私有数据指针。不会被 irq_domain 核心代码触及。

flags

每个 irq_domain 的标志

mapcount

已映射的中断数

mutex

域锁,分层域使用根域的锁

root

指向根域的指针,如果是非分层结构,则指向包含结构

fwnode

指向与 irq_domain 关联的固件节点的指针。可以通过 irq_domain_get_of_node 访问器轻松将其交换为 of_node

bus_token

fwnode 的 device_node 可能用于多个 irq 域。但是,结合 bus_token,该对在系统中应该是唯一的。

gc

指向通用芯片列表的指针。有一个辅助函数用于使用通用芯片库为中断控制器驱动程序设置一个或多个通用芯片,该库使用此指针。

dev

指向实例化 irqdomain 的设备的指针。对于每个设备的中断域,这不一定与 pm_dev 相同。

pm_dev

指向可用于与中断域相关的电源管理目的的设备的指针。

parent

指向父 irq_domain 的指针,以支持分层 irq_domain

msi_parent_ops

指向每个设备域初始化 MSI 父域方法的指针

exit

域被销毁时调用的函数

hwirq_max

硬件中断号的上限。特别是为了避免与保留的硬件中断冲突/失败。可以是 ~0。

revmap_size

线性映射表 revmap 的大小

revmap_tree

不适合线性映射的硬件中断的基数映射树

revmap

irq_data 指针的线性表

描述

可选元素:Revmap 数据,供 irq 域代码在内部使用

struct irq_domain_info

域信息结构

定义:

struct irq_domain_info {
    struct fwnode_handle                    *fwnode;
    unsigned int                            domain_flags;
    unsigned int                            size;
    irq_hw_number_t hwirq_max;
    int direct_max;
    unsigned int                            hwirq_base;
    unsigned int                            virq_base;
    enum irq_domain_bus_token               bus_token;
    const char                              *name_suffix;
    const struct irq_domain_ops             *ops;
    void *host_data;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY;
    struct irq_domain                       *parent;
#endif;
    struct irq_domain_chip_generic_info     *dgc_info;
    int (*init)(struct irq_domain *d);
    void (*exit)(struct irq_domain *d);
};

成员

fwnode

中断控制器的固件节点

domain_flags

添加到域标志的其他标志

大小

线性映射的大小;仅基数映射为 0

hwirq_max

控制器支持的最大中断数

direct_max

直接映射的最大值;无限制使用 ~0;无直接映射使用 0

hwirq_base

第一个硬件中断号(仅限旧式域)

virq_base

旧式域的第一个 Linux 中断号,用于在域创建后立即关联中断

bus_token

域总线令牌

name_suffix

可选名称后缀,以避免在使用同一 fwnode 添加多个域时发生冲突

ops

域操作回调

host_data

控制器私有数据指针

parent

指向分层域中使用的父中断域的指针

dgc_info

如果为 NULL,则用于为域创建通用芯片的通用芯片信息结构指针。

init

域创建时调用的函数。允许进行一些额外的域初始化。

exit

域销毁时调用的函数。允许执行一些额外的清理操作。

struct irq_domain *irq_domain_add_linear(struct device_node *of_node, unsigned int size, const struct irq_domain_ops *ops, void *host_data)

分配并注册线性反向映射 irq_domain。

参数

struct device_node *of_node

指向中断控制器的设备树节点的指针。

unsigned int size

域中的中断数。

const struct irq_domain_ops *ops

映射/取消映射域回调

void *host_data

控制器私有数据指针

unsigned int irq_find_mapping(struct irq_domain *domain, irq_hw_number_t hwirq)

从硬件中断号查找 Linux 中断。

参数

struct irq_domain *domain

拥有此硬件中断的域

irq_hw_number_t hwirq

该域空间中的硬件中断号

提供的公共函数

本章包含导出的内核 API 函数的自动生成文档。

bool synchronize_hardirq(unsigned int irq)

等待挂起的硬 IRQ 处理程序(在其他 CPU 上)

参数

unsigned int irq

要等待的中断号

此函数等待此中断的任何挂起硬 IRQ 处理程序完成,然后返回。如果在持有 IRQ 处理程序可能需要的资源时使用此函数,将会发生死锁。它不考虑关联的线程处理程序。

不要将其用于关闭场景,在这些场景中,您必须确保所有部分(硬中断和线程处理程序)都已完成。

返回

如果线程处理程序处于活动状态,则返回 false。

此函数可以(小心)从 IRQ 上下文调用。

它不会检查硬件级别是否有一个正在飞行但尚未处理的中断,因为当禁用中断并使用中断的目标 CPU 是当前 CPU 调用时,这可能会导致死锁。

void synchronize_irq(unsigned int irq)

等待挂起的中断处理程序(在其他 CPU 上)

参数

unsigned int irq

要等待的中断号

此函数等待此中断的任何挂起中断处理程序完成,然后返回。如果在持有 IRQ 处理程序可能需要的资源时使用此函数,将会发生死锁。

只能从可抢占代码调用,因为当中断线程与 irq 关联时,它可能会休眠。

它还会可选地确保(当中断芯片支持该方法时)中断在任何 CPU 中都没有挂起并等待服务。

int irq_can_set_affinity(unsigned int irq)

检查是否可以设置给定中断的亲和性

参数

unsigned int irq

要检查的中断

bool irq_can_set_affinity_usr(unsigned int irq)

检查是否可以从用户空间设置中断的亲和性。

参数

unsigned int irq

要检查的中断

描述

类似于上面的 irq_can_set_affinity(),但额外检查了 AFFINITY_MANAGED 标志。

void irq_set_thread_affinity(struct irq_desc *desc)

通知中断线程调整亲和性。

参数

struct irq_desc *desc

已更改亲和性的中断描述符。

我们只是设置 IRQTF_AFFINITY,并将亲和性设置委托给中断线程本身。我们不能在此处调用 set_cpus_allowed_ptr(),因为我们持有 desc->lock,并且此代码可以从硬中断上下文中调用。

int irq_update_affinity_desc(unsigned int irq, struct irq_affinity_desc *affinity)

更新中断的亲和性管理。

参数

unsigned int irq

要更新的中断号。

struct irq_affinity_desc *affinity

指向亲和性描述符的指针。

描述

此接口可用于配置已分配的中断的亲和性管理。

使用时存在某些限制 - 尝试在内核配置为通用 IRQ 保留模式(在配置 GENERIC_IRQ_RESERVATION_MODE 中)时使用它将会失败,因为它可能与托管/非托管中断记账冲突。此外,尝试在已经启动或已经配置为托管的中断上使用它也会失败,因为这些意味着无效的初始状态或双重初始化。

int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)

设置给定中断的中断亲和性。

参数

unsigned int irq

要设置亲和性的中断。

const struct cpumask *cpumask

cpumask

描述

如果 cpumask 不包含在线 CPU,则失败。

int irq_force_affinity(unsigned int irq, const struct cpumask *cpumask)

强制设置给定中断的中断亲和性。

参数

unsigned int irq

要设置亲和性的中断。

const struct cpumask *cpumask

cpumask

描述

与 irq_set_affinity 相同,但不检查掩码与在线 CPU 的匹配情况。

仅用于底层 CPU 热插拔代码,我们需要在 CPU 上线之前使每个 CPU 的中断具有亲和性。

int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)

控制 IRQ 亲和性更改的通知。

参数

unsigned int irq

要启用/禁用通知的中断。

struct irq_affinity_notify *notify

通知的上下文,或 NULL 以禁用通知。必须初始化函数指针;其他字段将由此函数初始化。

必须在进程上下文中调用。只能在分配 IRQ 后启用通知,并且必须在使用 free_irq() 释放 IRQ 之前禁用通知。

int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)

设置中断的 vCPU 亲和性。

参数

unsigned int irq

要设置亲和性的中断号。

void *vcpu_info

vCPU 特定数据或指向每个 CPU 设备 ID 中断的 vCPU 特定数据的每个 CPU 数组的指针。

此函数使用 vCPU 特定数据为中断设置 vCPU 亲和性。vCPU 特定数据从外部传递,例如 KVM。一个示例代码路径如下:KVM -> IOMMU -> irq_set_vcpu_affinity()

void disable_irq_nosync(unsigned int irq)

禁用中断而不等待。

参数

unsigned int irq

要禁用的中断。

禁用选定的中断线。禁用和启用是嵌套的。与 disable_irq() 不同,此函数不保证 IRQ 处理程序的现有实例在返回之前已完成。

此函数可以从 IRQ 上下文中调用。

void disable_irq(unsigned int irq)

禁用中断并等待完成。

参数

unsigned int irq

要禁用的中断。

禁用选定的中断线。启用和禁用是嵌套的。此函数会等待此中断的任何待处理 IRQ 处理程序在返回之前完成。如果在持有 IRQ 处理程序可能需要的资源时使用此函数,则会发生死锁。

只能从可抢占代码调用,因为当中断线程与 irq 关联时,它可能会休眠。

bool disable_hardirq(unsigned int irq)

禁用中断并等待硬中断完成。

参数

unsigned int irq

要禁用的中断。

禁用选定的中断线。启用和禁用是嵌套的。此函数会等待此中断的任何待处理硬 IRQ 处理程序在返回之前完成。如果在持有硬 IRQ 处理程序可能需要的资源时使用此函数,则会发生死锁。

当用于从原子上下文中乐观地禁用中断时,必须检查返回值。

返回

如果线程处理程序处于活动状态,则返回 false。

此函数可以(小心)从 IRQ 上下文调用。

void disable_nmi_nosync(unsigned int irq)

禁用 NMI 而不等待。

参数

unsigned int irq

要禁用的中断。

禁用选定的中断线。禁用和启用是嵌套的。要禁用的中断必须已通过 request_nmi 请求。与 disable_nmi() 不同,此函数不保证 IRQ 处理程序的现有实例在返回之前已完成。

void enable_irq(unsigned int irq)

启用中断处理。

参数

unsigned int irq

要启用的中断。

撤消一次调用 disable_irq() 的效果。如果这与上次禁用匹配,则会重新启用此 IRQ 线上中断的处理。

仅当 desc->irq_data.chip->bus_lock 和 desc->chip->bus_sync_unlock 为 NULL 时,才能从 IRQ 上下文中调用此函数!

void enable_nmi(unsigned int irq)

启用 NMI 处理。

参数

unsigned int irq

要启用的中断。

要启用的中断必须已通过 request_nmi 请求。撤消一次调用 disable_nmi() 的效果。如果这与上次禁用匹配,则会重新启用此 IRQ 线上中断的处理。

int irq_set_irq_wake(unsigned int irq, unsigned int on)

控制中断电源管理唤醒。

参数

unsigned int irq

要控制的中断。

unsigned int on

启用/禁用电源管理唤醒。

启用/禁用电源管理唤醒模式,默认情况下禁用。启用和禁用必须匹配,就像它们匹配非唤醒模式支持一样。

唤醒模式允许此 IRQ 从睡眠状态(如“挂起到 RAM”)唤醒系统。

注意

中断使能/禁用状态是完全正交的

与中断唤醒的使能/禁用状态无关。可以使用 disable_irq() 禁用中断,只要该中断启用了唤醒功能,它仍然可以唤醒系统。如果情况并非如此,则需要检查底层中断芯片和相关驱动程序。

void irq_wake_thread(unsigned int irq, void *dev_id)

为 dev_id 标识的操作唤醒中断线程

参数

unsigned int irq

中断线

void *dev_id

应该唤醒线程的设备标识

const void *free_irq(unsigned int irq, void *dev_id)

释放使用 request_irq 分配的中断

参数

unsigned int irq

要释放的中断线

void *dev_id

要释放的设备标识

移除中断处理程序。该处理程序被移除,如果该中断线不再被任何驱动程序使用,则禁用该中断线。在共享 IRQ 上,调用者必须确保在调用此函数之前,在它驱动的卡上禁用中断。该函数只有在该 IRQ 的所有正在执行的中断完成后才会返回。

此函数不能从中断上下文调用。

返回传递给 request_irq 的 devname 参数。

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)

分配中断线

参数

unsigned int irq

要分配的中断线

irq_handler_t handler

当 IRQ 发生时要调用的函数。线程中断的主要处理程序。如果 handler 为 NULL 且 thread_fn != NULL,则安装默认的主要处理程序。

irq_handler_t thread_fn

从 irq 处理程序线程调用的函数。如果为 NULL,则不创建 irq 线程

unsigned long irqflags

中断类型标志

const char *devname

声明设备的 ASCII 名称

void *dev_id

传递回处理程序函数的 cookie

此调用会分配中断资源并启用中断线和 IRQ 处理。从调用此函数开始,可能会调用你的处理程序函数。由于你的处理程序函数必须清除电路板引发的任何中断,因此你必须注意初始化硬件并按正确的顺序设置中断处理程序。

如果要为设备设置线程化的 irq 处理程序,则需要提供 **handler** 和 **thread_fn**。 **handler** 仍然在硬中断上下文中调用,并且必须检查中断是否来自设备。如果是,它需要禁用设备上的中断并返回 IRQ_WAKE_THREAD,这将唤醒处理程序线程并运行 **thread_fn**。这种分离的处理程序设计对于支持共享中断是必要的。

Dev_id 必须是全局唯一的。通常,设备数据结构的地址用作 cookie。由于处理程序接收此值,因此使用它是有意义的。

如果你的中断是共享的,则必须传递非 NULL 的 dev_id,因为在释放中断时需要它。

标志

IRQF_SHARED 中断是共享的 IRQF_TRIGGER_* 指定活动边沿或电平 IRQF_ONESHOT 在中断线被屏蔽的情况下运行 thread_fn

int request_any_context_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_id)

分配中断线

参数

unsigned int irq

要分配的中断线

irq_handler_t handler

当 IRQ 发生时要调用的函数。线程中断的线程处理程序。

unsigned long flags

中断类型标志

const char *name

声明设备的 ASCII 名称

void *dev_id

传递回处理程序函数的 cookie

此调用会分配中断资源并启用中断线和 IRQ 处理。它根据上下文选择 hardirq 或线程处理方法。

如果失败,它会返回负值。如果成功,它会返回 IRQC_IS_HARDIRQ 或 IRQC_IS_NESTED。

int request_nmi(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *name, void *dev_id)

为 NMI 传递分配中断线

参数

unsigned int irq

要分配的中断线

irq_handler_t handler

当 IRQ 发生时要调用的函数。线程中断的线程处理程序。

unsigned long irqflags

中断类型标志

const char *name

声明设备的 ASCII 名称

void *dev_id

传递回处理程序函数的 cookie

此调用会分配中断资源并启用中断线和 IRQ 处理。它将 IRQ 线设置为作为 NMI 处理。

传递 NMI 的中断线不能共享,并且 IRQ 处理不能线程化。

请求用于 NMI 传递的中断线必须生成每个 CPU 的中断,并且禁用自动启用设置。

Dev_id 必须是全局唯一的。通常,设备数据结构的地址用作 cookie。由于处理程序接收此值,因此使用它是有意义的。

如果中断线不能用于传递 NMI,则函数将失败并返回负值。

bool irq_percpu_is_enabled(unsigned int irq)

检查每个 CPU 的 irq 是否已启用

参数

unsigned int irq

要检查的 Linux irq 编号

描述

必须从不可迁移的上下文中调用。返回当前 CPU 上每个 CPU 中断的启用状态。

void remove_percpu_irq(unsigned int irq, struct irqaction *act)

释放每个 CPU 的中断

参数

unsigned int irq

要释放的中断线

struct irqaction *act

中断的 irqaction

描述

用于移除早期启动过程静态设置的中断。

void free_percpu_irq(unsigned int irq, void __percpu *dev_id)

释放使用 request_percpu_irq 分配的中断

参数

unsigned int irq

要释放的中断线

void __percpu *dev_id

要释放的设备标识

移除每个 CPU 的中断处理程序。该处理程序被移除,但中断线未被禁用。必须在调用此函数之前在每个 CPU 上执行此操作。该函数只有在该 IRQ 的所有正在执行的中断完成后才会返回。

此函数不能从中断上下文调用。

int setup_percpu_irq(unsigned int irq, struct irqaction *act)

设置每个 CPU 的中断

参数

unsigned int irq

要设置的中断线

struct irqaction *act

中断的 irqaction

描述

用于在早期启动过程中静态设置每个 CPU 的中断。

int __request_percpu_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void __percpu *dev_id)

分配每个 CPU 的中断线

参数

unsigned int irq

要分配的中断线

irq_handler_t handler

当发生 IRQ 时要调用的函数。

unsigned long flags

中断类型标志 (仅限 IRQF_TIMER)

const char *devname

声明设备的 ASCII 名称

void __percpu *dev_id

传递回处理函数的每个 CPU 的 cookie

此调用分配中断资源并启用本地 CPU 上的中断。 如果中断应在其他 CPU 上启用,则必须使用 enable_percpu_irq() 在每个 CPU 上执行此操作。

Dev_id 必须是全局唯一的。它是一个每个 CPU 的变量,并且使用被中断的 CPU 的该变量的实例来调用处理程序。

int request_percpu_nmi(unsigned int irq, irq_handler_t handler, const char *name, void __percpu *dev_id)

为 NMI 传递分配每个 CPU 的中断线

参数

unsigned int irq

要分配的中断线

irq_handler_t handler

当发生 IRQ 时要调用的函数。

const char *name

声明设备的 ASCII 名称

void __percpu *dev_id

传递回处理函数的每个 CPU 的 cookie

此调用为每个 CPU 的 NMI 分配中断资源。 每个 CPU 的 NMI 必须通过调用 prepare_percpu_nmi() 进行设置,然后才能使用 enable_percpu_nmi() 在同一 CPU 上启用。

Dev_id 必须是全局唯一的。它是一个每个 CPU 的变量,并且使用被中断的 CPU 的该变量的实例来调用处理程序。

为 NMI 传递请求的中断线应禁用自动启用设置。

如果中断线不能用于传递 NMI,该函数将失败并返回负值。

int prepare_percpu_nmi(unsigned int irq)

为 NMI 传递执行 CPU 本地设置

参数

unsigned int irq

要准备 NMI 传递的中断线

此调用准备一条中断线以在当前 CPU 上传递 NMI,然后使用 enable_percpu_nmi() 启用该中断线。

作为 CPU 本地操作,应从不可抢占的上下文中调用此操作。

如果中断线不能用于传递 NMI,该函数将失败并返回负值。

void teardown_percpu_nmi(unsigned int irq)

撤销 IRQ 线的 NMI 设置

参数

unsigned int irq

应从中删除 CPU 本地 NMI 配置的中断线

此调用撤消 prepare_percpu_nmi() 完成的设置。

不应为当前 CPU 启用 IRQ 线。

作为 CPU 本地操作,应从不可抢占的上下文中调用此操作。

int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, bool *state)

返回中断的 irqchip 状态。

参数

unsigned int irq

转发到 VM 的中断线

enum irqchip_irq_state which

调用者想了解的 IRQCHIP_STATE_* 之一

bool *state

指向要存储状态的布尔值的指针

此调用会捕获中断的内部 irqchip 状态的快照,并将与阶段 which 对应的位返回到 state

如果中断控制器具有每个 CPU 的寄存器,则应在禁用抢占的情况下调用此函数。

int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, bool val)

设置转发中断的状态。

参数

unsigned int irq

转发到 VM 的中断线

enum irqchip_irq_state which

要恢复的状态(IRQCHIP_STATE_* 之一)

bool val

which 对应的值

此调用根据 which 的值设置中断的内部 irqchip 状态。

如果中断控制器具有每个 CPU 的寄存器,则应在禁用迁移的情况下调用此函数。

bool irq_has_action(unsigned int irq)

检查是否请求了中断

参数

unsigned int irq

Linux 中断号

返回

当前状态的快照

bool irq_check_status_bit(unsigned int irq, unsigned int bitmask)

检查是否设置了 irq 描述符状态中的位

参数

unsigned int irq

Linux 中断号

unsigned int bitmask

要评估的位掩码

返回

如果设置了 bitmask 中的一个位,则为 True

int irq_set_chip(unsigned int irq, const struct irq_chip *chip)

设置 irq 的 irq 芯片

参数

unsigned int irq

irq 号

const struct irq_chip *chip

指向 irq 芯片描述结构的指针

int irq_set_irq_type(unsigned int irq, unsigned int type)

设置 irq 的 irq 触发类型

参数

unsigned int irq

irq 号

unsigned int type

IRQ_TYPE_{LEVEL,EDGE}_* 值 - 请参阅 include/linux/irq.h

int irq_set_handler_data(unsigned int irq, void *data)

设置 irq 的 irq 处理程序数据

参数

unsigned int irq

中断号

void *data

指向中断特定数据的指针

为 irq 设置硬件 irq 控制器数据

int irq_set_chip_data(unsigned int irq, void *data)

设置 irq 的 irq 芯片数据

参数

unsigned int irq

中断号

void *data

指向芯片特定数据的指针

为 irq 设置硬件 irq 芯片数据

void handle_simple_irq(struct irq_desc *desc)

简单和软件解码的 IRQ。

参数

struct irq_desc *desc

此 irq 的中断描述结构

简单中断要么从多路复用中断处理程序发送,要么来自硬件,其中不需要中断硬件控制。

注意

如果需要,调用者应处理确认、清除、屏蔽和

取消屏蔽问题。

void handle_untracked_irq(struct irq_desc *desc)

简单和软件解码的 IRQ。

参数

struct irq_desc *desc

此 irq 的中断描述结构

当多路分解器不知道是哪个设备产生的多路复用中断时,未跟踪的中断会从多路分解中断处理程序发送。通过此处处理的 IRQ 不会进行统计跟踪、随机性或杂散中断检测。

注意

与 handle_simple_irq 类似,调用者需要处理

必要的 ack、clear、mask 和 unmask 问题。

void handle_level_irq(struct irq_desc *desc)

电平类型 irq 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

只要硬件线路具有有效电平,电平类型的中断就会处于活动状态。这可能需要屏蔽中断,并在关联的处理程序确认设备后取消屏蔽中断,以便中断线路恢复为非活动状态。

void handle_fasteoi_irq(struct irq_desc *desc)

透明控制器的 irq 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

只会向芯片发出一个回调:在中断得到服务时调用 ->eoi()。这支持了现代形式的中断处理程序,这些处理程序以透明方式处理硬件中的流细节。

void handle_fasteoi_nmi(struct irq_desc *desc)

NMI 中断线的 irq 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

一个简单的 NMI 安全处理程序,考虑了 request_nmi 的限制。

只会向芯片发出一个回调:在中断得到服务时调用 ->eoi()。这支持了现代形式的中断处理程序,这些处理程序以透明方式处理硬件中的流细节。

void handle_edge_irq(struct irq_desc *desc)

边沿类型 IRQ 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

中断发生在硬件信号的下降沿和/或上升沿。该事件被锁定到 irq 控制器硬件中,必须进行确认才能重新启用。在确认之后,即使在关联的事件处理程序处理第一个中断之前,也可能在同一源上发生另一个中断。如果发生这种情况,可能需要根据控制器硬件禁用(屏蔽)中断。这需要在处理程序运行时处理已到达的中断的循环内重新启用中断。如果处理了所有挂起的中断,则退出循环。

void handle_fasteoi_ack_irq(struct irq_desc *desc)

堆叠在透明控制器上的边沿层次结构的 irq 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

handle_fasteoi_irq() 类似,但用于 irq_chip 也需要调用其 ->irq_ack() 函数的层次结构。

void handle_fasteoi_mask_irq(struct irq_desc *desc)

堆叠在透明控制器上的电平层次结构的 irq 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

handle_fasteoi_irq() 类似,但用于 irq_chip 也需要调用其 ->irq_mask_ack() 函数的层次结构。

int irq_chip_set_parent_state(struct irq_data *data, enum irqchip_irq_state which, bool val)

设置父中断的状态。

参数

struct irq_data *data

指向中断特定数据的指针

enum irqchip_irq_state which

要恢复的状态(IRQCHIP_STATE_* 之一)

bool val

which 对应的值

描述

如果底层 irqchip 未实现,则为有条件成功。

int irq_chip_get_parent_state(struct irq_data *data, enum irqchip_irq_state which, bool *state)

获取父中断的状态。

参数

struct irq_data *data

指向中断特定数据的指针

enum irqchip_irq_state which

调用者想要了解的 IRQCHIP_STATE_* 之一

bool *state

指向要存储状态的布尔值的指针

描述

如果底层 irqchip 未实现,则为有条件成功。

void irq_chip_enable_parent(struct irq_data *data)

启用父中断(如果为 NULL,则默认为取消屏蔽)

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_disable_parent(struct irq_data *data)

禁用父中断(如果为 NULL,则默认为屏蔽)

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_ack_parent(struct irq_data *data)

确认父中断

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_mask_parent(struct irq_data *data)

屏蔽父中断

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_mask_ack_parent(struct irq_data *data)

屏蔽并确认父中断

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_unmask_parent(struct irq_data *data)

取消屏蔽父中断

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_eoi_parent(struct irq_data *data)

在父中断上调用 EOI

参数

struct irq_data *data

指向中断特定数据的指针

int irq_chip_set_affinity_parent(struct irq_data *data, const struct cpumask *dest, bool force)

设置父中断的关联性

参数

struct irq_data *data

指向中断特定数据的指针

const struct cpumask *dest

要设置的关联性掩码

bool force

强制设置的标志(禁用在线检查)

描述

有条件的,因为底层父芯片可能未实现。

int irq_chip_set_type_parent(struct irq_data *data, unsigned int type)

在父中断上设置 IRQ 类型

参数

struct irq_data *data

指向中断特定数据的指针

unsigned int type

IRQ_TYPE_{LEVEL,EDGE}_* 值 - 请参阅 include/linux/irq.h

描述

有条件的,因为底层父芯片可能未实现。

int irq_chip_retrigger_hierarchy(struct irq_data *data)

在硬件中重新触发中断

参数

struct irq_data *data

指向中断特定数据的指针

描述

迭代遍历中断的域层次结构,并检查是否存在硬件重新触发函数。如果存在,则调用它。

int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info)

在父中断上设置 vcpu 亲和性

参数

struct irq_data *data

指向中断特定数据的指针

void *vcpu_info

vcpu 亲和性信息

int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on)

在父中断上设置/重置唤醒

参数

struct irq_data *data

指向中断特定数据的指针

unsigned int on

是否设置或重置此 irq 的唤醒功能

描述

有条件的,因为底层父芯片可能未实现。

int irq_chip_request_resources_parent(struct irq_data *data)

在父中断上请求资源

参数

struct irq_data *data

指向中断特定数据的指针

void irq_chip_release_resources_parent(struct irq_data *data)

在父中断上释放资源

参数

struct irq_data *data

指向中断特定数据的指针

提供的内部函数

本章包含内部函数的自动生成文档。

unsigned int irq_get_nr_irqs(void)

系统支持的中断数。

参数

void

无参数

unsigned int irq_set_nr_irqs(unsigned int nr)

设置系统支持的中断数。

参数

unsigned int nr

新的中断数。

返回

nr.

int generic_handle_irq(unsigned int irq)

调用特定 irq 的处理程序

参数

unsigned int irq

要处理的 irq 编号

返回

成功返回 0,如果转换失败返回 -EINVAL

此函数必须在 IRQ 上下文中调用,且 irq 寄存器已初始化。

int generic_handle_irq_safe(unsigned int irq)

从任何上下文中调用特定 irq 的处理程序。

参数

unsigned int irq

要处理的 irq 编号

返回

成功返回 0,错误返回负值。

描述

此函数可以从任何上下文(IRQ 或进程上下文)中调用。如果不是从 IRQ 上下文中调用,并且该 irq 已被标记为仅强制执行 IRQ 上下文,则会报告错误。

int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)

调用属于域的 HW irq 的处理程序。

参数

struct irq_domain *domain

执行查找的域

unsigned int hwirq

要转换为逻辑编号的 HW irq 编号

返回

成功返回 0,如果转换失败返回 -EINVAL

此函数必须在 IRQ 上下文中调用,且 irq 寄存器已初始化。

int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq)

调用属于域的 HW nmi 的处理程序。

参数

struct irq_domain *domain

执行查找的域

unsigned int hwirq

要转换为逻辑编号的 HW irq 编号

返回

成功返回 0,如果转换失败返回 -EINVAL

此函数必须在 NMI 上下文中调用,且 irq 寄存器已初始化。

void irq_free_descs(unsigned int from, unsigned int cnt)

释放 irq 描述符

参数

unsigned int from

描述符范围的起始位置

unsigned int cnt

要释放的连续 irq 数

int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, struct module *owner, const struct irq_affinity_desc *affinity)

分配并初始化一系列 irq 描述符

参数

int irq

如果 irq >= 0,则为特定的 irq 编号分配

unsigned int from

从该 irq 编号开始搜索

unsigned int cnt

要分配的连续 irq 数。

int node

应在其上分配 irq 描述符的首选节点

struct module *owner

所有者模块(可以为 NULL)

const struct irq_affinity_desc *affinity

指向大小为 cnt 的亲和性掩码数组的可选指针,该数组提示应在何处分配 irq 描述符以及使用哪些默认亲和性

描述

返回第一个 irq 编号或错误代码

unsigned int irq_get_next_irq(unsigned int offset)

获取下一个已分配的 irq 编号

参数

unsigned int offset

从哪里开始搜索

描述

返回 offset 之后的下一个 irq 编号,如果未找到,则返回 nr_irqs。

unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)

获取 cpu 上中断的统计信息

参数

unsigned int irq

中断号

int cpu

cpu 编号

描述

返回自启动以来 cpuirq 的中断计数总和。调用者必须确保中断不会同时被移除。

unsigned int kstat_irqs_usr(unsigned int irq)

从线程上下文中获取中断统计信息

参数

unsigned int irq

中断号

描述

返回自启动以来,所有 CPU 上 **irq** 的中断计数总和。

它使用 RCU 来保护访问,因为并发删除中断描述符会在 delayed_free_desc()/irq_kobj_release() 之前观察到 RCU 宽限期。

void handle_bad_irq(struct irq_desc *desc)

处理伪中断和未处理的中断

参数

struct irq_desc *desc

中断的描述

描述

处理伪中断和未处理的 IRQ。 它还会打印调试消息。

void noinstr generic_handle_arch_irq(struct pt_regs *regs)

对于不自行进行入口计数的架构的根中断处理程序

参数

struct pt_regs *regs

来自底层处理代码的寄存器文件

int irq_set_msi_desc_off(unsigned int irq_base, unsigned int irq_offset, struct msi_desc *entry)

在偏移量处为中断设置 MSI 描述符数据

参数

unsigned int irq_base

中断号基数

unsigned int irq_offset

中断号偏移量

struct msi_desc *entry

指向 MSI 描述符数据的指针

在偏移量处为中断设置 MSI 描述符条目

int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)

为中断设置 MSI 描述符数据

参数

unsigned int irq

中断号

struct msi_desc *entry

指向 MSI 描述符数据的指针

为中断设置 MSI 描述符条目

void irq_disable(struct irq_desc *desc)

标记中断已禁用

参数

struct irq_desc *desc

应禁用的中断描述符

描述

如果芯片未实现 irq_disable 回调,我们将使用惰性禁用方法。 这意味着我们将中断标记为已禁用,但保持硬件未屏蔽。 这是一种优化,因为我们可以避免在将中断标记为禁用后没有中断发生的常见情况下的硬件访问。 如果发生中断,则中断流处理程序会在硬件级别屏蔽该线路并将其标记为挂起。

如果中断芯片未实现 irq_disable 回调,则驱动程序可以通过调用 ‘irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY)’ 来禁用特定中断线的惰性方法。 这可以用于在某些情况下无法在设备级别禁用中断并且必须使用 disable_irq[_nosync] 的设备。

void handle_edge_eoi_irq(struct irq_desc *desc)

边沿 EOI 类型 IRQ 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

描述

与上面的 handle_edge_irq 类似,但使用 eoi 且没有屏蔽/取消屏蔽逻辑。

void handle_percpu_irq(struct irq_desc *desc)

每个 CPU 的本地中断处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

在没有锁定要求的 SMP 机器上的每个 CPU 中断

void handle_percpu_devid_irq(struct irq_desc *desc)

带有每个 CPU 设备 ID 的每个 CPU 本地中断处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

描述

在没有锁定要求的 SMP 机器上的每个 CPU 中断。 与上面的 handle_percpu_irq() 相同,但具有以下附加功能

action->percpu_dev_id 是指向每个 CPU 变量的指针,这些变量包含此处理程序被调用的 CPU 的实际设备 ID

void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc)

带有每个 CPU 设备 ID 的每个 CPU 本地 NMI 处理程序

参数

struct irq_desc *desc

此 irq 的中断描述结构

描述

类似于 handle_fasteoi_nmi,但将 dev_id cookie 处理为每个 CPU 指针。

void irq_cpu_online(void)

调用所有 irq_cpu_online 函数。

参数

void

无参数

描述

迭代所有中断并为每个中断调用 chip.irq_cpu_online()

void irq_cpu_offline(void)

调用所有 irq_cpu_offline 函数。

参数

void

无参数

描述

迭代所有中断并为每个中断调用 chip.irq_cpu_offline()

int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)

为中断芯片组合 msi 消息

参数

struct irq_data *data

指向中断特定数据的指针

struct msi_msg *msg

指向 MSI 消息的指针

描述

对于分层域,我们找到层次结构中第一个实现 irq_compose_msi_msg 回调的芯片。 对于非分层域,我们使用顶层芯片。

int irq_chip_pm_get(struct irq_data *data)

为 IRQ 芯片启用电源

参数

struct irq_data *data

指向中断特定数据的指针

描述

为中断数据结构引用的 IRQ 芯片启用电源。

int irq_chip_pm_put(struct irq_data *data)

禁用 IRQ 芯片的电源

参数

struct irq_data *data

指向中断特定数据的指针

描述

禁用由中断数据结构 `belongs` 引用的 IRQ 芯片的电源。请注意,只有在所有调用过 irq_chip_pm_get() 的 IRQ 都调用过此函数后,才会禁用电源。

致谢

以下人员为本文档做出了贡献

  1. Thomas Gleixner tglx@linutronix.de

  2. Ingo Molnar mingo@elte.hu