Linux 内核中断描述符 (irq_desc) 的初始化与动态分配机制详解 ...

打印 上一主题 下一主题

主题 1766|帖子 1766|积分 5298

往期内容

   本专栏往期内容,interrtupr子系统:
  

  • 深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现
  • Linux内核中IRQ Domain的结构、操作及映射机制详解
  • 中断描述符irq_desc成员详解
  pinctrl和gpio子系统专栏:
  

  • 专栏地点:pinctrl和gpio子系统
  • 编写假造的GPIO控制器的驱动程序:和pinctrl的交互使用
    – 末片,有专栏内容观看次序
  input子系统专栏:
  

  • 专栏地点:input子系统
  • input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有专栏内容观看次序
  I2C子系统专栏:
  

  • 专栏地点:IIC子系统
  • 详细芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有专栏内容观看次序
  总线和装备树专栏:
  

  • 专栏地点:总线和装备树
  • 装备树与 Linux 内核装备驱动模型的整合-CSDN博客
    – 末篇,有专栏内容观看次序
  

一.中断描述符irq_desc初始化相关的api

1. IRQ 位图限制

在 Linux 内核中,IRQ_BITMAP_BITS 用于定义系统中可用的最大中断号范围。根据是否开启了 CONFIG_SPARSE_IRQ 配置(开了的话就使用radix tree的方式存储中断描述符irq_desc),IRQ_BITMAP_BITS 的值有所不同:


  • 如果定义了 CONFIG_SPARSE_IRQ

    • IRQ_BITMAP_BITS = NR_IRQS + 8196
    • 这意味着在原本定义的 NR_IRQS 基础上,增加了 8196 个可用的中断号范围。这种情况通常用于较大或复杂的系统,尤其是那些使用希奇 IRQ 机制的系统。CONFIG_SPARSE_IRQ 答应系统动态管理中断号分配,减少浪费,优化性能。

  • 如果未定义 CONFIG_SPARSE_IRQ

    • IRQ_BITMAP_BITS = NR_IRQS
    • 这意味着中断号的最大范围仅限于静态定义的 NR_IRQS,即系统不支持希奇 IRQ 机制。得当较简单的系统或中断号固定的场景。

初始化的内容和静态定义的中断描述符初始化过程是一样的。最大可以分配的ID是IRQ_BITMAP_BITS,定义如下:
  1. \Linux-4.9.88\Linux-4.9.88\kernel\irq\internals.h
  2. #ifdef CONFIG_SPARSE_IRQ
  3. # define IRQ_BITMAP_BITS        (NR_IRQS + 8196)
  4. #else
  5. # define IRQ_BITMAP_BITS        NR_IRQS
  6. #endif
复制代码
2.静态表中的irq_desc初始化

  1. \Linux-4.9.88\Linux-4.9.88\kernel\irq\irqdesc.c
  2. int __init early_irq_init(void)
  3. {
  4.         int count, i, node = first_online_node;
  5.         struct irq_desc *desc;
  6.         init_irq_default_affinity();
  7.         printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);
  8.         desc = irq_desc; //是一个全局数组,保存系统中的所有中断描述符(struct irq_desc)。每个元素对应一个中断线。
  9.         count = ARRAY_SIZE(irq_desc);//用于获取 irq_desc 数组的大小(也就是中断的数量)
  10.         for (i = 0; i < count; i++) {//遍历中断描述符数组进行初始化
  11.                 desc[i].kstat_irqs = alloc_percpu(unsigned int); //-------(A)
  12.                 alloc_masks(&desc[i], GFP_KERNEL, node);//-------(B)
  13.                 raw_spin_lock_init(&desc[i].lock); //-------(C)
  14.                 lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);//-------(D)
  15.                 desc_set_defaults(i, &desc[i], node, NULL, NULL);-------(E)
  16.         }
  17.         return arch_early_irq_init();  //调用架构特定的中断初始化函数
  18. }
