3.4.1.3 IPIPE interrupt log数据结构

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

3.4.1.3 IPIPE interrupt log数据结构

       IPIPE interrupt log的概念,来自于IPIPE中断分发的核心函数__ipipe_dispatch_irq(kernel/ipipe/core.c)里面的注释。这个函数的具体流程会在后面的章节讨论,这里只讨论interrupt log的数据结构是怎么设计的。

       第一步,先把中断记录到interrupt log。看一下这个截取自__ipipe_dispatch_irq 函数的图片。

       把中断记录到interrupt log,核心函数就是__ipipe_set_irq_pending。从字面很好理解,暂时不处理中断,可不就是让中断处于pending状态嘛。从上图还能看出来,在特殊情况下(IPIPE_IRQF_NOSYNC),head域也不立即处理中断,会记录到interrupt log。下面看一下__ipipe_set_irq_pending的真面目!

       这个函数的定义,与include/linux/ipipe_domain.h中的两个宏定义IPIPE_IRQ_MAPSZ/ __IPIPE_IRQMAP_LEVELS相关。根据上一章的分析,IPIPE_NR_IRQS等于1088,所以IPIPE_IRQ_MAPSZ是17,__IPIPE_IRQMAP_LEVELS是2.

       在kernel/ipipe/core.c,找到__ipipe_set_irq_pending的定义:

#if __IPIPE_IRQMAP_LEVELS == 4		
……		
#elif __IPIPE_IRQMAP_LEVELS == 3		
……		
#else /* __IPIPE_IRQMAP_LEVELS == 2 */		
/* Must be called hw IRQs off. */		
void __ipipe_set_irq_pending(struct ipipe_domain *ipd, unsigned int irq)		
{		
	struct ipipe_percpu_domain_data *p = ipipe_this_cpu_context(ipd);	
	int l0b = irq / BITS_PER_LONG;	
		
	IPIPE_WARN_ONCE(!hard_irqs_disabled());	
		
	if (likely(!test_bit(IPIPE_LOCK_FLAG, &ipd->irqs[irq].control))) {	
		__set_bit(irq, p->irqpend_map);
		__set_bit(l0b, &p->irqpend_0map);
	} else	
		__set_bit(irq, p->irqheld_map);
		
	p->irqall[irq]++;	
}		
EXPORT_SYMBOL_GPL(__ipipe_set_irq_pending);		
#endif		

第5行,根据上面的分析,只关注__IPIPE_IRQMAP_LEVELS等于2的情况。

第9行,根据传入的struct ipipe_domain *ipd,找到struct ipipe_percpu_domain_data *p。对于支持SMP的多core CPU,当中断发生时,GIC V3一定是把中断送给某个CPU core来处理。所以,interrupt log也是每个CPU core自己单独记录的,它其实对应的就是ipipe_percpu_domain_data的成员irqpend_map。irqpend_map实际上就是一个数组,类型是unsigned long即每个数组元素提供64个bit位,数组长度是17即总共提供64*17=1088个bit位来标记中断是否pending。

struct ipipe_percpu_domain_data {
	unsigned long status;	/* <= Must be first in struct. */
	unsigned long irqpend_0map;
    ……
	unsigned long irqpend_map[IPIPE_IRQ_MAPSZ];
	unsigned long irqheld_map[IPIPE_IRQ_MAPSZ];
	unsigned long irqall[IPIPE_NR_IRQS];
	struct ipipe_domain *domain;
	int coflags;
};

 

第10行,数组irqpend_map相当于把1088个bit平分成17个区域(每区域64个bit),用中断号直接对64(BITS_PER_LONG)取整,得到的就是此中断所对应的区域编号。这里,把这个区域编号存入变量l0b,它的名字是level 0 bit的缩写。

第15行,利用linux的位图操作函数,直接把irq在数组irqpend_map中对应的bit置位为1,代表此中断pending。这里提出一个问题哈:置位的位置,属于完数组irqpend_map的哪个元素?答案其实就是第10行的l0b。

第16行,把第10行得出的区域编号l0b作为索引值,在变量irqpend_0map中置位对应的bit。所以,irqpend_0map的每个bit就代表每个区域中是否至少有1个bit处于pending状态。所以,只要irqpend_0map的值不等于0,就代表一定有中断处于pengding状态。

第18行,如果中断irq处于IPIPE_LOCK_FLAG状态,不会把中断记录到irqpend_map中,而是记录到irqheld_map中。只要root domain才会标记irq是否处于IPIPE_LOCK_FLAG状态,这里不做深入讨论,后面在分析中断流程的时候再展开。

第20行,对每个中断irq,记录一下在此CPU core总共发生了多少次。

        第二步,从interrupt log中查询出未处理的中断,进行处理,为方便描述简称interrupt log回放。核心函数就是__ipipe_next_irq,它是__ipipe_set_irq_pending的反向操作。从下图的函数调用关系可知,IPIPE总是通过调用__ipipe_sync_stage来完成interrupt log回放。

       按照__IPIPE_IRQMAP_LEVELS等于2,来分析一下__ipipe_next_irq。

#if __IPIPE_IRQMAP_LEVELS == 4						
……						
#elif __IPIPE_IRQMAP_LEVELS == 3						
……						
#else /* __IPIPE_IRQMAP_LEVELS == 2 */						
static inline int __ipipe_next_irq(struct ipipe_percpu_domain_data *p)						
{						
	unsigned long l0m, l1m;					
	int l0b, l1b;					
						
	l0m = p->irqpend_0map;					
	if (unlikely(l0m == 0))					
		return -1;				
						
	l0b = __ipipe_ffnz(l0m);					
	l1m = p->irqpend_map[l0b];					
	if (unlikely(l1m == 0))					
		return -1;				
						
	l1b = __ipipe_ffnz(l1m);					
	__clear_bit(l1b, &p->irqpend_map[l0b]);					
	if (p->irqpend_map[l0b] == 0)					
		__clear_bit(l0b, &p->irqpend_0map);				
						
	return l0b * BITS_PER_LONG + l1b;					
}						
#endif						

第11~13行,如之前对__ipipe_set_irq_pending的分析,只要l0m即irqpeng_0map的值为0,代表没有任何中断记录在数组中irqpend_map。

第15~16行,从l0m(level 0 map)即irqpend_0map中找到第一个非0的bit的编号l0b。这个编号l0b作为下标从数组irqpend_map取出一个元素l1m= irqpend_map[l0b],这个l1m就代表一个64bit长的区域,在此区域中一定存在着一个bit不为0.

第17~18行,如果判断成立,肯定是bug。

第20~21行,从l1m(level 1 map)即irqpend_map[l0b]中找到第一个不为0的bit的编号l1b,然后把这个bit位在irqpend_map[l0b]中清零。

第22~23行,如果irqpend_map[l0b]此时已经等于0,代表此64bit长的区域已经没有任何一个中断pend了,把irqpeng_0map中的l0b位清零。

第25行,l0b*64+l1b就是找到的penging的irq编号。

        分析起来挺绕的,其实从遍历数组irqpend_map[17]的每个元素,从中找到非0的bit。只不过irqpend_0map的bit位能够标记具体哪个数组元素里面存在非0的bit,所以不再需要用for循环一遍一遍的遍历数组。

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值