Linux中断情景分析

嵌入式技术

1368人已加入

描述

在一个系统中,中断时常发生,而且线程调度也是由一个硬件定时器时时刻刻发出中断来支撑的。可以说中断就是linux系统的灵魂。

由于中断来源很多,加上一个硬件系统中会存在许多中断控制器级联,因此会产生中断管理上的麻烦,Linux管理中断也是比较复杂,用到了很多数据结构,如果一开始就深究这些数据结构实例如何组织,只能是事倍功半,因此先进行情景分析,看看当一个中断在设备产生后,会发生什么事情。

一、中断控制器的级联情况

下面一张图可以涵盖两种级联情况

定时器

图中的GIC、GPC、GPIO控制器都能作为中断控制器,其中我认为GIC才是硬件上真正意义的中断控制器,而其他两个之所以能被称为中断控制器,是因为他们也需要分辨下一级的中断源。

还可以看到这里基本忽略了SGI和PPI中断,因为这些中断很可能不来自于设备。

一对一的级联

从GPC到GIC的中断,是一对一的。这种有个特点就是GIC发生了中断后,无需判断是下一级哪个GPC的哪个中断发生了。举个例子,如果是GIC 的128号中断发生了,无需再判断是GPC哪个中断传递给了GIC的128号中断,直接就能断定是GPC对应的128号中断产生了。

多对一的级联

从GPIO到GPC的5号中断,是多对一的关系。这个源自于GPIO的一个特点,就是有可能多个GPIO信号只能联合产生一个中断给到上一级中断控制器,这里上一级的中断控制器就是GPC。举个例子,假如GIC发生了中断,读取GIC寄存器知道发生了GIC的122号中断,那么也就知道了是GPC的5号中断产生了,已知GPIO会导致GPC5号中断产生,但是是哪个GPIO的产生呢?那么就需要在GPIO控制器的驱动程序中分辨一下。

以上两种级联的情况,决定了当一个中断产生后,途径的中断控制器的中断执行过程。

二、linux对中断号的管理

通常如果一个设备要作为一个中断源,会在设备树指定使用到哪个中断控制器,使用到这个中断控制器里面的第x个中断。但是这个数字并不能作为唯一标识这个中断源的号码,原因在于同一个中断源,在gpio上会表示x号中断,而在gpc上面,则可能是y号中断。

举个例子,比如有个按键key使用到了gpio中断号为4的,gpio控制器会向上gpc产生一个的中断号为5,同一个中断,一下子从4变成5,那么我们指定的4 和5都具有很强的硬件关系,并不能给到linux去标识唯一的中断。

因此,linux中的中断号称为linux irq,是一个系统软件上的索引。在编写设备驱动的时候,都会根据这个irq去申请中断并且注册中断服务函数。

linux irq如何转换?

linux irq实际上是在系统初始化,在解析设备树的时候根据在设备树定义的中断信息将和硬件相关的中断号转化成linux irq。

定时器

以上是对于按键编写的设备树节点,使用到GPIO1中断控制器,并且用到了GPIO的18号中断,触发中断类型是双边沿。那么在解析设备树的时候,18的硬件中断号就会被转换成唯一标识这个中断的linux irq。至于解析过程涉及代码比较多,这里只是情景分析,就不详细追代码了。

三、中断描述符

这里还要介绍一下中断描述符这个数据结构。一个linux irq对应一个中断描述符irq_desc,通过linux irq就能找到对应的中断描述符。看一下这个数据结构可以大概知道中断描述符有什么用

irq_desc