复制代码
(A)alloc_percpu(unsigned int) 为每个 CPU 分配内存,用来存储该中断在各 CPU 上触发的次数。kstat_irqs 是一个指向 per-CPU 统计数据的指针,用于跟踪中断的统计信息。per-CPU 内存分配意味着每个 CPU 都有一个独立的统计数据存储地区,这样不同 CPU 之间的中断统计互不干扰。
(B)alloc_masks() 函数分配与中断掩码相关的资源。掩码用于表示哪些 CPU 处理某个中断,或者用于屏蔽某个中断。GFP_KERNEL 表示常规的内核内存分配标志,node 表示内存分配的 NUMA 节点。
(C)aw_spin_lock_init() 初始化每个中断描述符的锁,用于掩护并发访问。这是由于中断处理是并发的,多个 CPU 可能会同时访问某个中断的描述符。
(D)lockdep_set_class() 设置自旋锁的锁依靠关系,用于锁依靠检查机制(lockdep),以帮助检测死锁等题目。
(E)desc_set_defaults() 函数为每个中断描述符设置默认值。它会初始化描述符的各个字段,例如中断号、处理器亲和性、触发方式等,确保描述符有一个初始的有效状态。这里的 node 表示使用的 NUMA 节点,NULL 表示没有传入自定义的中断处理函数或特定的数据
3.Radix tree中的irq_desc初始化

  1. \Linux-4.9.88\Linux-4.9.88\kernel\irq\irqdesc.c
  2. int __init early_irq_init(void)
  3. {
  4.         int i, initcnt, node = first_online_node;
  5.         struct irq_desc *desc;
  6.         init_irq_default_affinity(); //--------(1)
  7.         /* Let arch update nr_irqs and return the nr of preallocated irqs */
  8.         initcnt = arch_probe_nr_irqs();  //--------(2)
  9.         printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt);
  10.         if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))
  11.                 nr_irqs = IRQ_BITMAP_BITS; // 确保最大中断数量不超过最大数量,如果超过了就限制为IRQ_BITMAP_BITS
  12.                                 //IRQ_BITMAP_BITS 定义了系统中断位图的最大大小,用来存储哪些中断已经被分配或使用。
  13.         if (WARN_ON(initcnt > IRQ_BITMAP_BITS))
  14.                 initcnt = IRQ_BITMAP_BITS;  //同理
  15.         if (initcnt > nr_irqs)
  16.                 nr_irqs = initcnt;  //--------(3)
  17.         for (i = 0; i < initcnt; i++) {  //--------(4)
  18.                 desc = alloc_desc(i, node, 0, NULL, NULL);
  19.                 set_bit(i, allocated_irqs);
  20.                 irq_insert_desc(i, desc);
  21.         }
  22.         return arch_early_irq_init();//架构特定的初始化函数,负责进一步完成与体系结构相关的中断初始化工作
  23. }
复制代码
(1)初始化默认的中断亲和性,用于决定中断在哪些 CPU 上处理,特别是在多处理器系统(SMP)中。它与前面函数中的 init_irq_default_affinity() 类似。
(2)用来检测系统支持的最大中断数。 并返回初始化时必要预分配的中断数量 initcnt。然后打印系统中支持的最大中断数量 NR_IRQS、当前已分配的中断数 nr_irqs 和必要预分配的中断数 initcnt。
(3) 如果初始化时必要的中断数 initcnt 大于当前系统已分配的中断数 nr_irqs,将 nr_irqs 更新为 initcnt。这样可以确保系统可以或许支持这些中断号。
(4)循环遍历每一个中断号,并为每个中断号分配一个中断描述符,随后将其插入 radix tree 中:


  • 分配中断描述符。
  • 设置中断位图。llocated_irqs 是一个全局位图,用来追踪哪些中断号已经被使用。
  • 将中断描述符插入 radix tree
