首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-04 14:01
  • 3987
  • 11513
blog.csdn.net

往期内容

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

  1. 深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现
  2. Linux内核中IRQ Domain的结构、操作及映射机制详解
  3. 中断描述符irq_desc成员详解

pinctrl和gpio子系统专栏:

  1. 专栏地址:pinctrl和gpio子系统

  2. 编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

    – 末片,有专栏内容观看顺序

input子系统专栏:

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

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有专栏内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有专栏内容观看顺序

img

一.中断描述符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,定义如下:

\Linux-4.9.88\Linux-4.9.88\kernel\irq\internals.h
#ifdef CONFIG_SPARSE_IRQ
# define IRQ_BITMAP_BITS	(NR_IRQS + 8196)
#else
# define IRQ_BITMAP_BITS	NR_IRQS
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.静态表中的irq_desc初始化

\Linux-4.9.88\Linux-4.9.88\kernel\irq\irqdesc.c
int __init early_irq_init(void)
{
	int count, i, node = first_online_node;
	struct irq_desc *desc;

	init_irq_default_affinity();

	printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);

	desc = irq_desc; //是一个全局数组,保存系统中的所有中断描述符(struct irq_desc)。每个元素对应一个中断线。
	count = ARRAY_SIZE(irq_desc);//用于获取 irq_desc 数组的大小(也就是中断的数量)

	for (i = 0; i < count; i++) {//遍历中断描述符数组进行初始化
		desc[i].kstat_irqs = alloc_percpu(unsigned int); //-------(A)
		alloc_masks(&desc[i], GFP_KERNEL, node);//-------(B)
		raw_spin_lock_init(&desc[i].lock); //-------(C)
		lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);//-------(D)
		desc_set_defaults(i, &desc[i], node, NULL, NULL);-------(E)
	}
	return arch_early_irq_init();  //调用架构特定的中断初始化函数
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

(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初始化

\Linux-4.9.88\Linux-4.9.88\kernel\irq\irqdesc.c
int __init early_irq_init(void)
{
	int i, initcnt, node = first_online_node;
	struct irq_desc *desc;

	init_irq_default_affinity(); //--------(1)

	/* Let arch update nr_irqs and return the nr of preallocated irqs */
	initcnt = arch_probe_nr_irqs();  //--------(2)
	printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt);

	if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))
		nr_irqs = IRQ_BITMAP_BITS; // 确保最大中断数量不超过最大数量,如果超过了就限制为IRQ_BITMAP_BITS
                                //IRQ_BITMAP_BITS 定义了系统中断位图的最大大小,用来存储哪些中断已经被分配或使用。
	if (WARN_ON(initcnt > IRQ_BITMAP_BITS))
		initcnt = IRQ_BITMAP_BITS;  //同理

	if (initcnt > nr_irqs)
		nr_irqs = initcnt;  //--------(3)

	for (i = 0; i < initcnt; i++) {  //--------(4)
		desc = alloc_desc(i, node, 0, NULL, NULL);
		set_bit(i, allocated_irqs);
		irq_insert_desc(i, desc);
	}
	return arch_early_irq_init();//架构特定的初始化函数,负责进一步完成与体系结构相关的中断初始化工作
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

(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)的介绍中):

#ifdef CONFIG_SPARSE_IRQ
int __init arch_probe_nr_irqs(void)
{
	nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
	return nr_irqs;
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

而调用完early_irq_init初始化完,分配完中断完描述符并插入到radix tree后,想要再自己去分配irq_desc并实现插入到radix tree,该怎么做???其实内核中也有提供相关的API,具体的看下面小节

4.分配和释放irq_desc

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

1.2 irq_alloc_descs

\Linux-4.9.88\Linux-4.9.88\include\linux\irq.h:
/* use macros to avoid needing export.h for THIS_MODULE */
#define irq_alloc_descs(irq, from, cnt, node)	\
	__irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL)

-------->

