在飞腾CPU平台上,SMP机间中断主要用来在指定CPU上实现七个目标:
IPI序号 | 目标 |
IPI_RESCHEDULE | 远程cpu执行任务调度 当被唤醒进程不是运行本地cpu上,就需要给被唤醒进程所在的远程cpu发送IPI中断,让远程cpu重新调度。在接收到IPI时无论远程cpu是否处于空闲态,被唤醒进程都会在IPI中断过渡到内核态时得到执行。 |
IPI_CALL_FUNC | 远程cpu执行回调函数 当本地cpu将指定远程cpu调用的回调函数挂到远程cpu的队列上,然后给远程cpu发送IPI_CALL_FUNC机间中断; 远程cpu就会将执行队列上的所有回调函数,整个过程必须保持中断屏蔽状态。 |
IPI_CPU_STOP | 远程CPU停止运行 先将指定的远程cpu从在线CPU位图中删除,然后将屏蔽D,A,I,F中断屏蔽,最后处理器进入低功耗循环。 |
IPI_CPU_CRASH_STOP | 可选配置KEXEC_CORE 执行崩溃转储 |
IPI_TIMER | 可选配置GENERIC_CLOCKEVENTS_BROADCAST 定时器广播 |
IPI_IRQ_WORK | 可选配置IRQ_WORK 远程cpu在中断上下文中运行回调函数 |
IPI_WAKEUP | 可选配置ARM64_ACPI_PARKING_PROTOCOL 唤醒CPU |
1. 远程cpu执行任务调度
本地cpu端的Linux核心层通过函数smp_send_reschedule,向指定cpu发送“执行任务调度”需求。
void smp_send_reschedule(int cpu) { smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); }
远程cpu端相应的中断处理
static __always_inline void scheduler_ipi(void) { /* * Fold TIF_NEED_RESCHED into the preempt_count; anybody setting * TIF_NEED_RESCHED remotely (for the first time) will also send * this IPI. */ preempt_fold_need_resched(); }
2. 远程cpu执行回调函数
本地cpu端的Linux核心层通过调用函数arch_send_call_function_xxx,向指定cpu发送“执行回调函数”需求。
void arch_send_call_function_ipi_mask(const struct cpumask *mask) { smp_cross_call(mask, IPI_CALL_FUNC); } void arch_send_call_function_single_ipi(int cpu) { smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC); }
远程cpu端相应的中断处理
void generic_smp_call_function_single_interrupt(void) { cfd_seq_store(this_cpu_ptr(&cfd_seq_local)->gotipi, CFD_SEQ_NOCPU, smp_processor_id(), CFD_SEQ_GOTIPI); flush_smp_call_function_queue(true); }
3.远程CPU停止运行
本地cpu端发起machine_halt、machine_power_off、machine_restart等操作时,需要向其他所有cpu发送“停止运行”需求。
void smp_send_stop(void)
{ unsigned long timeout; if (num_other_online_cpus()) { cpumask_t mask; cpumask_copy(&mask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &mask); if (system_state <= SYSTEM_RUNNING) pr_crit("SMP: stopping secondary CPUs\n"); smp_cross_call(&mask, IPI_CPU_STOP); } /* Wait up to one second for other CPUs to stop */ timeout = USEC_PER_SEC; while (num_other_online_cpus() && timeout--) udelay(1); if (num_other_online_cpus()) pr_warn("SMP: failed to stop secondary CPUs %*pbl\n", cpumask_pr_args(cpu_online_mask)); sdei_mask_local_cpu(); }
远程cpu端相应的中断处理
static void local_cpu_stop(void) { set_cpu_online(smp_processor_id(), false); local_daif_mask(); sdei_mask_local_cpu(); cpu_park_loop(); }