在深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现中的 3. 中断描述符的存储和管理 提到过,必要配置了CONFIG_SPARSE_IRQ选项,系统才气使用radix tree保存中断描述符,那么也就是说有几个irq_desc就应该有几个IRQ(Linux 系统分配的软件中断号),而且应该是动态去分配的。但是在上面这个函数early_irq_init介绍中,中断描述符初始化的时间,也有机会预先分配一定数量的IRQ。这个数量由arch_probe_nr_irqs决定(序号为(2)的介绍中):
  1. #ifdef CONFIG_SPARSE_IRQ
  2. int __init arch_probe_nr_irqs(void)
  3. {
  4.         nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
  5.         return nr_irqs;
  6. }
  7. #endif
复制代码
而调用完early_irq_init初始化完,分配完中断完描述符并插入到radix tree后,想要再本身去分配irq_desc并实现插入到radix tree,该怎么做???着实内核中也有提供相关的API,详细的看下面末节
4.分配和开释irq_desc

对于使用Radix tree来保存中断描述符DB的linux kernel,其中断描述符是动态分配的,可以使用irq_alloc_descsirq_free_descs来分配和开释中断描述符。alloc_desc函数(内部调用)也会对中断描述符进行初始化,
1.2 irq_alloc_descs

  1. \Linux-4.9.88\Linux-4.9.88\include\linux\irq.h:
  2. /* use macros to avoid needing export.h for THIS_MODULE */
  3. #define irq_alloc_descs(irq, from, cnt, node)        \
  4.         __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL)
  5. -------->
  6. \Linux-4.9.88\kernel\irq\irqdesc.c:
  7. /**
  8. * irq_alloc_descs - allocate and initialize a range of irq descriptors
  9. * @irq:        Allocate for specific irq number if irq >= 0
  10. * @from:        Start the search from this irq number
  11. * @cnt:        Number of consecutive irqs to allocate.
  12. * @node:        Preferred node on which the irq descriptor should be allocated
  13. * @owner:        Owning module (can be NULL)
  14. * @affinity:        Optional pointer to an affinity mask array of size @cnt which
  15. *                hints where the irq descriptors should be allocated and which
  16. *                default affinities to use
  17. *
  18. * Returns the first irq number or error code
  19. */
  20. int __ref
  21. __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
  22.                   struct module *owner, const struct cpumask *affinity)
  23. {
  24.         int start, ret;
  25.         if (!cnt)
  26.                 return -EINVAL;
  27.         if (irq >= 0) {
  28.                 if (from > irq)
  29.                         return -EINVAL;
  30.                 from = irq;
  31.         } else {
  32.                 /*
  33.                  * For interrupts which are freely allocated the
  34.                  * architecture can force a lower bound to the @from
  35.                  * argument. x86 uses this to exclude the GSI space.
  36.                  */
  37.                 from = arch_dynirq_lower_bound(from);
  38.         }
  39.         mutex_lock(&sparse_irq_lock);
  40.         start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,
  41.                                            from, cnt, 0);
  42.         ret = -EEXIST;
  43.         if (irq >=0 && start != irq)
  44.                 goto unlock;
  45.         if (start + cnt > nr_irqs) {
  46.                 ret = irq_expand_nr_irqs(start + cnt);
  47.                 if (ret)
  48.                         goto unlock;
  49.         }
  50.         ret = alloc_descs(start, cnt, node, affinity, owner);
  51. unlock:
  52.         mutex_unlock(&sparse_irq_lock);
  53.         return ret;
  54. }
复制代码
动态分配一系列中断描述符:


  • irq: 起始的中断号,如果为负数则系统自动选择。
  • from: 分配起始范围(最小中断号)。
  • cnt: 必要分配的中断描述符数量。
  • node: NUMA 节点,用于指定在哪个节点上分配内存。
该函数首先检查 IRQ_BITMAP_BITS 位图,确定哪些中断号是空闲的,并根据 cnt 参数指定的数量进行分配。分配乐成后,使用 alloc_desc() 对每个中断描述符进行初始化,确保其可以正确工作。
1.3 irq_free_descs

  1. /**
  2. * irq_free_descs - free irq descriptors
  3. * @from:        Start of descriptor range
  4. * @cnt:        Number of consecutive irqs to free
  5. */
  6. void irq_free_descs(unsigned int from, unsigned int cnt)
  7. {
  8.         int i;
  9.         if (from >= nr_irqs || (from + cnt) > nr_irqs)
  10.                 return;
  11.         mutex_lock(&sparse_irq_lock);
  12.         for (i = 0; i < cnt; i++)
  13.                 free_desc(from + i);
  14.         bitmap_clear(allocated_irqs, from, cnt);
  15.         mutex_unlock(&sparse_irq_lock);
  16. }