\Linux-4.9.88\kernel\irq\irqdesc.c:
/**
 * irq_alloc_descs - allocate and initialize a range of irq descriptors
 * @irq:	Allocate for specific irq number if irq >= 0
 * @from:	Start the search from this irq number
 * @cnt:	Number of consecutive irqs to allocate.
 * @node:	Preferred node on which the irq descriptor should be allocated
 * @owner:	Owning module (can be NULL)
 * @affinity:	Optional pointer to an affinity mask array of size @cnt which
 *		hints where the irq descriptors should be allocated and which
 *		default affinities to use
 *
 * Returns the first irq number or error code
 */
int __ref
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
		  struct module *owner, const struct cpumask *affinity)
{
	int start, ret;

	if (!cnt)
		return -EINVAL;

	if (irq >= 0) {
		if (from > irq)
			return -EINVAL;
		from = irq;
	} else {
		/*
		 * For interrupts which are freely allocated the
		 * architecture can force a lower bound to the @from
		 * argument. x86 uses this to exclude the GSI space.
		 */
		from = arch_dynirq_lower_bound(from);
	}

	mutex_lock(&sparse_irq_lock);

	start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,
					   from, cnt, 0);
	ret = -EEXIST;
	if (irq >=0 && start != irq)
		goto unlock;

	if (start + cnt > nr_irqs) {
		ret = irq_expand_nr_irqs(start + cnt);
		if (ret)
			goto unlock;
	}
	ret = alloc_descs(start, cnt, node, affinity, owner);
unlock:
	mutex_unlock(&sparse_irq_lock);
	return ret;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

动态分配一系列中断描述符:

  • irq: 起始的中断号,如果为负数则系统自动选择。
  • from: 分配起始范围(最小中断号)。
  • cnt: 需要分配的中断描述符数量。
  • node: NUMA 节点,用于指定在哪个节点上分配内存。

该函数首先检查 IRQ_BITMAP_BITS 位图,确定哪些中断号是空闲的,并根据 cnt 参数指定的数量进行分配。分配成功后,使用 alloc_desc() 对每个中断描述符进行初始化,确保其可以正确工作。

1.3 irq_free_descs

/**
 * irq_free_descs - free irq descriptors
 * @from:	Start of descriptor range
 * @cnt:	Number of consecutive irqs to free
 */
void irq_free_descs(unsigned int from, unsigned int cnt)
{
	int i;

	if (from >= nr_irqs || (from + cnt) > nr_irqs)
		return;

	mutex_lock(&sparse_irq_lock);
	for (i = 0; i < cnt; i++)
		free_desc(from + i);

	bitmap_clear(allocated_irqs, from, cnt);
	mutex_unlock(&sparse_irq_lock);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

释放已分配的一系列中断描述符:

  • 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 函数中被调用,用于设置中断控制器、触发方式等信息。

典型的流程示例,假设有一个外设设备在设备树中定义了中断信息,它的初始化过程大致如下:

  1. 驱动程序调用 of_irq_get() 从设备树解析出设备的硬件中断号和触发方式。
  2. 使用 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的分配问题)
  3. 通过 irq_set_chip() 等接口,将该 IRQ 号的中断描述符配置为合适的中断控制器和处理函数。
  4. 注册中断处理程序,通过 request_irq() 将具体的中断处理函数注册到系统中。

2.irq_set_chip

irq_set_chip 函数用于为指定的 IRQ 号设置其对应的中断控制器 (irq_chip) 结构体。该函数的主要作用是将传入的 irq_chip 结构体指针赋值给 IRQ 描述符 (irq_desc) 中的 irq_data.chip 成员,从而关联中断处理的硬件操作。

