xref: /kvm-unit-tests/lib/arm/smp.c (revision 018550041b3837264cbaa6c8984ba57a93491a51)
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>
962bdc67fSAndrew Jones #include <auxinfo.h>
10dfc1fec2SAndrew Jones #include <cpumask.h>
1168ea0e0bSAndrew Jones #include <asm/thread_info.h>
12ff942175SAndrew Jones #include <asm/spinlock.h>
131742c67aSAndrew Jones #include <asm/barrier.h>
1468ea0e0bSAndrew Jones #include <asm/mmu.h>
1568ea0e0bSAndrew Jones #include <asm/psci.h>
1668ea0e0bSAndrew Jones #include <asm/smp.h>
1768ea0e0bSAndrew Jones 
18ce024192SAndrew Jones bool cpu0_calls_idle;
19ce024192SAndrew Jones 
2068ea0e0bSAndrew Jones cpumask_t cpu_present_mask;
2168ea0e0bSAndrew Jones cpumask_t cpu_online_mask;
229246de4cSAndrew Jones cpumask_t cpu_idle_mask;
23ff942175SAndrew Jones 
2468ea0e0bSAndrew Jones struct secondary_data secondary_data;
25ff942175SAndrew Jones static struct spinlock lock;
2668ea0e0bSAndrew Jones 
270df901e0SAndrew Jones /* Needed to compile with -Wmissing-prototypes */
280df901e0SAndrew Jones secondary_entry_fn secondary_cinit(void);
290df901e0SAndrew 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);
3662bdc67fSAndrew Jones 
3762bdc67fSAndrew Jones 	if (!(auxinfo.flags & AUXINFO_MMU_OFF)) {
3836b50de9SAndrew Jones 		ti->pgtable = mmu_idmap;
391742c67aSAndrew Jones 		mmu_mark_enabled(ti->cpu);
4062bdc67fSAndrew Jones 	}
4168ea0e0bSAndrew Jones 
4268ea0e0bSAndrew Jones 	/*
4368ea0e0bSAndrew Jones 	 * Save secondary_data.entry locally to avoid opening a race
4468ea0e0bSAndrew Jones 	 * window between marking ourselves online and calling it.
4568ea0e0bSAndrew Jones 	 */
4668ea0e0bSAndrew Jones 	entry = secondary_data.entry;
4768ea0e0bSAndrew Jones 	set_cpu_online(ti->cpu, true);
4805a76472SAndrew Jones 	smp_send_event();
4968ea0e0bSAndrew Jones 
5068ea0e0bSAndrew Jones 	/*
5168ea0e0bSAndrew Jones 	 * Return to the assembly stub, allowing entry to be called
5268ea0e0bSAndrew Jones 	 * from there with an empty stack.
5368ea0e0bSAndrew Jones 	 */
5468ea0e0bSAndrew Jones 	return entry;
5568ea0e0bSAndrew Jones }
5668ea0e0bSAndrew Jones 
57e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry)
5868ea0e0bSAndrew Jones {
5918ab6cadSAndrew Jones 	int ret;
6068ea0e0bSAndrew Jones 
61632f935cSAndrew Jones 	secondary_data.stack = thread_stack_alloc();
6268ea0e0bSAndrew Jones 	secondary_data.entry = entry;
631742c67aSAndrew Jones 	mmu_mark_disabled(cpu);
6418ab6cadSAndrew Jones 	ret = cpu_psci_cpu_boot(cpu);
6518ab6cadSAndrew Jones 	assert(ret == 0);
6668ea0e0bSAndrew Jones 
6768ea0e0bSAndrew Jones 	while (!cpu_online(cpu))
6805a76472SAndrew Jones 		smp_wait_for_event();
69e713fd55SAndrew Jones }
70ff942175SAndrew Jones 
71e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry)
72e713fd55SAndrew Jones {
73e713fd55SAndrew Jones 	spin_lock(&lock);
74e713fd55SAndrew Jones 	assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu);
75e713fd55SAndrew Jones 	__smp_boot_secondary(cpu, entry);
76ff942175SAndrew Jones 	spin_unlock(&lock);
7768ea0e0bSAndrew Jones }
78543ce33cSAndrew Jones 
79*01855004SAndrew Jones void smp_boot_secondary_nofail(int cpu, secondary_entry_fn entry)
80*01855004SAndrew Jones {
81*01855004SAndrew Jones 	spin_lock(&lock);
82*01855004SAndrew Jones 	if (!cpu_online(cpu))
83*01855004SAndrew Jones 		__smp_boot_secondary(cpu, entry);
84*01855004SAndrew Jones 	spin_unlock(&lock);
85*01855004SAndrew Jones }
86*01855004SAndrew Jones 
87e713fd55SAndrew Jones struct on_cpu_info {
8800b34f56SAndrew Jones 	void (*func)(void *data);
89e713fd55SAndrew Jones 	void *data;
90942d182bSAndrew Jones 	cpumask_t waiters;
91e713fd55SAndrew Jones };
92e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS];
93*01855004SAndrew Jones static cpumask_t on_cpu_info_lock;
94*01855004SAndrew Jones 
95*01855004SAndrew Jones static bool get_on_cpu_info(int cpu)
96*01855004SAndrew Jones {
97*01855004SAndrew Jones 	return !cpumask_test_and_set_cpu(cpu, &on_cpu_info_lock);
98*01855004SAndrew Jones }
99*01855004SAndrew Jones 
100*01855004SAndrew Jones static void put_on_cpu_info(int cpu)
101*01855004SAndrew Jones {
102*01855004SAndrew Jones 	int ret = cpumask_test_and_clear_cpu(cpu, &on_cpu_info_lock);
103*01855004SAndrew Jones 	assert(ret);
104*01855004SAndrew Jones }
105e713fd55SAndrew Jones 
10612009dbbSAndrew Jones static void __deadlock_check(int cpu, const cpumask_t *waiters, bool *found)
10712009dbbSAndrew Jones {
10812009dbbSAndrew Jones 	int i;
10912009dbbSAndrew Jones 
11012009dbbSAndrew Jones 	for_each_cpu(i, waiters) {
11112009dbbSAndrew Jones 		if (i == cpu) {
11212009dbbSAndrew Jones 			printf("CPU%d", cpu);
11312009dbbSAndrew Jones 			*found = true;
11412009dbbSAndrew Jones 			return;
11512009dbbSAndrew Jones 		}
11612009dbbSAndrew Jones 		__deadlock_check(cpu, &on_cpu_info[i].waiters, found);
11712009dbbSAndrew Jones 		if (*found) {
11812009dbbSAndrew Jones 			printf(" <=> CPU%d", i);
11912009dbbSAndrew Jones 			return;
12012009dbbSAndrew Jones 		}
12112009dbbSAndrew Jones 	}
12212009dbbSAndrew Jones }
12312009dbbSAndrew Jones 
12412009dbbSAndrew Jones static void deadlock_check(int me, int cpu)
12512009dbbSAndrew Jones {
12612009dbbSAndrew Jones 	bool found = false;
12712009dbbSAndrew Jones 
12812009dbbSAndrew Jones 	__deadlock_check(cpu, &on_cpu_info[me].waiters, &found);
12912009dbbSAndrew Jones 	if (found) {
13012009dbbSAndrew Jones 		printf(" <=> CPU%d deadlock detectd\n", me);
13112009dbbSAndrew Jones 		assert(0);
13212009dbbSAndrew Jones 	}
13312009dbbSAndrew Jones }
13412009dbbSAndrew Jones 
135942d182bSAndrew Jones static void cpu_wait(int cpu)
136942d182bSAndrew Jones {
137942d182bSAndrew Jones 	int me = smp_processor_id();
138942d182bSAndrew Jones 
139942d182bSAndrew Jones 	if (cpu == me)
140942d182bSAndrew Jones 		return;
141942d182bSAndrew Jones 
142942d182bSAndrew Jones 	cpumask_set_cpu(me, &on_cpu_info[cpu].waiters);
14312009dbbSAndrew Jones 	deadlock_check(me, cpu);
144942d182bSAndrew Jones 	while (!cpu_idle(cpu))
14505a76472SAndrew Jones 		smp_wait_for_event();
146942d182bSAndrew Jones 	cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters);
147942d182bSAndrew Jones }
148942d182bSAndrew Jones 
1499246de4cSAndrew Jones void do_idle(void)
150543ce33cSAndrew Jones {
1519246de4cSAndrew Jones 	int cpu = smp_processor_id();
152543ce33cSAndrew Jones 
153ce024192SAndrew Jones 	if (cpu == 0)
154ce024192SAndrew Jones 		cpu0_calls_idle = true;
155ce024192SAndrew Jones 
1569246de4cSAndrew Jones 	set_cpu_idle(cpu, true);
15705a76472SAndrew Jones 	smp_send_event();
1589246de4cSAndrew Jones 
1599246de4cSAndrew Jones 	for (;;) {
1609246de4cSAndrew Jones 		while (cpu_idle(cpu))
16105a76472SAndrew Jones 			smp_wait_for_event();
162e713fd55SAndrew Jones 		smp_rmb();
163e713fd55SAndrew Jones 		on_cpu_info[cpu].func(on_cpu_info[cpu].data);
164e713fd55SAndrew Jones 		on_cpu_info[cpu].func = NULL;
165e713fd55SAndrew Jones 		smp_wmb();
1669246de4cSAndrew Jones 		set_cpu_idle(cpu, true);
16705a76472SAndrew Jones 		smp_send_event();
1689246de4cSAndrew Jones 	}
169543ce33cSAndrew Jones }
170543ce33cSAndrew Jones 
171e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data)
172e713fd55SAndrew Jones {
173e713fd55SAndrew Jones 	if (cpu == smp_processor_id()) {
174e713fd55SAndrew Jones 		func(data);
175e713fd55SAndrew Jones 		return;
176e713fd55SAndrew Jones 	}
177e713fd55SAndrew Jones 
178ce024192SAndrew Jones 	assert_msg(cpu != 0 || cpu0_calls_idle, "Waiting on CPU0, which is unlikely to idle. "
179ce024192SAndrew Jones 						"If this is intended set cpu0_calls_idle=1");
180ce024192SAndrew Jones 
181*01855004SAndrew Jones 	smp_boot_secondary_nofail(cpu, do_idle);
182e713fd55SAndrew Jones 
183e713fd55SAndrew Jones 	for (;;) {
184942d182bSAndrew Jones 		cpu_wait(cpu);
185*01855004SAndrew Jones 		if (get_on_cpu_info(cpu)) {
186e713fd55SAndrew Jones 			if ((volatile void *)on_cpu_info[cpu].func == NULL)
187e713fd55SAndrew Jones 				break;
188*01855004SAndrew Jones 			put_on_cpu_info(cpu);
189e713fd55SAndrew Jones 		}
190*01855004SAndrew Jones 	}
191*01855004SAndrew Jones 
192e713fd55SAndrew Jones 	on_cpu_info[cpu].func = func;
193e713fd55SAndrew Jones 	on_cpu_info[cpu].data = data;
194e713fd55SAndrew Jones 	set_cpu_idle(cpu, false);
195*01855004SAndrew Jones 	put_on_cpu_info(cpu);
19605a76472SAndrew Jones 	smp_send_event();
197e713fd55SAndrew Jones }
198e713fd55SAndrew Jones 
199e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data)
200e713fd55SAndrew Jones {
201e713fd55SAndrew Jones 	on_cpu_async(cpu, func, data);
202942d182bSAndrew Jones 	cpu_wait(cpu);
203e713fd55SAndrew Jones }
204e713fd55SAndrew Jones 
20500b34f56SAndrew Jones void on_cpus(void (*func)(void *data), void *data)
206543ce33cSAndrew Jones {
207ce024192SAndrew Jones 	int cpu, me = smp_processor_id();
208543ce33cSAndrew Jones 
209543ce33cSAndrew Jones 	for_each_present_cpu(cpu) {
210ce024192SAndrew Jones 		if (cpu == me)
211543ce33cSAndrew Jones 			continue;
21200b34f56SAndrew Jones 		on_cpu_async(cpu, func, data);
213543ce33cSAndrew Jones 	}
21400b34f56SAndrew Jones 	func(data);
215543ce33cSAndrew Jones 
216942d182bSAndrew Jones 	for_each_present_cpu(cpu) {
217942d182bSAndrew Jones 		if (cpu == me)
218942d182bSAndrew Jones 			continue;
219942d182bSAndrew Jones 		cpumask_set_cpu(me, &on_cpu_info[cpu].waiters);
22012009dbbSAndrew Jones 		deadlock_check(me, cpu);
221942d182bSAndrew Jones 	}
222ce024192SAndrew Jones 	while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1)
22305a76472SAndrew Jones 		smp_wait_for_event();
224942d182bSAndrew Jones 	for_each_present_cpu(cpu)
225942d182bSAndrew Jones 		cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters);
226543ce33cSAndrew Jones }
227