复制代码
开释已分配的一系列中断描述符:


  • irq: 起始的中断号。
  • cnt: 必要开释的中断描述符数量。
会从 IRQ_BITMAP_BITS 位图中移除指定的中断号标志位,表示这些中断号不再被占用。同时,调用 free_desc() 来开释每个中断号对应的 irq_desc 结构。
5.静态和动态驱动

无论是静态定义的中断描述符(例如 irq_desc[] 数组中的中断描述符)还是动态分配的中断描述符,初始化过程都是一致的。两者的区别在于:


  • 静态定义的中断描述符:在系统启动时通过 early_irq_init() 函数进行初始化。这些中断描述符是提前预分配的,并直接存储在一个固定大小的数组中。
  • 动态分配的中断描述符:通过 irq_alloc_descs() 动态分配,使用 alloc_desc() 函数初始化,并将其存储在一个更灵活的基数树中。固然也是有机会去预先分配的(early_irq_init)
这两种机制的组合使得 Linux 内核可以或许高效管理中断号,既支持固定的、预分配的中断号,也支持灵活扩展中断号的分配,以满足更复杂系统的需求。
二.和中断控制器相关的irq_desc的接口

这里重要简单讲一下irq_set_chip、irq_set_chip_data以及irq_set_irq_type三个函数,有set肯定有get,着实原理都是差不多的,get感兴趣的去检察内核源码就行了。
1.调用时机

kernel提供了多少的接口API可以让内核其他模块可以操作指定IRQ number的描述符结构。中断描述符中有很多的成员是和底层的中断控制器相关,例如:
(1)该中断描述符对应的irq chip
(2)该中断描述符对应的irq trigger type(中断触发类型)
(3)high level handler
在传统系统中(静态表格管理中断描述符),IRQ 号是固定分配的,每个 IRQ 对应特定的硬件中断控制器和处理器处理该中断的方式(如触发类型)。随着系统规模的扩大,特别是在 SoC (System on Chip) 装备中,越来越多的中断必要处理,而且不同装备可能会有不同的中断控制器。为了解决这个复杂性,内核引入了 IRQ Domain 和装备树机制。
domain这里就不介绍了,在上一片文章解说过(Linux内核中IRQ Domain的结构、操作及映射机制详解),其中有讲过其map 函数的作用是将硬件中断号映射到内核内部的 irq_desc 结构中,而且调用诸如 irq_set_chip、irq_set_handler 等接口为该中断号设置中断控制器和处理函数。 通过内核提供的接口 API,可以对 irq_desc 结构体中的一些关键成员进行配置。这些接口函数在装备驱动程序和中断控制器的 map 函数中被调用,用于设置中断控制器、触发方式等信息。
典型的流程示例,假设有一个外设装备在装备树中定义了中断信息,它的初始化过程大致如下:

  • 驱动程序调用 of_irq_get() 从装备树解析出装备的硬件中断号和触发方式。
  • 使用 irq_domain_alloc_irqs() 或类似接口分配一个 Linux IRQ 号(最后都还是会调用到__irq_alloc_descs,上文介绍过)。比如以这里提到的irq_domain_alloc_irqs—>__irq_domain_alloc_irqs—>irq_domain_alloc_descs—>__irq_alloc_descs(这个函数就有提到irq number的分配题目)
  • 通过 irq_set_chip() 等接口,将该 IRQ 号的中断描述符配置为合适的中断控制器和处理函数。
  • 注册中断处理程序,通过 request_irq() 将详细的中断处理函数注册到系统中。
2.irq_set_chip

