深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现
往期内容
pinctrl和gpio子系统专栏:
专栏地址:pinctrl和gpio子系统
编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用
– 末片,有专栏内容观看顺序
input子系统专栏:
- 专栏地址:input子系统
- input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
– 末片,有专栏内容观看顺序I2C子系统专栏:
- 专栏地址:IIC子系统
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
– 末篇,有专栏内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
– 末篇,有专栏内容观看顺序
目录
- 往期内容
- 一. 概念
- 1. IRQ描述符和中断处理机制
- 2. 中断的开关
- 3. 中断描述符的存储和管理
- 4. irq domain与irq number的解耦
- 二. irq_desc相关数据结构体
- 1.总图
- 2.irq_data
- 3.irq_chip
一. 概念
1、IRQ number。CPU需要为每一个外设中断编号,我们称之IRQ Number。这个IRQ number是一个虚拟的interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断。
2、HW interrupt ID。对于interrupt controller而言,它收集了多个外设的interrupt request line并向上传递,因此,interrupt controller需要对外设中断进行编码。Interrupt controller用HW interrupt ID来标识外设的中断。在interrupt controller级联的情况下,仅仅用HW interrupt ID已经不能唯一标识一个外设中断,还需要知道该HW interrupt ID所属的interrupt controller(HW interrupt ID在不同的Interrupt controller上是会重复编码的)。
1. IRQ描述符和中断处理机制
每一个外设的中断都由一个struct irq_desc
(即中断描述符)来描述。中断描述符DB(Descriptor Base)存储所有中断描述符的信息。在发生中断时,系统会通过HW interrupt ID找到对应的中断线,并通过irq domain将其翻译为IRQ number。这个IRQ number用来索引到对应的中断描述符,然后调用其high-level IRQ-events handler进行处理。
- High-level IRQ-events handler主要进行两项工作:
- 调用中断描述符的底层irq chip driver,执行中断流控制(如mask、ack等回调函数)。
- 在合适的情况下,调用中断描述符上action list中的specific handler。这个处理器不总是执行,取决于中断描述符的当前状态。中断流控制结合软件和硬件来管理,比如软件设置标志位,而硬件通过mask/unmask来控制中断控制器。
2. 中断的开关
中断的开关包括两种:
- Local CPU的中断开关:控制当前CPU的中断。对于单处理器系统(UP),关闭CPU中断会完全关闭中断。对于多处理器系统(SMP),只能关闭当前CPU的中断,而不能关闭全局中断。
- Interrupt controller的中断控制:使用mask或unmask来控制某个IRQ number对应的中断。
在进入high-level handler时,CPU的中断已经关闭,具体是由硬件在进入中断模式时设定的。
旧内核(2.6.35之前)的slow handler与fast handler:
- 在旧的内核版本中,存在两种handler:slow handler和fast handler。对于fast handler,需要传递
IRQF_DISABLED
参数,确保中断处理过程中CPU的中断是关闭的(因为是fast handler,执行很快,即便是关闭CPU中断不会影响系统的性能)。而对于slow handler,并不是每一种外设中断的handler都是那么快(例如磁盘),如果整个处理过程中关闭CPU中断,会影响系统性能,说明其在中断处理过程中会耗时比较长。因此,内核根据IRQF_DISABLED
标志来决定是否打开CPU中断。如果未设置该标志,则会打开CPU的中断。
新内核(3.14及以后的内核):
- 随着硬件速度的提升和软件中更好的bottom half机制,
IRQF_DISABLED
已经被废弃。现在所有的specific handler在执行过程中都会关闭CPU中断,以避免中断嵌套和影响系统性能。
3. 中断描述符的存储和管理
无论外部的interrupt controller结构如何复杂,CPU只关心外设中断的handler。在系统中,irq number用于管理外部中断,使用一个线性的表格保存irq_desc
中断描述符,每条中断线对应一个IRQ number。表中的每个元素都是一个irq_desc
,这个表格在内核中静态定义:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
然而,静态表格并不总是合适的,因为在某些系统中,可能定义了很大的NR_IRQS
,但只用到少数几个中断源,导致内存浪费。为了解决这个问题,Linux内核引入了radix tree(基数树),用于动态分配和管理中断描述符。如果启用了CONFIG_SPARSE_IRQ
选项,系统将使用radix tree保存中断描述符,这样可以更加灵活地处理中断。
内核通过radix tree(基数树)来管理irq_desc。当硬件中断ID需要映射时,内核根据需要动态分配和插入相应的irq_desc到radix tree中,而不再是通过一个静态的线性数组。
\Linux-4.9.88\Linux-4.9.88\kernel\irq\irqdesc.c
struct irq_desc *irq_to_desc(unsigned int irq) //int irq就是irq number
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}
该代码展示了在启用SPARSE_IRQ
后,如何通过IRQ number找到对应的irq_desc
。
4. irq domain与irq number的解耦
在旧版本的内核中,IRQ number
通常直接映射到硬件的中断ID(Interrupt ID)。这意味着在不同平台上,IRQ number
和硬件的中断标识紧密关联。比如,外部中断控制器的硬件中断ID与内核中的IRQ number
是一一对应的关系,系统中有多少个硬件中断源,就有相应数量的IRQ number和对应的描述符。
然而,随着内核的发展,尤其在支持多种平台和复杂硬件拓扑的系统中,简单的线性映射已不能适应需求。为了解决跨平台的中断管理问题,Linux引入了IRQ Domain的概念。IRQ Domain
是用来抽象不同硬件平台的中断控制器和中断ID映射关系的,它允许将硬件中断ID与IRQ number
分离,使得IRQ number成为纯粹的逻辑概念,不再直接与硬件关联。
中断发生后,处理流程大致如下:
- 获取硬件中断ID: 系统从外部中断控制器获取触发的硬件中断ID。
- 通过IRQ Domain映射: 使用IRQ Domain将硬件ID映射为逻辑的IRQ number。
- 找到中断描述符: 通过IRQ number,在内核中找到对应的
irq_desc
。 - 调用高层中断处理程序(Highlevel IRQ Handler):
irq_desc
中的handle_irq
负责执行底层的中断控制逻辑,如mask
、ack
等,并决定是否调用挂载在该IRQ上的具体中断处理程序。 - 执行具体中断处理程序(Specific Handler): 最后,调用该IRQ上挂载的具体设备的中断处理函数,处理外设的中断请求。
二. irq_desc相关数据结构体
/**
* struct irq_desc - interrupt descriptor
* @irq_common_data: per irq and chip data passed down to chip functions
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
* @action: the irq action chain
* @status: status information
* @core_internal_state__do_not_mess_with_it: core internal status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @threads_handled: stats field for deferred spurious detection of threaded handlers
* @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @nr_actions: number of installed actions on this descriptor
* @no_suspend_depth: number of irqactions on a irq descriptor with
* IRQF_NO_SUSPEND set
* @force_resume_depth: number of irqactions on a irq descriptor with
* IRQF_FORCE_RESUME set
* @rcu: rcu head for delayed free
* @kobj: kobject used to represent this struct in sysfs
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
struct irq_common_data irq_common_data; // ---(1)
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq; // ---(3)
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */ // ---(5)
unsigned int status_use_accessors; // ---(6)
unsigned int core_internal_state__do_not_mess_with_it; // ---(6)
unsigned int depth; /* nested irq disables */ // ---(7)
unsigned int wake_depth; /* nested wake enables */ // ---(7)
unsigned int irq_count; /* For detecting broken IRQs */ // ---(8)
unsigned long last_unhandled; /* Aging timer for unhandled count */ // ---(8)
unsigned int irqs_unhandled; // ---(8)
atomic_t threads_handled;
int threads_handled_last;
raw_spinlock_t lock; // ---(9)
struct cpumask *percpu_enabled; // ---(10)
const struct cpumask *percpu_affinity; // ---(10)
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint; // ---(11)
struct irq_affinity_notify *affinity_notify;// ---(11)
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot; // ---(13)
atomic_t threads_active; // ---(13)
wait_queue_head_t wait_for_threads; // ---(13)
#ifdef CONFIG_PM_SLEEP
unsigned int nr_actions;
unsigned int no_suspend_depth;
unsigned int cond_suspend_depth;
unsigned int force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
#ifdef CONFIG_SPARSE_IRQ1.
struct rcu_head rcu;
struct kobject kobj;
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
重点看下标黑成员部分就行了
1. irq_common_data 和 irq_data
irq_common_data
和irq_data
:这两个字段包含每个中断相关的芯片数据和传递给底层中断控制器的特定信息。这些数据与中断控制器的操作(如屏蔽中断、应答中断等)紧密关联,封装了与硬件的交互。irq_data会单独具体讲解
\2. kstat_irqs
kstat_irqs
:该字段是一个per-cpu
的统计数据,记录了每个CPU处理的中断次数。这有助于分析不同CPU在系统中如何处理中断,并评估系统负载。
3. handle_irq
-
handle_irq
:这是一个高层中断事件处理函数(Highlevel IRQ-events Handler)。它是抽象化的,用于处理不同类型的中断源,而不是处理特定的设备中断。它根据中断类型(电平触发、边缘触发等)决定具体的中断处理流程,调用适当的底层处理器,如:handle_level_irq
:处理电平触发型中断。handle_edge_irq
:处理边缘触发型中断。handle_simple_irq
:处理简单中断。handle_fasteoi_irq
:处理快速结束(EOI)类型的中断。
-
high level是和specific相对,specific handler处理具体的事务,例如处理一个按键中断、处理一个磁盘中断。而high level则是对处理各种中断交互过程的一个抽象,根据中断控制器和IRQ trigger type硬件的不同
\4. preflow_handler
preflow_handler
:该字段用于指定在中断流处理器(Flow Handler)之前调用的处理程序。它主要用于特定架构(如SPARC),并非所有平台都使用。
5. action
action
:指向一个irqaction
结构体链表,代表一个IRQ上挂载的处理程序链。如果某个中断线(IRQ request line)允许共享,则链表中可以有多个处理程序;否则,仅有一个处理程序。irqaction
是具体设备的中断处理程序,每个链表节点对应一个设备驱动的中断处理函数。
6. status 和 istate
status_use_accessors
:表示中断的当前状态信息,例如是否被屏蔽、是否为共享中断等。core_internal_state__do_not_mess_with_it
(简称istate
):表示内部状态字段,主要用于内核核心管理,在具体使用的时候被被简化成istate,表示internal state。就像这个名字定义的那样,我们最好不要直接修改它。
7. depth 和 wake_depth
depth
:该字段表示中断的关闭深度。如果多次调用irq_disable()
,则depth
会递增。当depth
回到0时,中断才会重新启用。这个机制允许中断嵌套禁用,以保护临界区数据。我们可以通过enable和disable一个指定的IRQ来控制内核的并发,从而保护临界区的数据。对一个IRQ进行enable和disable的操作可以嵌套(当然一定要成对使用),depth是描述嵌套深度的信息。wake_depth
:与电源管理相关,表示该中断是否能唤醒系统。通过irq_set_irq_wake()
接口可以启用或禁用某个IRQ是否可以将系统从挂起(suspend)状态中唤醒。嵌套的enable
和disable
操作会改变wake_depth
的值。
8. irq_count, last_unhandled 和 irqs_unhandled
irq_count
:用于检测某个中断的触发次数。last_unhandled
:如果某个中断没有被处理,此字段会记录上次未处理的时间。irqs_unhandled
:统计未处理的中断次数。- 这些字段用于诊断和处理那些可能无法处理的中断(Broken IRQs)。所谓broken IRQ就是由于种种原因(例如错误firmware),IRQ handler没有定向到指定的IRQ上,当一个IRQ没有被处理的时候,kernel可以为这个没有被处理的handler启动scan过程,让系统中所有的handler来认领该IRQ。
9. lock
lock
:这是一个自旋锁,用于在多处理器(SMP)系统中保护对中断描述符的并发访问。它确保在处理同一个IRQ时,不同CPU不会同时访问中断描述符,避免数据竞争。
10. percpu_enabled 和 percpu_affinity
percpu_enabled
:这是一个cpumask
结构,表示某个中断是否在每个CPU上启用。这在多核系统中很有用,因为某些中断可能只对某些CPU启用。一个中断描述符可能会有两种情况,一种是该IRQ是global,一旦disable了该irq,那么对于所有的CPU而言都是disable的。还有一种情况,就是该IRQ是per CPU的,也就是说,在某个CPU上disable了该irq只是disable了本CPU的IRQ而已,其他的CPU仍然是enable的。percpu_enabled是一个描述该IRQ在各个CPU上是否enable成员。percpu_affinity
:指定了该中断的CPU affinity
,即该中断应该在某个CPU上处理。在SMP系统中,中断的绑定可以提高缓存命中率,降低延迟。
11. affinity_hint 和 affinity_notify
affinity_hint
:这是一个指向cpumask
的指针,向用户空间提供一个建议,说明该中断的最佳处理器关联。用户空间工具可以读取该信息,以优化中断的分布。affinity_notify
:用于通知系统或用户空间,当中断的CPU关联发生变化时执行特定的操作。
\12. pending_mask
pending_mask
:这是一个用于管理挂起的重新平衡中断的位图。它主要用于在CPU之间重新分配中断。
13. threads_oneshot, threads_active 和 wait_for_threads
threads_oneshot
:位字段,用于管理共享的单次触发中断线程(oneshot thread)。threads_active
:记录当前正在运行的irqaction
线程数量。wait_for_threads
:这是一个等待队列,用于在同步IRQ时等待正在运行的线程完成。某些中断处理程序被线程化,以减少长时间运行的中断处理对系统的影响。
\14. nr_actions, no_suspend_depth 和 force_resume_depth
nr_actions
:表示当前IRQ描述符上挂载的处理程序数量。no_suspend_depth
:表示有多少irqaction
设置了IRQF_NO_SUSPEND
标志,意味着这些处理程序不允许在系统进入挂起模式时暂停。force_resume_depth
:表示有多少irqaction
设置了IRQF_FORCE_RESUME
标志,意味着这些处理程序强制要求在恢复系统时重新启用中断。
\15. procfs 和 sysfs 相关字段
dir
:如果启用了PROC_FS
,该字段表示/proc/irq/
下的proc文件系统入口,可以用于查看系统中的中断信息。kobj
:这是kobject
,用于表示该结构在sysfs
中的表示,允许用户通过sysfs
查看和操作该中断。
\16. rcu 和 owner
rcu
:用于延迟释放的rcu
头,在某些情况下,内存的释放可能会延迟到安全的时机。owner
:指向拥有该中断处理程序的内核模块。这确保在卸载模块时可以正确释放中断。
\17. parent_irq 和 name
parent_irq
:如果该中断是某个父中断的子中断,则记录父中断的IRQ number
。name
:这是中断处理器的名字,通常用于/proc/interrupts
输出,帮助识别中断来源。
1.总图
2.irq_data
\Linux-4.9.88\Linux-4.9.88\include\linux\irq.h
/**
* struct irq_data - per irq chip data passed down to chip functions
* @mask: precomputed bitmask for accessing the chip registers
* 预计算的位掩码,用于访问中断控制器的寄存器。通常根据中断线生成,帮助高效操作。
* @irq: interrupt number
* 中断号,Linux 系统中为每个中断分配的唯一标识。
* @hwirq: hardware interrupt number, local to the interrupt domain
* 硬件中断号,本地于中断域的硬件中断号,在不同的中断域中不一定与 Linux 中的 irq 号相同。
* @common: point to data shared by all irqchips
* 指向所有中断芯片共用的 `irq_common_data` 数据结构。
* @chip: low level interrupt hardware access
* 指向 `irq_chip`,它定义了与硬件中断控制器的底层交互方法,例如启用、禁用、应答中断等。
* @domain: Interrupt translation domain; responsible for mapping
* between hwirq number and linux irq number.
* 中断转换域,负责硬件中断号(hwirq)和 Linux 系统中断号(irq)之间的映射。
* @parent_data: pointer to parent struct irq_data to support hierarchy
* irq_domain
* 支持中断域层次结构时,指向父级 `irq_data` 结构体。在多级中断域的情况下用于管理父子关系。
* @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations
* 针对每个中断控制器的特定平台私有数据。允许芯片实现共享并提供与硬件控制器特定的交互数据。
*/
struct irq_data {
u32 mask; // 位掩码,用于操作中断控制器寄存器
unsigned int irq; // Linux 系统分配的中断号
unsigned long hwirq; // 硬件中断号,本地于中断域
struct irq_common_data *common; // 指向所有中断芯片共用的 irq_common_data
struct irq_chip *chip; // 指向 irq_chip,定义与硬件中断控制器的交互
struct irq_domain *domain; // 中断域,负责硬件中断号和 Linux 中断号之间的映射
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_data *parent_data; // 指向父级 irq_data,支持层次化中断域
#endif
void *chip_data; // 平台私有数据,与中断控制器相关
};
其中:struct irq_common_data *common 具体如下:
/**
* struct irq_common_data - per irq data shared by all irqchips
* @state_use_accessors: status information for irq chip functions.
* Use accessor functions to deal with it
* 中断芯片函数的状态信息,建议通过访问函数进行操作,而不是直接访问此字段。
* @node: node index useful for balancing
* 用于 NUMA 系统中的节点索引,有助于在 NUMA 体系结构中进行负载平衡。
* @handler_data: per-IRQ data for the irq_chip methods
* 中断芯片方法特定于每个 IRQ 的数据。可以存储与中断处理相关的私有信息。
* @affinity: IRQ affinity on SMP. If this is an IPI
* related irq, then this is the mask of the
* CPUs to which an IPI can be sent.
* 在对称多处理(SMP)系统中表示中断的亲和性。对于与 IPI(中断处理器间中断)相关的中断,这是可以接收 IPI 的 CPU 的掩码。
* @msi_desc: MSI descriptor
* 用于存储与 MSI(消息信号中断)相关的信息。
* @ipi_offset: Offset of first IPI target cpu in @affinity. Optional.
* IPI 目标 CPU 在 `affinity` 掩码中的偏移量,可选字段。
*/
struct irq_common_data {
unsigned int __private state_use_accessors; // 中断芯片的状态信息,需通过访问函数操作
#ifdef CONFIG_NUMA
unsigned int node; // 在 NUMA 系统中用于节点索引的字段
#endif
void *handler_data; // 每个 IRQ 特定的中断处理数据
struct msi_desc *msi_desc; // MSI 描述符
cpumask_var_t affinity; // IRQ 亲和性掩码,表示中断能被处理的 CPU
#ifdef CONFIG_GENERIC_IRQ_IPI
unsigned int ipi_offset; // IPI 目标 CPU 在 affinity 中的偏移量
#endif
};
中断有两种形态,一种就是直接通过signal相连,用电平或者边缘触发。另外一种是基于消息的,被称为MSI (Message Signaled Interrupts)。msi_desc是MSI类型的中断相关(这个在讲PCI/PCIe子系统的时候会详细讲解)。
node成员用来保存中断描述符的内存位于哪一个memory node上。 对于支持NUMA(Non Uniform Memory Access Architecture)的系统,其内存空间并不是均一的,而是被划分成不同的node,对于不同的memory node,CPU其访问速度是不一样的。如果一个IRQ大部分(或者固定)由某一个CPU处理,那么在动态分配中断描述符的时候,应该考虑将内存分配在该CPU访问速度比较快的memory node上。
3.irq_chip
irq_data中的成员,包括了若干和具体Interrupt controller相关的callback函数
struct irq_chip {
struct device *parent_device;
const char *name;
//该中断控制器的名字,用于/proc/interrupts中的显示
unsigned int (*irq_startup)(struct irq_data *data); // 启动中断源
//start up 指定的irq domain上的HW interrupt ID。如果不设定的话,default会被设定为enable函数
void (*irq_shutdown)(struct irq_data *data); // 关闭中断源
//shutdown 指定的irq domain上的HW interrupt ID。如果不设定的话,default会被设定为disable函数
void (*irq_enable)(struct irq_data *data); // 启用中断源
//enable指定的irq domain上的HW interrupt ID。如果不设定的话,default会被设定为unmask函数
void (*irq_disable)(struct irq_data *data);// 禁用中断源
//disable指定的irq domain上的HW interrupt ID。
void (*irq_ack)(struct irq_data *data);// 确认中断
//和具体的硬件相关,有些中断控制器必须在Ack之后(清除pending的状态)才能接受到新的中断。
void (*irq_mask)(struct irq_data *data);// 屏蔽中断源
//mask指定的irq domain上的HW interrupt ID
void (*irq_mask_ack)(struct irq_data *data);// 确认并屏蔽中断源
// mask并ack指定的irq domain上的HW interrupt ID。
void (*irq_unmask)(struct irq_data *data);// 取消屏蔽中断源
//mask指定的irq domain上的HW interrupt ID
void (*irq_eoi)(struct irq_data *data); // 中断处理结束通知
//有些interrupt controler(例如GIC)提供了这样的寄存器接口,让CPU可以通知interrupt controller,它已经处理完一个中断
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);// 设置中断亲和性
//在SMP的情况下,可以通过该callback函数设定CPU affinity
int (*irq_retrigger)(struct irq_data *data);// 重新触发中断
//重新触发一次中断,一般用在中断丢失的场景下。如果硬件不支持retrigger,可以使用软件的方法。
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);// 设置中断触发类型
//设定指定的irq domain上的HW interrupt ID的触发方式,电平触发还是边缘触发
int (*irq_set_wake)(struct irq_data *data, unsigned int on);// 启用/禁用中断唤醒功能
//和电源管理相关,用来enable/disable指定的interrupt source作为唤醒的条件。
void (*irq_bus_lock)(struct irq_data *data); // 锁定慢速总线
//有些interrupt controller是连接到慢速总线上(例如一个i2c接口的IO expander芯片),在访问这些芯片的时候需要lock住那个慢速bus(只能有一个client在使用I2C bus)
void (*irq_bus_sync_unlock)(struct irq_data *data); // 解锁慢速总线并同步
//unlock慢速总线
void (*irq_cpu_online)(struct irq_data *data);// 配置中断源以支持新上线的 CPU
void (*irq_cpu_offline)(struct irq_data *data);// 配置中断源以支持新下线的 CPU
void (*irq_suspend)(struct irq_data *data);// 系统挂起时调用,用于保存中断芯片的状态。
//电源管理相关的callback函数
void (*irq_resume)(struct irq_data *data);// 系统恢复时调用,用于恢复中断芯片的状态。
//电源管理相关的callback函数
void (*irq_pm_shutdown)(struct irq_data *data); // 系统关机时调用,用于关闭中断芯片
//电源管理相关的callback函数
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);// 可选,用于生成 MSI 消息内容
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);// 可选,用于将 MSI 消息写入硬件
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);// 可选,用于在虚拟化环境中设置 vCPU 的中断亲和性。
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);// 发送单个 IPI 到目标 CPU。
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest); // 根据 CPU 掩码发送 IPI 到目标 CPU。
unsigned long flags;// 用于标识中断芯片特定行为的标志位。
};
接下来也可以看一下内核给的注释:
/**
* struct irq_chip - hardware interrupt chip descriptor
*
* @parent_device: pointer to parent device for irqchip
* 指向中断芯片的父设备指针,通常用于嵌入式系统中,表示中断控制器所属的设备。
* @name: name for /proc/interrupts
* 中断控制器的名字,用于在 /proc/interrupts 文件中显示。
* @irq_startup: start up the interrupt (defaults to ->enable if NULL)
* 启动中断源,如果该字段为 NULL,默认为 enable 操作。
* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
* 关闭中断源,如果该字段为 NULL,默认为 disable 操作。
* @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
* 启用中断源,如果该字段为 NULL,默认为 unmask 操作。
* @irq_disable: disable the interrupt
* 禁用中断源。
* @irq_ack: start of a new interrupt
* 确认中断并允许新的中断发生,通常用于需要在中断控制器中清除 pending 状态。
* @irq_mask: mask an interrupt source
* 屏蔽中断源,使其不再触发中断。
* @irq_mask_ack: ack and mask an interrupt source
* 屏蔽并确认中断源,常用于同时进行中断屏蔽和确认。
* @irq_unmask: unmask an interrupt source
* 取消对中断源的屏蔽,使其可以触发中断。
* @irq_eoi: end of interrupt
* 通知中断控制器中断处理完成,适用于需要通知硬件的中断控制器。
* @irq_set_affinity: set the CPU affinity on SMP machines
* 设置中断在多处理器系统中的亲和性,指定中断应由哪个 CPU 处理。
* @irq_retrigger: resend an IRQ to the CPU
* 重新触发中断,常用于丢失中断的场景。
* @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* 设置中断的触发类型(电平触发或边缘触发)。
* @irq_set_wake: enable/disable power-management wake-on of an IRQ
* 启用或禁用中断源作为系统唤醒事件。
* @irq_bus_lock: function to lock access to slow bus (i2c) chips
* 锁定慢速总线的访问,例如 I2C 接口上的中断控制器。
* @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
* 解锁慢速总线,并同步完成任何未完成的操作。
* @irq_cpu_online: configure an interrupt source for a secondary CPU
* 配置中断源以支持新上线的 CPU。
* @irq_cpu_offline: un-configure an interrupt source for a secondary CPU
* 配置中断源以支持新下线的 CPU。
* @irq_suspend: function called from core code on suspend once per
* chip, when one or more interrupts are installed
* 当系统挂起时调用,用于保存中断芯片状态。
* @irq_resume: function called from core code on resume once per chip,
* when one ore more interrupts are installed
* 当系统恢复时调用,用于恢复中断芯片状态。
* @irq_pm_shutdown: function called from core code on shutdown once per chip
* 在系统关机时调用,用于关闭中断芯片。
* @irq_calc_mask: Optional function to set irq_data.mask for special cases
* 可选函数,用于在特殊情况下计算中断掩码。
* @irq_print_chip: optional to print special chip info in show_interrupts
* 可选函数,用于在显示中断信息时打印中断芯片的额外信息。
* @irq_request_resources: optional to request resources before calling
* any other callback related to this irq
* 可选函数,在处理中断前分配所需的资源。
* @irq_release_resources: optional to release resources acquired with
* irq_request_resources
* 可选函数,用于释放中断相关的资源。
* @irq_compose_msi_msg: optional to compose message content for MSI
* 可选函数,用于生成 MSI 消息的内容。
* @irq_write_msi_msg: optional to write message content for MSI
* 可选函数,用于将 MSI 消息内容写入硬件。
* @irq_get_irqchip_state: return the internal state of an interrupt
* 返回中断的内部状态。
* @irq_set_irqchip_state: set the internal state of a interrupt
* 设置中断的内部状态。
* @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
* 可选函数,用于在虚拟机中设置 vCPU 的中断亲和性。
* @ipi_send_single: send a single IPI to destination cpus
* 发送单个处理器间中断 (IPI) 到目标 CPU。
* @ipi_send_mask: send an IPI to destination cpus in cpumask
* 根据 CPU 掩码发送 IPI 到目标 CPU。
* @flags: chip specific flags
* 中断芯片特定的标志位,用于标识中断控制器的行为特性。
*/