• 正文
    • 1. IPI中断介绍
    • 2. linux里的IPI中断类型
    • 3. IPI中断源码
  • 相关推荐
申请入驻 产业图谱

ARM GIC中断详解 –– IPI中断

03/24 14:31
323
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

1. IPI中断介绍

IPI中断,即处理器核间中断(Inter-Processor Interrupt),其实就是ARM GIC架构里定义的SGI中断(Software Generated Interrupt)。在GICv3架构中,共有16个SGI中断(不包括extension的),中断号是从0到15,如下图。

核间中断就是一个CPU核(PE)向系统中的目标CPU核(target PE)发送中断信号,以使目标CPU执行特定的操作。IPI中断的基本流程为: PE触发 -> GICR -> GICD -> target GICR -> target CPU interface -> target PE。

IPI中断的触发:PE通过写系统寄存器ICC_SGI0R_EL1、ICC_SGI1R_EL1或ICC_ASGI1R_EL1来触发。

IPI中断有两种routing模式(通过ICC_SGI0R_EL1.IRM/ ICC_SGI1R_EL1.IRM设置):

IRM bit为1,SGI会广播给系统中所有PE(除了产生SGI的PE)。

IRM bit为0,SGI会发给指定的一些PE,通过Aff3.Aff2.Aff1.指定。(可以包括产生SGI的PE)。

ICC_SGI0R_EL1/ICC_SGI1R_EL1寄存器

2. linux里的IPI中断类型

Linux kernel代码中,默认定义了8种IPI中断(SGI0 - SGI7),具体如下(linux/arch/arm64/kernel/smp.c):

enum ipi_msg_type { IPI_RESCHEDULE, IPI_CALL_FUNC, IPI_CPU_STOP, IPI_CPU_CRASH_STOP, IPI_TIMER, IPI_IRQ_WORK, IPI_WAKEUP, NR_IPI};

IPI_RESCHEDULE

0号中断,重新调度进程scheduler_ipi()。

一般是先为进程设置TIF_NEED_RESCHED标志(表示需要调度该进程),如果该进程没有在当前CPU上,会通过smp_send_reschedule接口触发IPI_RESCHEDULE中断给目标CPU,目标CPU最终进入hadle_IPI的scheduler_ipi中。

IPI_CALL_FUNC

1号中断,调用generic_smp_call_function_interrupt(),远程cpu执行回调函数。

如果想在某个cpu(不是本cpu)上调用一个函数时,会触发IPI_CALL_FUNC中断,常用smp_call_function接口触发target cpu的IPI_CALL_FUNC软中断。

IPI_CPU_STOP

2号中断,调用local_cpu_stop()函数使当前CPU停止工作,进入wfi/wfe的低功耗状态。

当某个cpu想让其他cpu停下来时会发送此IPI,主要接口是machine_halt, machine_power_off, machine_restart。这些接口都会调用smp_send_stop来触发IPI_CPU_STOP中断,target cpu收到中断后会调用local_cpu_stop()函数,如下:

  static void local_cpu_stop(void)                                                        {                                                                                                             set_cpu_online(smp_processor_id(), false);/*把当前cpu offline*/                                                                                                                local_daif_mask(); /*设置pstate的DAIF状态位为1,关闭本cpu的系统调试(D),系统错误SError(A),IRQ中断(I),FIQ中断(F)*/          sdei_mask_local_cpu();           cpu_park_loop(); /*通过wfe和wfi指令,让当前cpu进入低功耗standby状态*/                     }

IPI_CPU_CRASH_STOP

3号中断,调用ipi_cpu_crash_stop(),使处理器停止。

在系统crash时,发生crash的cpu给其他cpu发送该中断。在使能了KEXEC的系统中,做出以下响应,保存寄存器信息并传递给第二内核。KEXEC常用于系统crash时在不重启的情况下快速进入第二内核。第二内核的目的是把当前ddr内存镜像保存下来,方便之后通过crash tool分析系统crash问题。

IPI_TIMER :