irq_set_chip 函数用于为指定的 IRQ 号设置其对应的中断控制器 (irq_chip) 结构体。该函数的重要作用是将传入的 irq_chip 结构体指针赋值给 IRQ 描述符 (irq_desc) 中的 irq_data.chip 成员,从而关联中断处理的硬件操作。
  1. \Linux-4.9.88\Linux-4.9.88\kernel\irq\chip.c
  2. /**
  3. *        irq_set_chip - set the irq chip for an irq
  4. *        @irq:        irq number
  5. *        @chip:        pointer to irq chip description structure
  6. */
  7. int irq_set_chip(unsigned int irq, struct irq_chip *chip)
  8. {
  9.         unsigned long flags;
  10.         struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); //-------(1)
  11.         if (!desc)
  12.                 return -EINVAL;
  13.         if (!chip)
  14.                 chip = &no_irq_chip;
  15.         desc->irq_data.chip = chip;
  16.         irq_put_desc_unlock(desc, flags); //-------(2)
  17.         /*
  18.          * For !CONFIG_SPARSE_IRQ make the irq show up in
  19.          * allocated_irqs.
  20.          */
  21.         irq_mark_irq(irq);//-------(3)
  22.         return 0;
  23. }
复制代码
(1)irq_get_desc_lock() 函数: 通过 irq 号获取对应的 irq_desc 结构体(中断描述符),而且通过加锁机制掩护对 irq_desc 的并发访问。 这个函数用于获取某个中断号的描述符 (irq_desc),同时关闭中断并使用自旋锁 (spinlock) 来掩护中断描述符的访问。这种方式适用于快速访问的硬件资源,如系统内嵌的中断控制器。在这些场景中,访问寄存器非常快,内核通过短暂关闭中断并使用自旋锁可以或许保证对描述符的安全访问,而不会造成显著的性能开销。


  • 加锁:关闭本地中断(以确保不会有新的中断发生)并得到对 irq_desc 的自旋锁 (raw_spin_lock)。
  • flags:保存中断标志位,在解锁时规复之前的状态。
(2)irq_put_desc_unlock: 该函数将之前保存的 flags 标志位规复到原始状态,并开释 irq_desc 上的自旋锁。这是与 irq_get_desc_lock 相对应的操作,确保对 irq_desc 的访问在多核情况中是安全的。
(3)irq_mark_irq: 将当前的 irq 号标记为已分配的状态。 在非 CONFIG_SPARSE_IRQ 配置下,这个函数将静态定义的 irq 标记为已使用,即在系统中体现为已分配。这是为了避免重复分配同一个 irq 号。 对于 CONFIG_SPARSE_IRQ 配置(希奇 IRQ),irq_alloc_desc 或 irq_alloc_descs 在分配 irq_desc 时已经标记了这些 IRQ,所以这一步可能是多余的。但对于静态分配的 irq_desc 来说,这一步非常重要,用来确保内核知道该 irq 已经被使用(由于静态存储的中断描述符没有alloc的概念)。
3.irq_set_irq_type

设置中断触发类型
  1. \Linux-4.9.88\kernel\irq\chip.c
  2. /**
  3. *        irq_set_type - set the irq trigger type for an irq
  4. *        @irq:        irq number
  5. *        @type:        IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
  6. */
  7. int irq_set_irq_type(unsigned int irq, unsigned int type)
  8. {
  9.         unsigned long flags;
  10.         struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
  11.         int ret = 0;
  12.         if (!desc)
  13.                 return -EINVAL;
  14.         ret = __irq_set_trigger(desc, type);
  15.         irq_put_desc_busunlock(desc, flags);
  16.         return ret;
  17. }
