Linux中断子系统之核间中断

Linux中断子系统之IPI核间中断

一、IPI概况

在SMP系统中,IPI用于传递核间事件「inter-core event」。为了使用硬件体系架构相关的特殊功能,要借助中断控制器提供的功能使IPI从一个核传递给另外其他核。因此,在中断控制器驱动初始化时将调用set_smp_cross_call()函数去设定触发IPI的回调函数。IPI不仅需要能够被一个核或多个核捕获,而且还需要能够被中断处理函数辨识出。在使用GIC中断控制器的系统中,小于等于15的硬中断号「hwirq」都属于软件生成中断「SGI,Software Generated Interrupt」。在Linux Kernel操作系统中,默认定义了8中IPI中断

enum ipi_msg_type {
    IPI_WAKEUP,             /* 唤醒其他CPU */
    IPI_TIMER,              /* 调用tick时钟设备「tick-clock device」的事件处理程序 */
    IPI_RESCHEDULE,         /* 唤醒挂起的任务 */
    IPI_CALL_FUNC,          /* 让其他CPU执行特定的函数 */
    IPI_CPU_STOP,           /* 暂停其他CPU */
    IPI_IRQ_WORK,           /* 执行当前CPU的irq_work */
    IPI_COMPLETION,
    NR_IPI,
    IPI_CPU_BACKTRACE = NR_IPI,
    MAX_IPI
};

二、IPI处理函数的注册及调用

当ARM core收到IRQ后,会触发cpu的irq异常,会跳转到linux kernel的irq异常向量表,在该向量表中,会调用gicv3的gic_handle_irq函数。在Linux Kernel5.0之前,在gic_handle_irq()的gic处理函数中,会判断硬件中断号,中断小于16的属于SGI中断,单独走handle_IPI()函数,用于处理核间中断。5.0之后不在区分,统一在handle_domain_irq()中处理(参见中断机制)。

在Linux Kernel 5.0为硬件中断号0-7的每个中断号都注册了一个中断处理函数,其指向ipi_handler()函数。

void __init set_smp_ipi_range(int ipi_base, int n)
{
    int i;

    WARN_ON(n < MAX_IPI);
    nr_ipi = min(n, MAX_IPI);

    for (i = 0; i < nr_ipi; i++) {
        int err;

        err = request_percpu_irq(ipi_base + i, ipi_handler,
                     "IPI", &irq_stat);
        WARN_ON(err);

        ipi_desc[i] = irq_to_desc(ipi_base + i);
        irq_set_status_flags(ipi_base + i, IRQ_HIDDEN);
    }

    ipi_irq_base = ipi_base;

    /* Setup the boot CPU immediately */
    ipi_setup(smp_processor_id());
}

在触发中断异常后,它和一般的request_irq注册的中断没有什么不同,都是从异常向量表调过来,然后调用到这个ipi_handler()程序,该程序再调用do_handle_IPI()真正处理核间通信的任务。

static void do_handle_IPI(int ipinr)
{
    unsigned int cpu = smp_processor_id();

    if ((unsigned)ipinr < NR_IPI)
        trace_ipi_entry_rcuidle(ipi_types[ipinr]);

    switch (ipinr) {
    case IPI_RESCHEDULE:
        scheduler_ipi();
        break;

    case IPI_CALL_FUNC:
        generic_smp_call_function_interrupt();
        break;

    case IPI_CPU_STOP:
        local_cpu_stop();
        break;

    case IPI_CPU_CRASH_STOP:
        if (IS_ENABLED(CONFIG_KEXEC_CORE)) {
            ipi_cpu_crash_stop(cpu, get_irq_regs());

            unreachable();
        }
        break;

#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
    case IPI_TIMER:
        tick_receive_broadcast();
        break;
#endif

#ifdef CONFIG_IRQ_WORK
    case IPI_IRQ_WORK:
        irq_work_run();
        break;
#endif

#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
    case IPI_WAKEUP:
        WARN_ONCE(!acpi_parking_protocol_valid(cpu),
              "CPU%u: Wake-up IPI outside the ACPI parking protocol\n",
              cpu);
        break;
#endif

    default:
        pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
        break;
    }

    if ((unsigned)ipinr < NR_IPI)
        trace_ipi_exit_rcuidle(ipi_types[ipinr]);
}

三、触发核间中断

核间中断的相关的API:

1)smp_call_function(func, info, wait) //在所有其它处理器执行一个函数;

2)smp_call_function_single(int cpuid, smp_call_func_t func, void *info,int wait) //在指定处理器执行一个函数;

3)smp_send_reschedule(int cpu) //指定处理器重新调度进程。

SMP(IPI)的这类函数,最终都是调用到gic_send_sgi(),给core发送要给SGI中断

static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
{
	u64 val;

	val = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3)	|
	       MPIDR_TO_SGI_AFFINITY(cluster_id, 2)	|
	       irq << ICC_SGI1R_SGI_ID_SHIFT		|
	       MPIDR_TO_SGI_AFFINITY(cluster_id, 1)	|
	       MPIDR_TO_SGI_RS(cluster_id)		|
	       tlist << ICC_SGI1R_TARGET_LIST_SHIFT);

	pr_devel("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
	gic_write_sgi1r(val);
}

static inline void gic_write_sgi1r(u64 val)
{
	write_sysreg_s(val, SYS_ICC_SGI1R_EL1);  //寄存器
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值