4号中断,调用tick_receive_broadcast(), 广播时钟事件。

当某个cpu调用tick_broadcast(const struct cpumask *mask)时,即调用IPI_TIMER中断给相应cpu(通过mask指定cpu)发送timer的广播中断,target cpu执行tick_receive_broadcast()函数进行响应。

IPI_IRQ_WORK :

5号中断,调用irq_work_run(),在中断上下文中执行回调函数。

IPI_WAKEUP :

6号中断,调用acpi_parking_protocol_valid(cpu), 唤醒CPU。当CPU核收到该IPI中断,即从parked的状态(wfi/wfe的低功耗状态)唤醒过来。

NR_IPI :

7号中断,没有使用。

3. IPI中断源码

负责处理IPI中断的函数主要是handle_IPI,源代码如下:

<arch/arm64/kernel/smp.c>                                                                                                 void handle_IPI(int ipinr, struct pt_regs *regs)                                                      {                                                                                                             unsigned int cpu = smp_processor_id(); /*获得当前cpu id*/           struct pt_regs *old_regs = set_irq_regs(regs); /*pt_regs结构体包含当前的寄存器信息*/                                                                                                                                                   if ((unsigned)ipinr < NR_IPI) {  /*当前有效的IPI中断为7个*/                   trace_ipi_entry_rcuidle(ipi_types[ipinr]); /* ftrace记录进入ipi中断,用于debug */                  __inc_irq_stat(cpu, ipi_irqs[ipinr]); /* 统计各cpu不同类型的ipi中断数量*/                 }                                                                                                                                                                                                           switch (ipinr) {                                                                                      case IPI_RESCHEDULE:                                                                                          scheduler_ipi();  /* 触发重调度 */                  break;                                                                                                                                                                                              case IPI_CALL_FUNC:                                                                                           irq_enter();                                                                                          generic_smp_call_function_interrupt(); /*执行本cpu所有function回调 */                     irq_exit();                                                                                           break;                                                                                                                                                                                              case IPI_CPU_STOP:                                                                                            irq_enter();                                                                                          local_cpu_stop();  /*将本cpu停下来,进入低功耗状态*/                                  irq_exit();                                                                                           break;                                                                                                                                                                                              case IPI_CPU_CRASH_STOP:                                                                                      if (IS_ENABLED(CONFIG_KEXEC_CORE)) { /*如果配置了KEXEC,在系统panic时会进入第二内核*/                                                                      irq_enter();                                                                                          ipi_cpu_crash_stop(cpu, regs);                                                                                                                                                                              unreachable();                           }                                                                                                     break;                                                                                                                                                                                      #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST                                                                   case IPI_TIMER:                                                                                               irq_enter();                                                                                          tick_receive_broadcast();  /* 接收timer广播,执行timer的中断回调 */                      irq_exit();                                                                                           break;                                                                                #endif                                                                                                                                                                                                      #ifdef CONFIG_IRQ_WORK                                                                                        case IPI_IRQ_WORK:                                                                                            irq_enter();                                                                                          irq_work_run(); /*本cpu执行irq_work */                                          irq_exit();                                                                                           break;                                                                                #endif                                                                                                                                                                                                      #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL                                                                     case IPI_WAKEUP:        /* 从低功耗状态中唤醒本cpu */                  WARN_ONCE(!acpi_parking_protocol_valid(cpu),                                                                    "CPU%u: Wake-up IPI outside the ACPI parking protocoln",                                             cpu);                                                                                       break;                                                                                #endif                                                                                                                                                                                                              default:                                                                                                      pr_crit("CPU%u: Unknown IPI message 0x%xn", cpu, ipinr);                                             break;                                                                                        }                                                                                                                                                                                                           if ((unsigned)ipinr < NR_IPI)                                                                                 trace_ipi_exit_rcuidle(ipi_types[ipinr]);                                                     set_irq_regs(old_regs);                                                                       }
点赞
收藏
评论
分享
加入交流群
举报

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录