본문 바로가기

RISC-V

RISC-V IPI Call: Code walkthrough

RISC-V IPI Call: Code walkthrough

void smp_call_function_many(const struct cpumask *mask,
			    smp_call_func_t func, void *info, bool wait)
{
	smp_call_function_many_cond(mask, func, info, wait * SCF_WAIT, NULL);
}
EXPORT_SYMBOL(smp_call_function_many);

void send_call_function_single_ipi(int cpu)
{
	struct rq *rq = cpu_rq(cpu);

	if (!set_nr_if_polling(rq->idle))
		arch_send_call_function_single_ipi(cpu);
	else
		trace_sched_wake_idle_without_ipi(cpu);
}

arch/riscv/kernel/smp.c
void arch_send_call_function_single_ipi(int cpu)
{
	send_ipi_single(cpu, IPI_CALL_FUNC);
}

static void send_ipi_single(int cpu, enum ipi_message_type op)
{
	smp_mb__before_atomic();
	set_bit(op, &ipi_data[cpu].bits);
	smp_mb__after_atomic();

	if (ipi_ops && ipi_ops->ipi_inject)
		ipi_ops->ipi_inject(cpumask_of(cpu));
	else
		pr_warn("SMP: IPI inject method not available\n");
}

static const struct riscv_ipi_ops sbi_ipi_ops = {
	.ipi_inject = sbi_send_cpumask_ipi
};


static void sbi_send_cpumask_ipi(const struct cpumask *target)
{
	sbi_send_ipi(target);
}

int sbi_send_ipi(const struct cpumask *cpu_mask)
{
	return __sbi_send_ipi(cpu_mask);
}

arch/riscv/kernel/sbi.c
void __init sbi_init(void)
{
...
	if (!sbi_spec_is_0_1()) {
		pr_info("SBI implementation ID=0x%lx Version=0x%lx\n",
			sbi_get_firmware_id(), sbi_get_firmware_version());
		if (sbi_probe_extension(SBI_EXT_TIME)) {
			__sbi_set_timer = __sbi_set_timer_v02;
			pr_info("SBI TIME extension detected\n");
		} else {
			__sbi_set_timer = __sbi_set_timer_v01;
		}
		if (sbi_probe_extension(SBI_EXT_IPI)) {
			__sbi_send_ipi	= __sbi_send_ipi_v02;
			pr_info("SBI IPI extension detected\n");
		} else {
			__sbi_send_ipi	= __sbi_send_ipi_v01;
		}


arch/riscv/kernel/sbi.c
static int __sbi_send_ipi_v01(const struct cpumask *cpu_mask)
{
	pr_warn("IPI extension is not available in SBI v%lu.%lu\n",
		sbi_major_version(), sbi_minor_version());

	return 0;
}

static int __sbi_send_ipi_v02(const struct cpumask *cpu_mask)
{
...
	if (hmask) {
		ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI,
				hmask, hbase, 0, 0, 0, 0);
		if (ret.error)
			goto ecall_failed;
	}

	return 0;

ecall_failed:
	result = sbi_err_map_linux_errno(ret.error);
	pr_err("%s: hbase = [%lu] hmask = [0x%lx] failed (error [%d])\n",
	       __func__, hbase, hmask, result);
	return result;
}


struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
			unsigned long arg1, unsigned long arg2,
			unsigned long arg3, unsigned long arg4,
			unsigned long arg5)
{
	struct sbiret ret;

	register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
	register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
	register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
	register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
	register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
	register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
	register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
	register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
	asm volatile ("ecall"
		      : "+r" (a0), "+r" (a1)
		      : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
		      : "memory");
	ret.error = a0;
	ret.value = a1;

	return ret;
}
EXPORT_SYMBOL(sbi_ecall);

ffffffff80006a20 <sbi_ecall>:
ffffffff80006a20:	1101                	add	sp,sp,-32
ffffffff80006a22:	ec22                	sd	s0,24(sp)
ffffffff80006a24:	832a                	mv	t1,a0
ffffffff80006a26:	1000                	add	s0,sp,32
ffffffff80006a28:	8e2e                	mv	t3,a1
ffffffff80006a2a:	8532                	mv	a0,a2
ffffffff80006a2c:	85b6                	mv	a1,a3
ffffffff80006a2e:	863a                	mv	a2,a4
ffffffff80006a30:	86be                	mv	a3,a5
ffffffff80006a32:	8742                	mv	a4,a6
ffffffff80006a34:	87c6                	mv	a5,a7
ffffffff80006a36:	8872                	mv	a6,t3
ffffffff80006a38:	889a                	mv	a7,t1
ffffffff80006a3a:	00000073          	ecall
ffffffff80006a3e:	6462                	ld	s0,24(sp)
ffffffff80006a40:	6105                	add	sp,sp,32
ffffffff80006a42:	8082                	ret