\Linux-4.9.88\Linux-4.9.88\kernel\irq\chip.c
/**
 *	irq_set_chip - set the irq chip for an irq
 *	@irq:	irq number
 *	@chip:	pointer to irq chip description structure
 */
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); //-------(1)

	if (!desc)
		return -EINVAL;

	if (!chip)
		chip = &no_irq_chip;

	desc->irq_data.chip = chip;
	irq_put_desc_unlock(desc, flags); //-------(2)
	/*
	 * For !CONFIG_SPARSE_IRQ make the irq show up in
	 * allocated_irqs.
	 */
	irq_mark_irq(irq);//-------(3)
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

(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

设置中断触发类型

\Linux-4.9.88\kernel\irq\chip.c
/**
 *	irq_set_type - set the irq trigger type for an irq
 *	@irq:	irq number
 *	@type:	IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
 */
int irq_set_irq_type(unsigned int irq, unsigned int type)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
	int ret = 0;

	if (!desc)
		return -EINVAL;

	ret = __irq_set_trigger(desc, type);
	irq_put_desc_busunlock(desc, flags);
	return ret;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

诶??有没有发现,它获取描述符的函数是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

/**
 *	irq_set_chip_data - set irq chip data for an irq
 *	@irq:	Interrupt number
 *	@data:	Pointer to chip specific data
 *
 *	Set the hardware irq chip data for an irq
 */
int irq_set_chip_data(unsigned int irq, void *data)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);

	if (!desc)
		return -EINVAL;
	desc->irq_data.chip_data = data;
	irq_put_desc_unlock(desc, flags);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这个就比较简单啦,就是设置私有数据而已

5.__irq_set_handler

__irq_set_handler就是设定high level handler的接口函数,不过一般不会直接调用,而是通过irq_set_chip_and_handler_name或者irq_set_chip_and_handler来进行设定。

\Linux-4.9.88\kernel\irq\chip.c
void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
		  const char *name)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);

	if (!desc)
		return;

	__irq_do_set_handler(desc, handle, is_chained, name);
	irq_put_desc_busunlock(desc, flags);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中,主要是这个函数:

static void
__irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
		     int is_chained, const char *name)
{
	if (!handle) {
		handle = handle_bad_irq;
	} else {
		struct irq_data *irq_data = &desc->irq_data;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
		/*
		 * With hierarchical domains we might run into a
		 * situation where the outermost chip is not yet set
		 * up, but the inner chips are there.  Instead of
		 * bailing we install the handler, but obviously we
		 * cannot enable/startup the interrupt at this point.
		 */
		while (irq_data) {
			if (irq_data->chip != &no_irq_chip)
				break;
			/*
			 * Bail out if the outer chip is not set up
			 * and the interrrupt supposed to be started
			 * right away.
			 */
			if (WARN_ON(is_chained))
				return;
			/* Try the parent */
			irq_data = irq_data->parent_data;
		}
#endif
		if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
			return;
	}

	/* Uninstall? */
	if (handle == handle_bad_irq) {
		if (desc->irq_data.chip != &no_irq_chip)
			mask_ack_irq(desc);
		irq_state_set_disabled(desc);
		if (is_chained)
			desc->action = NULL;
		desc->depth = 1;
	}
	desc->handle_irq = handle;
	desc->name = name;

	if (handle != handle_bad_irq && is_chained) {
		unsigned int type = irqd_get_trigger_type(&desc->irq_data);

		/*
		 * We're about to start this interrupt immediately,
		 * hence the need to set the trigger configuration.
		 * But the .set_type callback may have overridden the
		 * flow handler, ignoring that we're dealing with a
		 * chained interrupt. Reset it immediately because we
		 * do know better.
		 */
		if (type != IRQ_TYPE_NONE) {
			__irq_set_trigger(desc, type);
			desc->handle_irq = handle;
		}

		irq_settings_set_noprobe(desc);
		irq_settings_set_norequest(desc);
		irq_settings_set_nothread(desc);
		desc->action = &chained_action;
		irq_startup(desc, true);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

其中主要是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 来启动中断处理过程。
注:本文转载自blog.csdn.net的憧憬一下的文章"https://blog.csdn.net/caiji0169/article/details/143637384"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top