处理器间中断

处理器间中断

处理器间中断(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

参考资料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux内核深度解析,余华兵
  3. Linux设备驱动开发详解,宋宝华
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值