xref: /kvm-unit-tests/lib/arm/smp.c (revision 942d182b6a3e3e304f233555cb7dc727b0371a2e)
168ea0e0bSAndrew Jones /*
268ea0e0bSAndrew Jones  * Secondary cpu support
368ea0e0bSAndrew Jones  *
468ea0e0bSAndrew Jones  * Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com>
568ea0e0bSAndrew Jones  *
668ea0e0bSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
768ea0e0bSAndrew Jones  */
868ea0e0bSAndrew Jones #include <libcflat.h>
968ea0e0bSAndrew Jones #include <asm/thread_info.h>
10ff942175SAndrew Jones #include <asm/spinlock.h>
1168ea0e0bSAndrew Jones #include <asm/cpumask.h>
121742c67aSAndrew Jones #include <asm/barrier.h>
1368ea0e0bSAndrew Jones #include <asm/mmu.h>
1468ea0e0bSAndrew Jones #include <asm/psci.h>
1568ea0e0bSAndrew Jones #include <asm/smp.h>
1668ea0e0bSAndrew Jones 
17ce024192SAndrew Jones bool cpu0_calls_idle;
18ce024192SAndrew Jones 
1968ea0e0bSAndrew Jones cpumask_t cpu_present_mask;
2068ea0e0bSAndrew Jones cpumask_t cpu_online_mask;
219246de4cSAndrew Jones cpumask_t cpu_idle_mask;
22ff942175SAndrew Jones 
23ff942175SAndrew Jones struct secondary_data {
24ff942175SAndrew Jones 	void *stack;            /* must be first member of struct */
25ff942175SAndrew Jones 	secondary_entry_fn entry;
26ff942175SAndrew Jones };
2768ea0e0bSAndrew Jones struct secondary_data secondary_data;
28ff942175SAndrew Jones static struct spinlock lock;
2968ea0e0bSAndrew Jones 
3068ea0e0bSAndrew Jones secondary_entry_fn secondary_cinit(void)
3168ea0e0bSAndrew Jones {
3268ea0e0bSAndrew Jones 	struct thread_info *ti = current_thread_info();
3368ea0e0bSAndrew Jones 	secondary_entry_fn entry;
3468ea0e0bSAndrew Jones 
3568ea0e0bSAndrew Jones 	thread_info_init(ti, 0);
361742c67aSAndrew Jones 	mmu_mark_enabled(ti->cpu);
3768ea0e0bSAndrew Jones 
3868ea0e0bSAndrew Jones 	/*
3968ea0e0bSAndrew Jones 	 * Save secondary_data.entry locally to avoid opening a race
4068ea0e0bSAndrew Jones 	 * window between marking ourselves online and calling it.
4168ea0e0bSAndrew Jones 	 */
4268ea0e0bSAndrew Jones 	entry = secondary_data.entry;
4368ea0e0bSAndrew Jones 	set_cpu_online(ti->cpu, true);
4468ea0e0bSAndrew Jones 	sev();
4568ea0e0bSAndrew Jones 
4668ea0e0bSAndrew Jones 	/*
4768ea0e0bSAndrew Jones 	 * Return to the assembly stub, allowing entry to be called
4868ea0e0bSAndrew Jones 	 * from there with an empty stack.
4968ea0e0bSAndrew Jones 	 */
5068ea0e0bSAndrew Jones 	return entry;
5168ea0e0bSAndrew Jones }
5268ea0e0bSAndrew Jones 
53e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry)
5468ea0e0bSAndrew Jones {
5518ab6cadSAndrew Jones 	int ret;
5668ea0e0bSAndrew Jones 
57632f935cSAndrew Jones 	secondary_data.stack = thread_stack_alloc();
5868ea0e0bSAndrew Jones 	secondary_data.entry = entry;
591742c67aSAndrew Jones 	mmu_mark_disabled(cpu);
6018ab6cadSAndrew Jones 	ret = cpu_psci_cpu_boot(cpu);
6118ab6cadSAndrew Jones 	assert(ret == 0);
6268ea0e0bSAndrew Jones 
6368ea0e0bSAndrew Jones 	while (!cpu_online(cpu))
6468ea0e0bSAndrew Jones 		wfe();
65e713fd55SAndrew Jones }
66ff942175SAndrew Jones 
67e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry)
68e713fd55SAndrew Jones {
69e713fd55SAndrew Jones 	spin_lock(&lock);
70e713fd55SAndrew Jones 	assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu);
71e713fd55SAndrew Jones 	__smp_boot_secondary(cpu, entry);
72ff942175SAndrew Jones 	spin_unlock(&lock);
7368ea0e0bSAndrew Jones }
74543ce33cSAndrew Jones 
75e713fd55SAndrew Jones typedef void (*on_cpu_func)(void *);
76e713fd55SAndrew Jones struct on_cpu_info {
77e713fd55SAndrew Jones 	on_cpu_func func;
78e713fd55SAndrew Jones 	void *data;
79*942d182bSAndrew Jones 	cpumask_t waiters;
80e713fd55SAndrew Jones };
81e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS];
82e713fd55SAndrew Jones 
83*942d182bSAndrew Jones static void cpu_wait(int cpu)
84*942d182bSAndrew Jones {
85*942d182bSAndrew Jones 	int me = smp_processor_id();
86*942d182bSAndrew Jones 
87*942d182bSAndrew Jones 	if (cpu == me)
88*942d182bSAndrew Jones 		return;
89*942d182bSAndrew Jones 
90*942d182bSAndrew Jones 	cpumask_set_cpu(me, &on_cpu_info[cpu].waiters);
91*942d182bSAndrew Jones 	assert_msg(!cpumask_test_cpu(cpu, &on_cpu_info[me].waiters), "CPU%d <=> CPU%d deadlock detected", me, cpu);
92*942d182bSAndrew Jones 	while (!cpu_idle(cpu))
93*942d182bSAndrew Jones 		wfe();
94*942d182bSAndrew Jones 	cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters);
95*942d182bSAndrew Jones }
96*942d182bSAndrew Jones 
979246de4cSAndrew Jones void do_idle(void)
98543ce33cSAndrew Jones {
999246de4cSAndrew Jones 	int cpu = smp_processor_id();
100543ce33cSAndrew Jones 
101ce024192SAndrew Jones 	if (cpu == 0)
102ce024192SAndrew Jones 		cpu0_calls_idle = true;
103ce024192SAndrew Jones 
1049246de4cSAndrew Jones 	set_cpu_idle(cpu, true);
1059246de4cSAndrew Jones 	sev();
1069246de4cSAndrew Jones 
1079246de4cSAndrew Jones 	for (;;) {
1089246de4cSAndrew Jones 		while (cpu_idle(cpu))
1099246de4cSAndrew Jones 			wfe();
110e713fd55SAndrew Jones 		smp_rmb();
111e713fd55SAndrew Jones 		on_cpu_info[cpu].func(on_cpu_info[cpu].data);
112e713fd55SAndrew Jones 		on_cpu_info[cpu].func = NULL;
113e713fd55SAndrew Jones 		smp_wmb();
1149246de4cSAndrew Jones 		set_cpu_idle(cpu, true);
1159246de4cSAndrew Jones 		sev();
1169246de4cSAndrew Jones 	}
117543ce33cSAndrew Jones }
118543ce33cSAndrew Jones 
119e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data)
120e713fd55SAndrew Jones {
121e713fd55SAndrew Jones 	if (cpu == smp_processor_id()) {
122e713fd55SAndrew Jones 		func(data);
123e713fd55SAndrew Jones 		return;
124e713fd55SAndrew Jones 	}
125e713fd55SAndrew Jones 
126ce024192SAndrew Jones 	assert_msg(cpu != 0 || cpu0_calls_idle, "Waiting on CPU0, which is unlikely to idle. "
127ce024192SAndrew Jones 						"If this is intended set cpu0_calls_idle=1");
128ce024192SAndrew Jones 
129e713fd55SAndrew Jones 	spin_lock(&lock);
130e713fd55SAndrew Jones 	if (!cpu_online(cpu))
131e713fd55SAndrew Jones 		__smp_boot_secondary(cpu, do_idle);
132e713fd55SAndrew Jones 	spin_unlock(&lock);
133e713fd55SAndrew Jones 
134e713fd55SAndrew Jones 	for (;;) {
135*942d182bSAndrew Jones 		cpu_wait(cpu);
136e713fd55SAndrew Jones 		spin_lock(&lock);
137e713fd55SAndrew Jones 		if ((volatile void *)on_cpu_info[cpu].func == NULL)
138e713fd55SAndrew Jones 			break;
139e713fd55SAndrew Jones 		spin_unlock(&lock);
140e713fd55SAndrew Jones 	}
141e713fd55SAndrew Jones 	on_cpu_info[cpu].func = func;
142e713fd55SAndrew Jones 	on_cpu_info[cpu].data = data;
143e713fd55SAndrew Jones 	spin_unlock(&lock);
144e713fd55SAndrew Jones 	set_cpu_idle(cpu, false);
145e713fd55SAndrew Jones 	sev();
146e713fd55SAndrew Jones }
147e713fd55SAndrew Jones 
148e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data)
149e713fd55SAndrew Jones {
150e713fd55SAndrew Jones 	on_cpu_async(cpu, func, data);
151*942d182bSAndrew Jones 	cpu_wait(cpu);
152e713fd55SAndrew Jones }
153e713fd55SAndrew Jones 
1546f447945SAndrew Jones void on_cpus(void (*func)(void))
155543ce33cSAndrew Jones {
156ce024192SAndrew Jones 	int cpu, me = smp_processor_id();
157543ce33cSAndrew Jones 
158543ce33cSAndrew Jones 	for_each_present_cpu(cpu) {
159ce024192SAndrew Jones 		if (cpu == me)
160543ce33cSAndrew Jones 			continue;
1616f447945SAndrew Jones 		on_cpu_async(cpu, (on_cpu_func)func, NULL);
162543ce33cSAndrew Jones 	}
163543ce33cSAndrew Jones 	func();
164543ce33cSAndrew Jones 
165*942d182bSAndrew Jones 	for_each_present_cpu(cpu) {
166*942d182bSAndrew Jones 		if (cpu == me)
167*942d182bSAndrew Jones 			continue;
168*942d182bSAndrew Jones 		cpumask_set_cpu(me, &on_cpu_info[cpu].waiters);
169*942d182bSAndrew Jones 		assert_msg(!cpumask_test_cpu(cpu, &on_cpu_info[me].waiters), "CPU%d <=> CPU%d deadlock detected", me, cpu);
170*942d182bSAndrew Jones 	}
171ce024192SAndrew Jones 	while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1)
1729246de4cSAndrew Jones 		wfe();
173*942d182bSAndrew Jones 	for_each_present_cpu(cpu)
174*942d182bSAndrew Jones 		cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters);
175543ce33cSAndrew Jones }
176