Linux中断子系统domain的示例分析(domain,linux,开发技术)

时间:2024-04-29 12:30:31 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

随着现代CPU的复杂度加大,外设中断数量增加,实际上系统可能同时需要多个中断控制器进行级联,面对这样的趋势,Linux引入了irq domain这个概念。

对于Linux系统中所有的interrupt controller都会形成树状结构,对于每个interrupt controller都可以连接若干个外设的中断请求,interrupt controller会对连接其上的interrupt source进行编号(也就是HW interrupt ID,后面我们叫hirq),但这个编号仅仅限制在本interrupt controller范围内。既然存在hirq,那么在系统软件中, 就存在一个全局统管的编号,这个编号可以定位控制器和控制器中的中断号,我们把这个编号叫做virq(我们在Linux系统中调用API request_irq中需要的irq号 实际上是virq号)。对于中断级联,我们以TI的TiTDA系列为例,如下图:

Linux中断子系统domain的示例分析
Linux中断子系统domain详解Linux中断子系统domain详解

如上图,该CPU是直接连接在中断控制器GICv3上面的,但是GIC不是直接和设备相连,而是与INTR控制器连接,而INTR控制器又与INTA控制器连接,最后INTA控制器才直接和设备相连。在这里其实就简单的形成了一个简单的树形结构。

domain里面的hirq排列可以是树形的也可以是线性的,本文主要说树形的

咋们先看看domain的结构(一个中断控制器就可以看成是一个domain):