struct irq_desc {
 struct irq_data  irq_data;
 unsigned int __percpu *kstat_irqs;
 irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
 irq_preflow_handler_t preflow_handler;
#endif
 struct irqaction *action; /* IRQ action list */
 unsigned int  status_use_accessors;
 unsigned int  core_internal_state__do_not_mess_with_it;
 unsigned int  depth;  /* nested irq disables */
 unsigned int  wake_depth; /* nested wake enables */
 unsigned int  irq_count; /* For detecting broken IRQs */
 unsigned long  last_unhandled; /* Aging timer for unhandled count */
 unsigned int  irqs_unhandled;
 atomic_t  threads_handled;
 int   threads_handled_last;
 raw_spinlock_t  lock;
 struct cpumask  *percpu_enabled;
#ifdef CONFIG_SMP
 const struct cpumask *affinity_hint;
 struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
 cpumask_var_t  pending_mask;
#endif
#endif
 unsigned long  threads_oneshot;
 atomic_t  threads_active;
 wait_queue_head_t       wait_for_threads;
#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
 int   parent_irq;
 struct module  *owner;
 const char  *name;
} ____cacheline_internodealigned_in_smp;
  • irq_data : 每一个中断能够给irq_chip使用的数据
  • handle_irq :linux irq 对应的高层次中断处理函数,中断是根据linux irq执行的,高层次的意思就是第一时间执行的中断处理函数,里面还会继续调用深层次的中断处理函数
  • action :action链表,就是用户注册的中断处理函数,也就是深层次的中断处理函数最后会被调用

irq_data

struct irq_data {
 u32   mask;
 unsigned int  irq;
 unsigned long  hwirq;
 unsigned int  node;
 unsigned int  state_use_accessors;
 struct irq_chip  *chip;
 struct irq_domain *domain;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
 struct irq_data  *parent_data;
#endif
 void   *handler_data;
 void   *chip_data;
 struct msi_desc  *msi_desc;
 cpumask_var_t  affinity;
};
  • irq :linux irq
  • hwirq :设备树指定的在此中断控制器的硬件中断号
  • irq_chip :代表一个中断控制器,包含了一个中断控制器的方法,比如在自己这个层次屏蔽的中断方法,打开中断方法,要注意,是在自己的层次。
  • irq_domain : 每一个中断控制器都有的一个数据结构,用来保存linux irq 和硬件irq号的对应关系,可以想象到中断控制器就是通过一个硬件irq和一个irq_domian找到linux irq进而找到irq_desc

四、中断情景分析

有了上面的铺垫勉强可以进行中断的情景分析

按键中断:

定时器

当按键产生一个中断,中断源是按键,中断会途径GPIO 1号中断、GIC的5号中断,最后到GIC 122号中断CPU。

但是,软件处理是返回来的:

  1. CPU被中断后,跳到中断向量表中执行GIC驱动定义的中断处理函数,发现是122号中断产生
  2. 因为GPC和GIC的级联是一对一的关系,因为也就直接知道了是GIC的5号中断产生,因此很重要的一点是,GPC的驱动程序无需分辨是哪个中断产生。
  3. 那么现在转到了GPIO的驱动程序,已知是GPIO控制器导致了GPC的5号中断产生,那么具体是哪个GPIO产生了中断呢?根据GPIO控制器的linux irq找到irq_desc,调用其中断处理函数,需要在GPIO的驱动程序提供的中断处理函数作分辨,这里称为handler A,读出寄存器是GPIO控制器的1信号发生了中断,那么在handler A中继续调用按键驱动程序的中断处理函数。
  4. 最后到了按键的中断处理函数,这里就是用户为设备注册的最后的中断处理函数,比如按键按下了点灯
  5. 整个过程就是中断上下文

对于定时器中断:

定时器

当定时器产生一个中断,中断源是定时器,中断会途径GPC 128号中断最后到底GIC的128号中断,最后GIC再中断CPU。

但是,软件处理是返回来的:

  1. CPU被中断后,跳到中断向量表中执行GIC驱动定义的中断处理函数发现是128号中断产生
  2. 因为GPC和GIC的级联是一对一的关系,因为也就直接知道了是GIC的128号中断产生。
  3. 最后因为定时器并不是一个中断控制器,已经是最后一层了,就直接调用定时器的中断处理函数,比如线程调度(需要看这个定时器是不是作为arch timer)
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分