Linux内核深度解析之中断、异常和系统调用——中断

本文深入探讨ARM64架构下的中断处理机制,包括中断控制器、中断域的概念,以及GIC v2控制器的具体实现。文章详细讲解了中断的硬件与软件处理流程,中断线程化的优势,中断的亲和性设置,处理器间中断的应用,以及如何在代码中禁用和启用中断。

中断

中断是外围设备通知处理器的一种机制。

1. 中断控制器

外围设备不是把中断请求直接发送给处理器,而是发给中断控制器,由中断控制器转发给处理器。

不同种类的中断控制器的访问方法存在差异,为了屏蔽差异,内核定义了中断控制器描述符irq_chip,每种中断控制器自定义各种操作函数。GIC v2控制器的描述符如下:

drivers/irqchip/irq-gic.c

tatic const struct irq_chip gic_chip = {
	.irq_mask		= gic_mask_irq,
	.irq_unmask		= gic_unmask_irq,
	.irq_eoi		= gic_eoi_irq,
	.irq_set_type		= gic_set_type,
	.irq_get_irqchip_state	= gic_irq_get_irqchip_state,
	.irq_set_irqchip_state	= gic_irq_set_irqchip_state,
	.flags			= IRQCHIP_SET_TYPE_MASKED |
				  IRQCHIP_SKIP_SET_WAKE |
				  IRQCHIP_MASK_ON_SUSPEND,
};

2. 中断域

一个大型系统可能有多个中断控制器,这些中断控制器可以级联,一个中断控制器作为中断源连接到另一个中断控制器,但只有一个中断控制器作为根控制器直接连接到处理器。为了把每个中断控制器本地的硬件中断映射到全局唯一的Linux中断号(也称为虚拟中断),内核定义了中断域irq_domain,每个中断控制器由自己的中断域。

2.1. 创建中断域

中断控制器的驱动程序使用分配函数irq_domain_add_*()创建和注册中断域。

2.2. 创建映射

创建中断域以后,需要向中断域添加硬件中断号到Linux中断号的映射,内核提供了函数irq_create_mapping:

unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq);

输入参数是中断域和硬件中断号,返回Linux中断号。

该函数首先分配Linux中断号,然后把硬件中断号到Linux中断号的映射添加到中断域。

2.3. 查找映射

中断处理程序需要根据硬件中断号查找Linux中断号,内核提供了函数irq_find_mapping:

unsigned int irq_find_mapping(struct irq_domain *host, irq_hw_number_t hwirq);

输入参数是中断域和硬件中断号,返回Linux中断号。

3. 中断控制器驱动初始化

3.1. 设备树源文件

ARM64架构使用扁平设备树(Flattened Device Tree,FDT)描述板卡的硬件信息,好处是可以把板卡的特定的代码从内核中删除,编译生成通用的板卡无关的内核。

设备树源文件是文本文件,扩展名是“.dts”,需要在设备树源文件中描述中断的相关信息:

(1)中断控制器的信息

(2)对于作为中断源的外围设备,需要描述设备连接到哪个中断控制器,使用哪个硬件中断号

参考arch/arm64/boot/dts/arm/foundation-v8.dtsi、arch/arm64/boot/dts/arm/foundation-v8-gicv2.dtsi。

3.2. 中断控制器匹配表

在GIC v2控制器的驱动程序中,定义了多个类型为of_device_id的静态变量,成员compatible是驱动程序支持的设备的名称,成员data是初始化函数,编译器把这些静态变量放在专用的节“__irqchip_of_table”里面。

我们把节“__irqchip_of_table”称为中断控制器匹配表,里面每个表项的格式是结构体of_device_id。

drivers/irqchip/irq-gic.c

IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
...
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
...

把宏IRQCHIP_DECLARE展开以后是:

static const struct of_device_id __of_table_cortex_gic_400
	__section(__irqchip_of_table)
		= { .compatible = "arm,gic-g400",	
		    .data = gic_of_init  }
...
static const struct of_device_id __of_table_cortex_a15_gic
	__section(__irqchip_of_table)
		= { .compatible = "arm,cortex-a15-gic",	
		    .data = gic_of_init  }
static const struct of_device_id __of_table_cortex_a9_gic
	__section(__irqchip_of_table)
		= { .compatible = "arm,cortex-a9-gic",	
		    .data = gic_of_init  }
...

3.3. 初始化

在内核初始化的时候,匹配设备树文件中的中断控制器的属性“compatible”和内核的中断控制器匹配表,找到合适的中断控制器驱动程序,执行驱动程序的初始化函数。

start_kernel()  ->  init_IRQ()  ->  irqchip_init()

drivers/irqchip/irqchip.c
void __init irqchip_init(void)
{
	of_irq_init(__irqchip_of_table);    // 参数是中断控制器匹配表的起始地址__irqchip_of_table
	...
}

(1)函数of_irq_init

driver/of/irq.c
/**
 * of_irq_init - Scan and init matching interrupt controllers in DT
 * @matches: 0 terminated array of nodes to match and init function to call
 *
 * This function scans the device tree for matching interrupt controller nodes,
 * and calls their initialization functions in order with parents first.
 */
void __init of_irq_init(const struct of_device_id *matches)
{
	const struct of_device_id *match;
	struct device_node *np, *parent = NULL;
	struct of_intc_desc *desc, *temp_desc;
	struct list_head intc_desc_list, intc_parent_list;

	INIT_LIST_HEAD(&intc_desc_list);
	INIT_LIST_HEAD(&intc_parent_list);

	for_each_matching_node_and_match(np, matches, &match) {		/* 遍历设备树文件的设备节点。如果属性compatible和中断控制器匹配表中的任何一条表项的字段compatible匹配,处理如下 */
		if (!of_property_read_bool(np, "interrupt-controller") ||
				!of_device_is_available(np))		/* 如果没有节点属性interrupt-controller,说明设备不是中断控制器,忽略该设备 */
			continue;

		if (WARN(!match->data, "of_irq_init: no init function for %s\n",
			 match->compatible))
			continue;

		/*
		 * Here, we allocate and populate an of_intc_desc with the node
		 * pointer, interrupt-parent device_node etc.
		 */
		desc = kzalloc(sizeof(*desc), GFP_KERNEL);		/* 分配一个of_intc_desc实例 */
		if (WARN_ON(!desc)) {
			of_node_put(np);
			goto err;
		}

		desc->irq_init_cb = match->data;		/* 成员irq_init_cb保存初始化函数 */
		desc->dev = of_node_get(np);		/* 成员dev保存本设备的device_node */
		desc->interrupt_parent = of_irq_find_parent(np);		/* 成员interrupt保存父设备 */
		if (desc->interrupt_parent == np)
			desc->interrupt_parent = NULL;
		list_add_tail(&desc->list, &intc_desc_list);		/* 把of_intc_desc实例添加到链表intc_desc_list中 */
	}

	/*
	 * The root irq controller is the one without an interrupt-parent.
	 * That one goes first, followed by the controllers that reference it,
	 * followed by the ones that reference the 2nd level controllers, etc.
	 */
	while (!list_empty(&intc_desc_list)) {		/* 遍历链表intc_desc_list,从根设备开始,先执行父设备的初始化函数,然后执行子设备的初始化函数 */
		/*
		 * Process all 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值