处理器间中断
处理器间中断(Inter-Processor Interrupt)是一种特殊的中断,在多处理器系统中,一 个处理器可以向其他处理器发送中断。要求目标处理器执行某件事情。处理器间中断是多核系统中用于处理核间通信的一种重要机制。
IPI发送函数
smp_call_function()
- 功能:在所有其他 CPU 上执行指定的函数。
- 函数实现:
/**
* smp_call_function(): Run a function on all other CPUs.
* @func: The function to run. This must be fast and non-blocking.
* @info: An arbitrary pointer to pass to the function.
* @wait: If true, wait (atomically) until function has completed
* on other CPUs.
*
* Returns 0.
*
* If @wait is true, then returns once @func has returned; otherwise
* it returns just before the target cpu calls @func.
*
* You must not call this function with disabled interrupts or from a
* hardware interrupt handler or from a bottom half handler.
*/
int smp_call_function(smp_call_func_t func, void *info, int wait)
{
preempt_disable();
smp_call_function_many(cpu_online_mask, func, info, wait);
preempt_enable();
return 0;
}
- 参数:
func:需要在其他 CPU 上执行的函数。info:传递给func的参数。wait:是否等待目标 CPU 执行完成。
smp_call_function_single()
- 功能:在指定的单个 CPU 上执行某个函数。
- 函数实现:
/*
* smp_call_function_single - Run a function on a specific CPU
* @func: The function to run. This must be fast and non-blocking.
* @info: An arbitrary pointer to pass to the function.
* @wait: If true, wait until function has completed on other CPUs.
*
* Returns 0 on success, else a negative status code.
*/
int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
int wait)
{
struct call_single_data *csd;
struct call_single_data csd_stack = { .flags = CSD_FLAG_LOCK | CSD_FLAG_SYNCHRONOUS };
int this_cpu;
int err;
/*
* prevent preemption and reschedule on another processor,
* as well as CPU removal
*/
this_cpu = get_cpu();
/*
* Can deadlock when called with interrupts disabled.
* We allow cpu's that are not yet online though, as no one else can
* send smp call function interrupt to this cpu and as such deadlocks
* can't happen.
*/
WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled()
&& !oops_in_progress);
csd = &csd_stack;
if (!wait) {
csd = this_cpu_ptr(&csd_data);
csd_lock(csd);
}
err = generic_exec_single(cpu, csd, func, info);
if (wait)
csd_lock_wait(csd);
put_cpu();
return err;
}
- 参数:
cpu:目标 CPU。- 其他参数与
smp_call_function相同。
smp_send_reschedule()
- 功能:向指定的 CPU 发送调度 IPI,要求指定的CPU重新调度进程。
- 函数实现
void smp_send_reschedule(int cpu)
{
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
}
- 参数:
cpu:目标 CPU。
IPI处理函数
对于ARM64架构的GIC控制器,把处理器间中断称为软件生成的中断,可以写分发器的寄存器以生成处理器间中断。(GICD_SGIR 软件生成中断寄存器 Software Generated Interrupt Register)
之前的《中断处理流程》中,已经介绍过GIC v2的中断处理流程,处理流程如下图。硬件中断号小于16,即为软件生成的中断(IPI),处理函数为handle_IPI()。

函数实现如下:
/*
* Main handler for inter-processor interrupts
*/
void handle_IPI(int ipinr, struct pt_regs *regs)
{
unsigned int cpu = smp_processor_id();
struct pt_regs *old_regs = set_irq_regs(regs);
if ((unsigned)ipinr < NR_IPI) {
trace_ipi_entry_rcuidle(ipi_types[ipinr]);
__inc_irq_stat(cpu, ipi_irqs[ipinr]);
}
switch (ipinr) {
case IPI_RESCHEDULE:
scheduler_ipi();
break;
case IPI_CALL_FUNC:
irq_enter();
generic_smp_call_function_interrupt();
irq_exit();
break;
case IPI_CPU_STOP:
irq_enter();
ipi_cpu_stop(cpu);
irq_exit();
break;
case IPI_CPU_CRASH_STOP:
if (IS_ENABLED(CONFIG_KEXEC_CORE)) {
irq_enter();
ipi_cpu_crash_stop(cpu, regs);
unreachable();
}
break;
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
case IPI_TIMER:
irq_enter();
tick_receive_broadcast();
irq_exit();
break;
#endif
#ifdef CONFIG_IRQ_WORK
case IPI_IRQ_WORK:
irq_enter();
irq_work_run();
irq_exit();
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]);
set_irq_regs(old_regs);
}
目前支持7种处理器间中断。
(1)IPI_RESCHEDULE:硬件中断号是0,重新调度进程,函数smp_send_reschedule()生成的中断。
(2)IPI_CALL_FUNC:硬件中断号是1,执行函数,函数smp_call_function()生成的中断。
(3)IPI_CPU_STOP:硬件中断号是2,使处理器停止,函数smp_send_stop()生成的中断。
(4)IPI_CPU_CRASH_STOP:硬件中断号是3,使处理器停止,函数smp_send_crash_stop()生成的中断。
(5)IPI_TIMER:硬件中断号是4,广播的时钟事件,函数tick_broadcast()生成的中断。
(6)IPI_IRQ_WORK:硬件中断号是5,在硬中断上下文中执行回调函数,函数irq_work_queue()生成的中断。
(7)IPI_WAKEUP:硬件中断号是6,唤醒处理器,函数acpi_parkinig_protocol_cpu_boot()生成的中断。
实机上查询中断如下:
# cat /proc/interrupts
...
IPI0: 209613 203627 202885 202945 202942 210851 203161 204303 Rescheduling interrupts
IPI1: 4640853 4131763 4069513 4030339 3948570 3934138 3916842 3924195 Function call interrupts
IPI2: 0 0 0 0 0 0 0 0 CPU stop interrupts
IPI3: 0 0 0 0 0 0 0 0 CPU stop (for crash dump) interrupts
IPI4: 0 0 0 0 0 0 0 0 Timer broadcast interrupts
IPI5: 29 16 20 13 21 13 13 14 IRQ work interrupts
IPI6: 0 0 0 0 0 0 0 0 CPU wake-up interrupts
参考资料
- Professional Linux Kernel Architecture,Wolfgang Mauerer
- Linux内核深度解析,余华兵
- Linux设备驱动开发详解,宋宝华

1005

被折叠的 条评论
为什么被折叠?



