3.4.1.2 IPIPE对Linux中断号的改造

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
3.4.1.2 IPIPE对Linux中断号的改造

        在IPIPE domain中,IPIPE_NR_IRQS代表中断总数量,在代码中经常用到,最具代表的就是下图中定义struct ipipe_irqdesc irqs[IPIPE_NR_IRQS].

        先列一下宏定义的位置,然后分析一下。

arch/arm64/include/asm/ipipe_base.h:
#define IPIPE_NR_ROOT_IRQS	1024
#define IPIPE_NR_XIRQS		IPIPE_NR_ROOT_IRQS

include/linux/ipipe_domain.h:
#define __bpl_up(x)		(((x)+(BITS_PER_LONG-1)) & ~(BITS_PER_LONG-1))
/* Number of virtual IRQs (must be a multiple of BITS_PER_LONG) */
#define IPIPE_NR_VIRQS		BITS_PER_LONG
/* First virtual IRQ # (must be aligned on BITS_PER_LONG) */
#define IPIPE_VIRQ_BASE		__bpl_up(IPIPE_NR_XIRQS)
/* Total number of IRQ slots */
#define IPIPE_NR_IRQS		(IPIPE_VIRQ_BASE+IPIPE_NR_VIRQS)

        IPIPE_NR_IRQS由两部分构成,分别分析一下。

        第一部分IPIPE_VIRQ_BASE,等同于__bpl_up(IPIPE_NR_XIRQS),即1024。

__bpl_up(x)宏定义的作用,是使得输入的x和BITS_PER_LONG向上对齐,bpl是bits per long三个单词的缩写。假设BITS_PER_LONG是64,那么x返回的值总是和64向上对齐,即总是64的整倍数。用下面的shell可以验证,其中~(64-1) = 0xFFC0。

        因为IPIPE_NR_XIRQS(不知道这个X是代指什么)是1024,与64对齐,所以IPIPE_VIRQ_BASE也是1024,等同于IPIPE_NR_XIRQS。

        IPIPE_VIRQ_BASE代表第一个virtual IRQ的编号,这就引出了第二部分IPIPE_NR_VIRQS。

        第二部分IPIPE_NR_VIRQS,等同于BITS_PER_LONG,即64。

        这里的VIRQ和前面讨论的虚拟中断标志位完全不是一个概念哦,千万不能混淆。VIRQ的前10个,分别为7个NR_IPI和3个IPIPE_OOB_IPI_NR,接下来分别展开说一下。

arch/arm64/include/asm/hardirq.h:
#define NR_IPI	7

arch/arm64/include/asm/ipipe_base.h:
/*
 * Out-of-band IPIs are directly mapped to SGI1-3, instead of
 * multiplexed over SGI0 like regular in-band messages.
 */
#define IPIPE_IPI_BASE         IPIPE_VIRQ_BASE
#define IPIPE_OOB_IPI_NR       3
#define IPIPE_CRITICAL_IPI     (IPIPE_IPI_BASE + NR_IPI)
#define IPIPE_HRTIMER_IPI      (IPIPE_IPI_BASE + NR_IPI + 1)
#define IPIPE_RESCHEDULE_IPI   (IPIPE_IPI_BASE + NR_IPI + 2)

        前7个NR_IPI,是Linux原始定义的7个IPI中断,全称是Inter-Processor Interrupt,用于CPU core之间的通信。Linux内核可以通过smp_cross_call->__smp_cross_call-> gic_raise_softirq,从当前CPU core向其它CPU core来触发IPI中断。smp_cross_call调用函数指针__smp_cross_call,而__smp_cross_call由GIC V3中断控制器驱动(irq-gic-v3.c)调用gic_smp_init-> set_smp_cross_call(gic_raise_softirq)初始化为gic_raise_softirq。所以,CPU core之间的IPI中断,是依赖GIC V3中断控制器的,本质是调用gic_raise_softirq。注意这里面的softirq,其实就是GIC V3里面定义的SGI中断,即Software Generated Interrupt.

arch/arm64/kernel/smp.c:

//Linux原始定义的7个IPI中断
enum ipi_msg_type {
	IPI_RESCHEDULE,
	IPI_CALL_FUNC,
	IPI_CPU_STOP,
	IPI_CPU_CRASH_STOP,
	IPI_TIMER,
	IPI_IRQ_WORK,
	IPI_WAKEUP
};

void (*__smp_cross_call)(const struct cpumask *, unsigned int);

void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
{
	__smp_cross_call = fn;
}

static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
{
	trace_ipi_raise(target, ipi_types[ipinr]);
	__smp_cross_call(target, ipinr);
}

drivers/irqchip/irq-gic-v3.c:
static void gic_smp_init(void)
{
	set_smp_cross_call(gic_raise_softirq);
	cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
				  "irqchip/arm/gicv3:starting",
				  gic_starting_cpu, NULL);
}

       后面3个IPIPE_OOB_IPI_NR,就算是IPIPE自己额外新增的3个IPI中断了。实时内核可以通过ipipe_send_ipi->smp_cross_call->__smp_cross_call->gic_raise_softirq来触发IPI中断。这3个IPIPE_OOB_IPI_NR各自又封装了接口:

ipipe/core.c: 
// IPIPE_CRITICAL_IPI
unsigned long ipipe_critical_enter(void (*syncfn)(void))

include/xenomai/pipeline/pipeline.h:
//IPIPE_RESCHEDULE_IPI
static inline void pipeline_send_resched_ipi(const struct cpumask *dest)

// IPIPE_HRTIMER_IPI
static inline void pipeline_send_timer_ipi(const struct cpumask *dest)

       总结起来,IPIPE_NR_IRQS两部分之和:IPIPE_NR_XIRQS(1024)和IPIPE_NR_VIRQS(64)。它们只是干巴巴的数字,实际的意义是什么呢?结合GIC V3来看一下!

        参考《GICv3_Software_Overview_Official_Release_B》,下表描述了GIC V3支持的INTID(硬件中断号)的范围。

SGI (Software Generated Interrupt):软件触发的中断。Linux内核可以通过写GICD_SGIR寄存器来触发一个中断事件,用于CPU core之间的通信。

PPI (Private Peripheral Interrupt):私有外设中断。这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟。

SPI (Shared Peripheral Interrupt):软件触发的中断。软件可以通过写GICD_SGIR寄存器来触发一个中断事件,一般用于核间通信。

LPI (Locality-specific Peripheral Interrupt):LPI是GICv3中的新特性,是基于消息的中断。当前GIC V3驱动irq-gic-v3.c的参数gicv3_nolpi默认为0,所以默认是默认支持LPI的。

       GIC V3的PPI+SPI+LPI是硬件中断号,在Linux中一般用int hwirq来表示,下面都用hwirq来代指硬件中断号。为了方便管理中断号,在Linux中还定义了逻辑中断号,一般用int virq来表示,每个virq都对应一个struct irq_desc数据结构,下面都用virq来代指逻辑中断号。逻辑中断号virq和硬件中断号hwirq的映射关系,不是简单的相等关系。从CPU硬件的角度来说,hwirq是固定分配好的,但是逻辑中断号virq是通过位图变量allocated_irqs按照先申请先得的规则分配的。二者的映射关系是在内核启动过程中建立,函数调用关系如下,irq_create_fwspec_mapping返回的就是virq,在这里就不具体展开了,还是回到主题来梳理一下逻辑中断号virq的范围。

       如刚刚提到的,逻辑中断号virq是通过位图变量allocated_irqs按照先申请先得的规则分配的,所以virq的范围就要看位图变量allocated_irqs到底是多少bit位。

kernel/irq/irqdesc.c:

static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);

//分配virq时,首先找到空闲的bit位
__irq_alloc_descs-> bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,    from, cnt, 0); 

       IRQ_BITMAP_BITS具体是多大呢?64或者64+8196,取决于CONFIG_SPARSE_IRQ。

include/asm-generic/irq.h:
/*
 * NR_IRQS is the upper bound of how many interrupts can be handled
 * in the platform. It is used to size the static irq_map array,
 * so don't make it too big.
 */
#ifndef NR_IRQS
#define NR_IRQS 64
#endif

kernel/irq/internals.h:

#ifdef CONFIG_SPARSE_IRQ
# define IRQ_BITMAP_BITS	(NR_IRQS + 8196)
#else
# define IRQ_BITMAP_BITS	NR_IRQS
#endif

        CONFIG_SPARSE_IRQ代表如何选择struct irq_desc的管理方式。对于指定的virq,如何快速的找到对应的struct irq_desc结构体?如果CONFIG_SPARSE_IRQ=y,就用radix tree,否则就要静态数组。当前默认是CONFIG_SPARSE_IRQ=y。

kernel/irq/irqdesc.c:

#ifdef CONFIG_SPARSE_IRQ
……
static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
……
#else /* !CONFIG_SPARSE_IRQ */

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
	[0 ... NR_IRQS-1] = {
		.handle_irq	= handle_bad_irq,
		.depth		= 1,
		.lock		= __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
	}
};

……
#endif

截止目前,我们分析出了3组和中断相关数字:

IPIPE_NR_IRQS = IPIPE_NR_XIRQS + IPIPE_NR_VIRQS

GIC V3 INTID = SGI + hwirq(PPI + SPI + LPI)

Linux virq的数量 = IRQ_BITMAP_BITS

它们的关系到底是什么呢?接下来用一副图来揭秘!

        IPIPE_NR_XIRQS的值默认是1024,如果hwirq的总数超过1024,映射过程就会出错。

        IPIPE_NR_VIRQS对应ARM64 GIC V3中断处理器的SGI中断。Linux原始定义的7个IPI中断,会占用7个SGI中断号:SGI0~SGI6。但是IPIPE来了之后,会强制Linux 7个IPI中断共享SGI0。具体是咋做到的,后面会结合代码再分析。

 

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值