复制代码
诶??有没有发现,它获取描述符的函数是irq_get_desc_buslock,而不是像上一个函数使用irq_get_desc_lock来获取。讲一下这个函数: irq_get_desc_buslock函数的使用场景是访问较慢的中断控制器,如通过 I2C、SPI 等总线连接的外部中断控制器。这些控制器的访问速率远远慢于直接内存映射的寄存器,因此使用自旋锁 (spinlock) 会让 CPU 长时间处于忙等候状态,浪费大量的盘算资源。为了避免这种情况,内核使用了一种称为 “bus lock” 的机制(通常通过 mutex 实现),答应在访问慢速总线时避免长时间的自旋锁占用。


  • irq_set_chip 使用 irq_get_desc_lock 是由于它只涉及修改中断描述符的 irq_chip 成员,而不必要与中断控制器进行慢速的总线通讯,因此可以直接使用自旋锁掩护。
  • irq_set_irq_type 使用 irq_get_desc_buslock 是由于它必要调用 irq_set_type,该函数终极可能涉及对中断控制器(包罗慢速的外部中断控制器)的操作。这种情况下,必要使用 bus lock 来避免长时间占用 CPU 自旋等候资源。
这就是两个函数相比,该函数多了个bus的缘故原由,接下来就是irq_get_desc_buslock函数中有个奇怪的宏IRQ_GET_DESC_CHECK_GLOBAL,在上一个函数irq_get_desc_lock中是设定为0的。这个宏看上去是检查什么东西???


  • IRQ_GET_DESC_CHECK_GLOBAL:该宏用于标识在访问中断描述符时是否必要进行“全局”检查。在 1-N 模式中,多个 CPU 可能会处理同一个中断源的哀求,但从硬件和软件角度看,中断的处理必须是全局的,只有一个 CPU 处理该中断,其他 CPU 应该感知到这个状态。因此,这里的“global”意味着这个中断源是共享的,必须在全部 CPU 之间有全局一致性。例如 GIC 中的 SPI 使用这种模型。
  • IRQ_GET_DESC_CHECK_PERCPU:讲了global全局,就再讲一下percpu。与 global 模型相对的 per-CPU 模型应用于 N-N 模式。在这种模式中,每个 CPU 都有本身的一套中断寄存器,对应的中断源是“独立的”。这意味着每个 CPU 可以独立处理中断,相互之间没有冲突,例如 GIC 中的 PPI 和 SGI 使用 N-N 模式。
irq_set_irq_type 设置中断的触发类型(如电平触发或边沿触发)。这种操作可能会影响中断控制器的全局状态,因此必须检查该中断是否属于全局模式(1-N 模式)。如果该中断是全局模式的,中断控制器和中断寄存器是全局共享的,必要确保修改操作的正确性。相反,如果中断属于 per-CPU 模式,尝试设置触发类型将返回错误,由于每个 CPU 的中断状态是独立的。
1-N 模式 中,修改触发类型必要全局同步,由于这个中断源的状态是由全部 CPU 共享的。例如,SPI 中断只能由一个 CPU 处理,如果一个 CPU 修改了触发类型,其他 CPU 也必须遵照同样的规则。
通俗点讲就是: 1-N中(GLOBAL),这个中断源的状态必须让全部的CPU共享,也就是全局性。就是总不能第一个CPU抢先得到处理中断的机会,此时中断源触发类型没改(比如是上升沿);而第二次中断源触发类型改了(下升沿),被第二个CPU抢先了,而第二个CPU对中断源的触发类型的认知是更改后的(下升沿)。而第一个CPU对其的认知由于不是全局性,仍停顿在没改之前(上升沿),当下一次抢到处理机会时,就出了岔子,接纳上升沿的的重点处理方式,这不就出了题目了。
4.irq_set_chip_data

  1. /**
  2. *        irq_set_chip_data - set irq chip data for an irq
  3. *        @irq:        Interrupt number
  4. *        @data:        Pointer to chip specific data
  5. *
  6. *        Set the hardware irq chip data for an irq
  7. */
  8. int irq_set_chip_data(unsigned int irq, void *data)
  9. {
  10.         unsigned long flags;
  11.         struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
  12.         if (!desc)
  13.                 return -EINVAL;
  14.         desc->irq_data.chip_data = data;
  15.         irq_put_desc_unlock(desc, flags);
  16.         return 0;
  17. }
复制代码
这个就比较简单啦,就是设置私有数据而已
5.__irq_set_handler

