xref: /linux-6.15/kernel/stop_machine.c (revision ffdb5976)
1*ffdb5976SRusty Russell /* Copyright 2008, 2005 Rusty Russell [email protected] IBM Corporation.
2e5582ca2SRusty Russell  * GPL v2 and any later version.
3e5582ca2SRusty Russell  */
41da177e4SLinus Torvalds #include <linux/cpu.h>
51da177e4SLinus Torvalds #include <linux/err.h>
6ee527cd3SPrarit Bhargava #include <linux/kthread.h>
7ee527cd3SPrarit Bhargava #include <linux/module.h>
8ee527cd3SPrarit Bhargava #include <linux/sched.h>
9ee527cd3SPrarit Bhargava #include <linux/stop_machine.h>
101da177e4SLinus Torvalds #include <linux/syscalls.h>
11a12bb444SBenjamin Herrenschmidt #include <linux/interrupt.h>
12a12bb444SBenjamin Herrenschmidt 
131da177e4SLinus Torvalds #include <asm/atomic.h>
141da177e4SLinus Torvalds #include <asm/uaccess.h>
151da177e4SLinus Torvalds 
16*ffdb5976SRusty Russell /* This controls the threads on each CPU. */
171da177e4SLinus Torvalds enum stopmachine_state {
18*ffdb5976SRusty Russell 	/* Dummy starting state for thread. */
19*ffdb5976SRusty Russell 	STOPMACHINE_NONE,
20*ffdb5976SRusty Russell 	/* Awaiting everyone to be scheduled. */
211da177e4SLinus Torvalds 	STOPMACHINE_PREPARE,
22*ffdb5976SRusty Russell 	/* Disable interrupts. */
231da177e4SLinus Torvalds 	STOPMACHINE_DISABLE_IRQ,
24*ffdb5976SRusty Russell 	/* Run the function */
255c2aed62SJason Baron 	STOPMACHINE_RUN,
26*ffdb5976SRusty Russell 	/* Exit */
271da177e4SLinus Torvalds 	STOPMACHINE_EXIT,
281da177e4SLinus Torvalds };
29*ffdb5976SRusty Russell static enum stopmachine_state state;
301da177e4SLinus Torvalds 
315c2aed62SJason Baron struct stop_machine_data {
325c2aed62SJason Baron 	int (*fn)(void *);
335c2aed62SJason Baron 	void *data;
34*ffdb5976SRusty Russell 	int fnret;
35*ffdb5976SRusty Russell };
365c2aed62SJason Baron 
37*ffdb5976SRusty Russell /* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */
38*ffdb5976SRusty Russell static unsigned int num_threads;
39*ffdb5976SRusty Russell static atomic_t thread_ack;
40*ffdb5976SRusty Russell static struct completion finished;
41*ffdb5976SRusty Russell static DEFINE_MUTEX(lock);
421da177e4SLinus Torvalds 
43*ffdb5976SRusty Russell static void set_state(enum stopmachine_state newstate)
441da177e4SLinus Torvalds {
45*ffdb5976SRusty Russell 	/* Reset ack counter. */
46*ffdb5976SRusty Russell 	atomic_set(&thread_ack, num_threads);
47*ffdb5976SRusty Russell 	smp_wmb();
48*ffdb5976SRusty Russell 	state = newstate;
49*ffdb5976SRusty Russell }
501da177e4SLinus Torvalds 
51*ffdb5976SRusty Russell /* Last one to ack a state moves to the next state. */
52*ffdb5976SRusty Russell static void ack_state(void)
53*ffdb5976SRusty Russell {
54*ffdb5976SRusty Russell 	if (atomic_dec_and_test(&thread_ack)) {
55*ffdb5976SRusty Russell 		/* If we're the last one to ack the EXIT, we're finished. */
56*ffdb5976SRusty Russell 		if (state == STOPMACHINE_EXIT)
57*ffdb5976SRusty Russell 			complete(&finished);
58*ffdb5976SRusty Russell 		else
59*ffdb5976SRusty Russell 			set_state(state + 1);
60*ffdb5976SRusty Russell 	}
61*ffdb5976SRusty Russell }
62d8cb7c1dSAndrew Morton 
63*ffdb5976SRusty Russell /* This is the actual thread which stops the CPU.  It exits by itself rather
64*ffdb5976SRusty Russell  * than waiting for kthread_stop(), because it's easier for hotplug CPU. */
65*ffdb5976SRusty Russell static int stop_cpu(struct stop_machine_data *smdata)
66*ffdb5976SRusty Russell {
67*ffdb5976SRusty Russell 	enum stopmachine_state curstate = STOPMACHINE_NONE;
68*ffdb5976SRusty Russell 	int uninitialized_var(ret);
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	/* Simple state machine */
71*ffdb5976SRusty Russell 	do {
72*ffdb5976SRusty Russell 		/* Chill out and ensure we re-read stopmachine_state. */
73*ffdb5976SRusty Russell 		cpu_relax();
74*ffdb5976SRusty Russell 		if (state != curstate) {
75*ffdb5976SRusty Russell 			curstate = state;
76*ffdb5976SRusty Russell 			switch (curstate) {
77*ffdb5976SRusty Russell 			case STOPMACHINE_DISABLE_IRQ:
781da177e4SLinus Torvalds 				local_irq_disable();
79a12bb444SBenjamin Herrenschmidt 				hard_irq_disable();
80*ffdb5976SRusty Russell 				break;
81*ffdb5976SRusty Russell 			case STOPMACHINE_RUN:
82*ffdb5976SRusty Russell 				/* |= allows error detection if functions on
83*ffdb5976SRusty Russell 				 * multiple CPUs. */
84*ffdb5976SRusty Russell 				smdata->fnret |= smdata->fn(smdata->data);
85*ffdb5976SRusty Russell 				break;
86*ffdb5976SRusty Russell 			default:
87*ffdb5976SRusty Russell 				break;
881da177e4SLinus Torvalds 			}
89*ffdb5976SRusty Russell 			ack_state();
901da177e4SLinus Torvalds 		}
91*ffdb5976SRusty Russell 	} while (curstate != STOPMACHINE_EXIT);
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	local_irq_enable();
94*ffdb5976SRusty Russell 	do_exit(0);
95*ffdb5976SRusty Russell }
961da177e4SLinus Torvalds 
97*ffdb5976SRusty Russell /* Callback for CPUs which aren't supposed to do anything. */
98*ffdb5976SRusty Russell static int chill(void *unused)
99*ffdb5976SRusty Russell {
1001da177e4SLinus Torvalds 	return 0;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
103*ffdb5976SRusty Russell int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
1041da177e4SLinus Torvalds {
105*ffdb5976SRusty Russell 	int i, err;
106*ffdb5976SRusty Russell 	struct stop_machine_data active, idle;
107*ffdb5976SRusty Russell 	struct task_struct **threads;
1081da177e4SLinus Torvalds 
109*ffdb5976SRusty Russell 	active.fn = fn;
110*ffdb5976SRusty Russell 	active.data = data;
111*ffdb5976SRusty Russell 	active.fnret = 0;
112*ffdb5976SRusty Russell 	idle.fn = chill;
113*ffdb5976SRusty Russell 	idle.data = NULL;
1141da177e4SLinus Torvalds 
115*ffdb5976SRusty Russell 	/* If they don't care which cpu fn runs on, just pick one. */
116*ffdb5976SRusty Russell 	if (cpu == NR_CPUS)
117*ffdb5976SRusty Russell 		cpu = any_online_cpu(cpu_online_map);
118*ffdb5976SRusty Russell 
119*ffdb5976SRusty Russell 	/* This could be too big for stack on large machines. */
120*ffdb5976SRusty Russell 	threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL);
121*ffdb5976SRusty Russell 	if (!threads)
122*ffdb5976SRusty Russell 		return -ENOMEM;
123*ffdb5976SRusty Russell 
124*ffdb5976SRusty Russell 	/* Set up initial state. */
125*ffdb5976SRusty Russell 	mutex_lock(&lock);
126*ffdb5976SRusty Russell 	init_completion(&finished);
127*ffdb5976SRusty Russell 	num_threads = num_online_cpus();
128*ffdb5976SRusty Russell 	set_state(STOPMACHINE_PREPARE);
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	for_each_online_cpu(i) {
131*ffdb5976SRusty Russell 		struct stop_machine_data *smdata;
13285653af7SSatoru Takeuchi 		struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
13385653af7SSatoru Takeuchi 
134*ffdb5976SRusty Russell 		if (cpu == ALL_CPUS || i == cpu)
135*ffdb5976SRusty Russell 			smdata = &active;
136*ffdb5976SRusty Russell 		else
137*ffdb5976SRusty Russell 			smdata = &idle;
138*ffdb5976SRusty Russell 
139*ffdb5976SRusty Russell 		threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u",
140*ffdb5976SRusty Russell 					    i);
141*ffdb5976SRusty Russell 		if (IS_ERR(threads[i])) {
142*ffdb5976SRusty Russell 			err = PTR_ERR(threads[i]);
143*ffdb5976SRusty Russell 			threads[i] = NULL;
144*ffdb5976SRusty Russell 			goto kill_threads;
1451da177e4SLinus Torvalds 		}
146*ffdb5976SRusty Russell 
147*ffdb5976SRusty Russell 		/* Place it onto correct cpu. */
148*ffdb5976SRusty Russell 		kthread_bind(threads[i], i);
149*ffdb5976SRusty Russell 
150*ffdb5976SRusty Russell 		/* Make it highest prio. */
151*ffdb5976SRusty Russell 		if (sched_setscheduler_nocheck(threads[i], SCHED_FIFO, &param))
152*ffdb5976SRusty Russell 			BUG();
153*ffdb5976SRusty Russell 	}
154*ffdb5976SRusty Russell 
155*ffdb5976SRusty Russell 	/* We've created all the threads.  Wake them all: hold this CPU so one
156*ffdb5976SRusty Russell 	 * doesn't hit this CPU until we're ready. */
157*ffdb5976SRusty Russell 	cpu = get_cpu();
158*ffdb5976SRusty Russell 	for_each_online_cpu(i)
159*ffdb5976SRusty Russell 		wake_up_process(threads[i]);
160*ffdb5976SRusty Russell 
161*ffdb5976SRusty Russell 	/* This will release the thread on our CPU. */
162*ffdb5976SRusty Russell 	put_cpu();
163*ffdb5976SRusty Russell 	wait_for_completion(&finished);
164*ffdb5976SRusty Russell 	mutex_unlock(&lock);
165*ffdb5976SRusty Russell 
166*ffdb5976SRusty Russell 	kfree(threads);
167*ffdb5976SRusty Russell 
168*ffdb5976SRusty Russell 	return active.fnret;
169*ffdb5976SRusty Russell 
170*ffdb5976SRusty Russell kill_threads:
171*ffdb5976SRusty Russell 	for_each_online_cpu(i)
172*ffdb5976SRusty Russell 		if (threads[i])
173*ffdb5976SRusty Russell 			kthread_stop(threads[i]);
174*ffdb5976SRusty Russell 	mutex_unlock(&lock);
175*ffdb5976SRusty Russell 
176*ffdb5976SRusty Russell 	kfree(threads);
177*ffdb5976SRusty Russell 	return err;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds 	int ret;
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 	/* No CPUs can come up or down during this. */
18586ef5c9aSGautham R Shenoy 	get_online_cpus();
186*ffdb5976SRusty Russell 	ret = __stop_machine_run(fn, data, cpu);
18786ef5c9aSGautham R Shenoy 	put_online_cpus();
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	return ret;
1901da177e4SLinus Torvalds }
191ee527cd3SPrarit Bhargava EXPORT_SYMBOL_GPL(stop_machine_run);
192