structirq_domain{structlist_headlink;constchar*name;//domain的名字conststructirq_domain_ops*ops;//当前domain的处理函数void*host_data;//存放私有数据unsignedintflags;unsignedintmapcount;/*Optionaldata*/structfwnode_handle*fwnode;//和设备树有关系enumirq_domain_bus_tokenbus_token;//bus标记,用于匹配domainstructirq_domain_chip_generic*gc;#ifdefCONFIG_IRQ_DOMAIN_HIERARCHYstructirq_domain*parent;//树形结构的父节点domain#endifirq_hw_number_thwirq_max;//hwirq树形(radixtree)支持的最大节点数量unsignedintrevmap_direct_max_irq;//hwirq和virq1:1映射支持的最大数量unsignedintrevmap_size;//线性映射支持的最大数量structradix_tree_rootrevmap_tree;//radixtree的树根structmutexrevmap_tree_mutex;unsignedintlinear_revmap[];//线性映射的数组}

如上图可知,domain的结构里面基本都是关于hirq相关信息的,因此,我们可以推断domain就是用于管理hirq的(每个domain都有自己的一套hirq),而domain中的ops字段就是用于操作这些hirq的, 下面是domain的操作函数ops的结构:

structirq_domain_ops{int(*match)(structirq_domain*d,structdevice_node*node,enumirq_domain_bus_tokenbus_token);//用于匹配domain,优先级低于selecint(*select)(structirq_domain*d,structirq_fwspec*fwspec,enumirq_domain_bus_tokenbus_token);//用于匹配domain,优先级高于matchint(*map)(structirq_domain*d,unsignedintvirq,irq_hw_number_thw);//线性映射void(*unmap)(structirq_domain*d,unsignedintvirq);int(*xlate)(structirq_domain*d,structdevice_node*node,//通过参数,线性hirq获取constu32*intspec,unsignedintintsize,unsignedlong*out_hwirq,unsignedint*out_type);#ifdefCONFIG_IRQ_DOMAIN_HIERARCHY/*extendedV2interfacestosupporthierarchyirq_domains*/int(*alloc)(structirq_domain*d,unsignedintvirq,//树形节点申请,映射(只有树形才有这个)unsignedintnr_irqs,void*arg);void(*free)(structirq_domain*d,unsignedintvirq,unsignedintnr_irqs);int(*activate)(structirq_domain*d,structirq_data*irqd,boolreserve);//中断激活void(*deactivate)(structirq_domain*d,structirq_data*irq_data);int(*translate)(structirq_domain*d,structirq_fwspec*fwspec,//通过参数对树形hirq获取unsignedlong*out_hwirq,unsignedint*out_type);#endif}

如上图,对于domain的hirq,我们可以看到结构里面有查询分配hirq,以及hirq与virq的映射的函数。现在,我们已经了解了domain的数据结构,下面是一个中断控制器注册一个domain的流程:

irq_domain_add_hierarchyirq_domain_create_tree__irq_domain_addlist_add(&domain->link,&irq_domain_list)

可见最终所有的domain都添加到irq_domain_list链表当中的。但是我们知道,我们调用request_irq用的是virq号,而并不是hirq,因为hirq号不是唯一的,每个domain都可能会存在一个相同的hirq,因此Linux才增加的virq编号,这个编号是独立的且唯一。现在domain已经注册好了,但是hirq和virq直接的关系还没有建立起来,因此我们还不能用virq来注册中断,如下是把hirq和virq做一个映射函数调用:

irq_create_fwspec_mapping(传入domain和参数)irq_find_matching_fwspecdomain->ops->match//查看当前domain和参数指定的domain是否匹配irq_domain_translatedomain->ops->translate//通过参数申请一个hirq号irq_find_mapping//通过hirq去查询,这个hirq是否已经映射,如果已经映射,就返回virqirq_domain_alloc_irqs//hirq没有映射,通过该函数去申请一个virq并映射__irq_domain_alloc_irqsirq_domain_alloc_descs//申请一个irq_desc描述irq_domain_alloc_irq_data//为virq设置irq_data树,该virq会为它所在的domain的父domain,一直到rootdomain添加irq_datairq_domain_alloc_irqs_hierarchy//向domain申请hirq,并和virq做映射domain->ops->allocirq_domain_set_hwirq_and_chip//将hirq与virq的irq_data关联irq_domain_insert_irq//将virq信息插入到每一级irq_data里面(一个domain对应一个irq_data)

如上图,当驱动调用irq_create_fwspec_mapping函数的时候,该函数会通过参数在指定的domain里面的申请hirq,然后申请一个virq,并将virq和hirq关联起来。下图是,irq_data的结构图: Linux中断子系统domain的示例分析

如上图所示,在domain里面利用linear_revmap(线性)或者revmap_tree(radix tree)来保存irq_data结构,在接收到中断的时候,我们只知道hirq号和对应的domain,因此可以以hirq为key值从revmap_tree或者linear_revmap当中获取到irq_data数据结构;当我们调用request_irq的时候,只填入了virq,因此也可以通过virq做为key在irq_desc_tree或者irq_desc[]数组中获取到irq_desc描述,从而获取到这个描述的irq_data。irq_data包含irq_chip(中断控制屏蔽掩码,设置触发方式等函数)、virq(这个值和该irq_data的父节点parent_data(irq_data)的virq的值相同),hwirq(每个irq_data的hwirq是不同的,对应不同的domain内部编号)、domain(指向当前irq_data所在的domain)、chip_data(指向当前irq_data所在domain的私有数据)、parent_data(指向irq_data的父节点,这个父节点存放父domain相关信息)。

linear_revmap和revmap_tree是domain结构里面的变量,每个domain都有,用于保存hirq与irq_data的对应关系;irq_desc_tree和irq_desc[]是全局变量,用于保存irq_desc结构的链表或者树。

通常在ARMv8体系结构中,中断处理的流程,如下图:

Linux中断子系统domain的示例分析

如上图,我们这个处理流程是el1模式的,在el1模式的时候,汇编级别的共用中断函数为el1_irq。图中我们可以看见几个箭头指示的点,1、handle_arch_irq;2、desc->handle_irq;3、irqaction->handler。我们知道,通常ARM体系结构的CPU都和GIC中断控制器相连,详细《ARM中断控制器-GICv2》,因此,handle_arch_irq这个全局函数指针,是有通用的中断控制器GIC驱动设置的(GIC驱动调用set_handle_irq函数设置),这样CPU上的所有异常中断都经过GIC的回调函数处理。GIC的这个回调函数gic_handle_irq(就是handle_arch_irq),负责mask中断和eoi中断(eoi就是ack控制器)。在这个回调函数里面,我们可以通过寄存器值获取到当前控制的那个中断号(该domain的hirq)触发了中断,然后通过hirq可以获取到对应的virq,从而获取到irq_desc描述,最后调用irq_desc描述的handle_irq来处理该中断。由图中,我们还看到handle_irq这个函数其实也是注册的,但是为了方便统一,这个回调通常都是gic驱动为每个中断注册一个统一的回调函数(handle_fasteoi_irq);handle_fasteoi_irq这个函数支持中断共享,然后依次调用挂在irq_desc下面的irqaction结构中的handle函数(玩家每调用依次request_irq就会在对应的virq上增加一个irqaction)。

irq_desc是一个virq对应的描述结构体,每个virq对应一个该结构。在irq_desc结构体里面有irq_data和irqaction变量,一个用于存放与当前virq映射的hirq信息,另一个用于存放该virq的中断回调处理函数。nr_irqs全局变量保存当前支持最大的中断数(可以通过irq_expand_nr_irqs函数扩展)


 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Linux中断子系统domain的示例分析的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Linux中如何使用TestDisk恢复文件下一篇:

6 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18