__irq_set_handler就是设定high level handler的接口函数,不外一般不会直接调用,而是通过irq_set_chip_and_handler_name或者irq_set_chip_and_handler来进行设定。
  1. \Linux-4.9.88\kernel\irq\chip.c
  2. void
  3. __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
  4.                   const char *name)
  5. {
  6.         unsigned long flags;
  7.         struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
  8.         if (!desc)
  9.                 return;
  10.         __irq_do_set_handler(desc, handle, is_chained, name);
  11.         irq_put_desc_busunlock(desc, flags);
  12. }
复制代码
其中,重要是这个函数:
  1. static void
  2. __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
  3.                      int is_chained, const char *name)
  4. {
  5.         if (!handle) {
  6.                 handle = handle_bad_irq;
  7.         } else {
  8.                 struct irq_data *irq_data = &desc->irq_data;
  9. #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
  10.                 /*
  11.                  * With hierarchical domains we might run into a
  12.                  * situation where the outermost chip is not yet set
  13.                  * up, but the inner chips are there.  Instead of
  14.                  * bailing we install the handler, but obviously we
  15.                  * cannot enable/startup the interrupt at this point.
  16.                  */
  17.                 while (irq_data) {
  18.                         if (irq_data->chip != &no_irq_chip)
  19.                                 break;
  20.                         /*
  21.                          * Bail out if the outer chip is not set up
  22.                          * and the interrrupt supposed to be started
  23.                          * right away.
  24.                          */
  25.                         if (WARN_ON(is_chained))
  26.                                 return;
  27.                         /* Try the parent */
  28.                         irq_data = irq_data->parent_data;
  29.                 }
  30. #endif
  31.                 if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
  32.                         return;
  33.         }
  34.         /* Uninstall? */
  35.         if (handle == handle_bad_irq) {
  36.                 if (desc->irq_data.chip != &no_irq_chip)
  37.                         mask_ack_irq(desc);
  38.                 irq_state_set_disabled(desc);
  39.                 if (is_chained)
  40.                         desc->action = NULL;
  41.                 desc->depth = 1;
  42.         }
  43.         desc->handle_irq = handle;
  44.         desc->name = name;
  45.         if (handle != handle_bad_irq && is_chained) {
  46.                 unsigned int type = irqd_get_trigger_type(&desc->irq_data);
  47.                 /*
  48.                  * We're about to start this interrupt immediately,
  49.                  * hence the need to set the trigger configuration.
  50.                  * But the .set_type callback may have overridden the
  51.                  * flow handler, ignoring that we're dealing with a
  52.                  * chained interrupt. Reset it immediately because we
  53.                  * do know better.
  54.                  */
  55.                 if (type != IRQ_TYPE_NONE) {
  56.                         __irq_set_trigger(desc, type);
  57.                         desc->handle_irq = handle;
  58.                 }
  59.                 irq_settings_set_noprobe(desc);
  60.                 irq_settings_set_norequest(desc);
  61.                 irq_settings_set_nothread(desc);
  62.                 desc->action = &chained_action;
  63.                 irq_startup(desc, true);
  64.         }
  65. }
复制代码
其中重要是is_chained:指示这个中断是否是链式处理的标志,链式中断是嵌套在另一层中断控制器之下的中断处理模式(这个在之前有讲过,链式和层式中断)。
如果 is_chained 为真,表示我们正在处理链式中断:


  • 获取中断的触发类型 (IRQ_TYPE_EDGE 或 IRQ_TYPE_LEVEL 等),并通过 __irq_set_trigger 设置触发类型。
  • 必要注意的是,设置触发类型的操作可能会导致 flow handler(中断处理流程函数)被覆盖,因此在调用之后重新设置 desc->handle_irq 为指定的 handle。
  • 设置了一些中断的属性,比如 irq_settings_set_noprobe、irq_settings_set_norequest、irq_settings_set_nothread,这些属性告诉内核该中断不应当被探测、哀求或者在线程上下文中处理。
  • 最后调用 irq_startup 来启动中断处理过程。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

温锦文欧普厨电及净水器总代理

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表