Linux中断 -- 中断应答、嵌套、

接上文,本文继续介绍Linux软件部分逻辑。

参考内核版本:kernel-4.19

目录

1.中断信号在各级中断控制器中的应答

2.supports_deactivate_key意义

3.中断嵌套


1.中断信号在各级中断控制器中的应答

        本章主要从内核软件层面来看各中断控制器对中断信号处理&应答(关于硬件层面的处理硬件过程可以参考前文)。同样使用前文中GICv3 & PIO 中断控制器为硬件框架,介绍PIO接受到外部中断时软件层各控制器的应答(文中设定supports_deactivate_key 为false)。

        如下图触发中断(EL1 、SPI类型)后软件通过操作硬件中断控制器对中断应答操作(卷尺框图):

        #1.读取GICv3中断控制器的IAR(中断应答寄存器)获取GIC的硬件中断号

        #2.通过PIO中断控制器mask&ack对应中断;

        #3.通过PIO中断控制器unmask对应中断;

        #4.操作GICv3中断控制器EOI寄存器,标记该GIC中断为deactivted并回退GIC的中优先级

从 #2、#3代码上逻辑上看一推出:

        a.因在执行该中断的处理handle时,PIO中断控制器上mask该中断故认为该中断在该CPU不               能实现中断嵌套;

        b.对于GIC已经通过IAR寄存器响应了来自PIO的中断,则处理该中断的CPU不能相应低于PIO            中断优先级的中断;

Linux中断 -- 中断应答、嵌套、_第1张图片

2.supports_deactivate_key意义

        supports_deactivate_key 是用指示GIC采用 ICC_CTLR_ELn.EOImode的形式(mode0或者mode1),值为true时表示采用mode1即EOI寄存器被置位时只是回退到该中断处理的前的优先级、需要再操作DIR寄存器将当前中断状态切换到非激活状态,值为false时表示采用mode0即操作EOI寄存器就会完成优先级回退和切换该中断到非激活状态。GICv3的驱动代码可以明确体现上述逻辑:

static void gic_cpu_sys_reg_init(void)
{
    …

    if (static_branch_likely(&supports_deactivate_key)) {

        /*当supports_deactivate_key使能时配置为EOI为mode1形式*/
        gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop);

    } else {

        /*当supports_deactivate_key使能时配置为EOI为mode0形式*/
        gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);

    }

    …
}

        supports_deactivate_key的值与hyp mode(Arm的虚拟化模式),默认值为ture当判定hyp mode未使能时则设置该置为false。代码逻辑如下:

static int __init gic_init_bases(void __iomem *dist_base,
 struct redist_region *rdist_regs,
 u32 nr_redist_regions,
 u64 redist_stride,
 struct fwnode_handle *handle)
{
    …

    /*hyp mode未使能,设置supports_deactivate_key为false*/
    if (!is_hyp_mode_available())
        static_branch_disable(&supports_deactivate_key);

    /*supports_deactivate_key值为true时,表示EOL & Deactivate 分开处理*/
    if (static_branch_likely(&supports_deactivate_key))
        pr_info("GIC: Using split EOI/Deactivate mode\n");

    …
}
3.中断嵌套

         据说是从kernel-2.6.34开始不支持。其实内核中有很多代码都可以证明不支持中断嵌套处理逻辑,如下便是一例作证:

static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
    /*
	*  request_irq() -> __setup_irq()
	*  如下当irq设置 IRQ_NESTED_THREAD 支持嵌套中断属性时,如果注册的中断处理无thread_fn
    *  函数时支持返回中断注册EINVAL错误.
	*  如果有注册thread_fn则将中断handle处理函数设置为irq_nested_primary_handler,
    *  因为irq_nested_primary_handler函数直接返回IRQ_NONE,则thread_fn也无法得到执行。
    *  故支持中断嵌套处理的函数不能得到处理,所以Linu内核不支持嵌套中断。
	*/

	/*
	 * Check whether the interrupt nests into another interrupt
	 * thread.
	 */
	nested = irq_settings_is_nested_thread(desc);
	if (nested) {
		if (!new->thread_fn) {
			ret = -EINVAL;
			goto out_mput;
		}
		/*
		 * Replace the primary handler which was provided from
		 * the driver for non nested interrupt handling by the
		 * dummy function which warns when called.
		 */
		new->handler = irq_nested_primary_handler;
    ...
}

        另外从处理器硬件层面也限制了中断嵌套处理的可能。如下ARM异常处理的文档中描述:当处理器捕获到异常并进入异常处理状态时,该处理器的PSTATE.DAIF中断掩码被自动设置,也是说该处理不能再响应对应的异常处理。当完成异常处理时,系统通过调用eret指令完成PSTATE状态和被中断指令地址的恢复,即同时恢复了PSTATE.DAIF状态,可能把当前处理器从异常中断状态恢复出来。

        在系统进行IRQ异常处理的过程中,可能有局部使能或者关闭(local_irq_enable/local_irq_disable)当前CPU IRQ的行为,如:bottom half处理的softirq& tasklet处理过程,会使能当前CPU的IRQ但也会进行成对的关闭行为(这点符合局部对称的设计要要求);但因为softirq阶段有短暂的使能本地中断行为,所以在中断的bottom half阶段可能有高优先级的中断被路由到当前CPU导致中断嵌套。

        总结:当前内核不支持top half阶段的中断的嵌套,支持bottom half阶段的中断嵌套。

你可能感兴趣的:(Linux,interrupt,linux,kernel,驱动开发,嵌入式硬件)