xref: /linux/include/linux/stop_machine.h (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
21da177e4SLinus Torvalds #ifndef _LINUX_STOP_MACHINE
31da177e4SLinus Torvalds #define _LINUX_STOP_MACHINE
41142d810STejun Heo 
51da177e4SLinus Torvalds #include <linux/cpu.h>
6eeec4fadSRusty Russell #include <linux/cpumask.h>
7bb2eac66SPaul Gortmaker #include <linux/smp.h>
81142d810STejun Heo #include <linux/list.h>
91da177e4SLinus Torvalds 
101142d810STejun Heo /*
111142d810STejun Heo  * stop_cpu[s]() is simplistic per-cpu maximum priority cpu
121142d810STejun Heo  * monopolization mechanism.  The caller can specify a non-sleeping
131142d810STejun Heo  * function to be executed on a single or multiple cpus preempting all
141142d810STejun Heo  * other processes and monopolizing those cpus until it finishes.
151142d810STejun Heo  *
161142d810STejun Heo  * Resources for this mechanism are preallocated when a cpu is brought
171142d810STejun Heo  * up and requests are guaranteed to be served as long as the target
181142d810STejun Heo  * cpus are online.
191142d810STejun Heo  */
201142d810STejun Heo typedef int (*cpu_stop_fn_t)(void *arg);
211142d810STejun Heo 
22bbf1bb3eSTejun Heo #ifdef CONFIG_SMP
23bbf1bb3eSTejun Heo 
241142d810STejun Heo struct cpu_stop_work {
251142d810STejun Heo 	struct list_head	list;		/* cpu_stopper->works */
261142d810STejun Heo 	cpu_stop_fn_t		fn;
27a8b62fd0SPeter Zijlstra 	unsigned long		caller;
281142d810STejun Heo 	void			*arg;
291142d810STejun Heo 	struct cpu_stop_done	*done;
301142d810STejun Heo };
311142d810STejun Heo 
321142d810STejun Heo int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg);
331be0bd77SPeter Zijlstra int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg);
341b034bd9SOleg Nesterov bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
351142d810STejun Heo 			 struct cpu_stop_work *work_buf);
36233e7f26SOleg Nesterov void stop_machine_park(int cpu);
37c00166d8SOleg Nesterov void stop_machine_unpark(int cpu);
384ecf0a43SHeiko Carstens void stop_machine_yield(const struct cpumask *cpumask);
391142d810STejun Heo 
40a8b62fd0SPeter Zijlstra extern void print_stop_info(const char *log_lvl, struct task_struct *task);
41a8b62fd0SPeter Zijlstra 
42bbf1bb3eSTejun Heo #else	/* CONFIG_SMP */
43bbf1bb3eSTejun Heo 
44bbf1bb3eSTejun Heo #include <linux/workqueue.h>
45bbf1bb3eSTejun Heo 
46bbf1bb3eSTejun Heo struct cpu_stop_work {
47bbf1bb3eSTejun Heo 	struct work_struct	work;
48bbf1bb3eSTejun Heo 	cpu_stop_fn_t		fn;
49bbf1bb3eSTejun Heo 	void			*arg;
50bbf1bb3eSTejun Heo };
51bbf1bb3eSTejun Heo 
stop_one_cpu(unsigned int cpu,cpu_stop_fn_t fn,void * arg)52bbf1bb3eSTejun Heo static inline int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg)
53bbf1bb3eSTejun Heo {
54bbf1bb3eSTejun Heo 	int ret = -ENOENT;
55bbf1bb3eSTejun Heo 	preempt_disable();
56bbf1bb3eSTejun Heo 	if (cpu == smp_processor_id())
57bbf1bb3eSTejun Heo 		ret = fn(arg);
58bbf1bb3eSTejun Heo 	preempt_enable();
59bbf1bb3eSTejun Heo 	return ret;
60bbf1bb3eSTejun Heo }
61bbf1bb3eSTejun Heo 
stop_one_cpu_nowait_workfn(struct work_struct * work)62bbf1bb3eSTejun Heo static void stop_one_cpu_nowait_workfn(struct work_struct *work)
63bbf1bb3eSTejun Heo {
64bbf1bb3eSTejun Heo 	struct cpu_stop_work *stwork =
65bbf1bb3eSTejun Heo 		container_of(work, struct cpu_stop_work, work);
66bbf1bb3eSTejun Heo 	preempt_disable();
67bbf1bb3eSTejun Heo 	stwork->fn(stwork->arg);
68bbf1bb3eSTejun Heo 	preempt_enable();
69bbf1bb3eSTejun Heo }
70bbf1bb3eSTejun Heo 
stop_one_cpu_nowait(unsigned int cpu,cpu_stop_fn_t fn,void * arg,struct cpu_stop_work * work_buf)711b034bd9SOleg Nesterov static inline bool stop_one_cpu_nowait(unsigned int cpu,
72bbf1bb3eSTejun Heo 				       cpu_stop_fn_t fn, void *arg,
73bbf1bb3eSTejun Heo 				       struct cpu_stop_work *work_buf)
74bbf1bb3eSTejun Heo {
75bbf1bb3eSTejun Heo 	if (cpu == smp_processor_id()) {
76bbf1bb3eSTejun Heo 		INIT_WORK(&work_buf->work, stop_one_cpu_nowait_workfn);
77bbf1bb3eSTejun Heo 		work_buf->fn = fn;
78bbf1bb3eSTejun Heo 		work_buf->arg = arg;
79bbf1bb3eSTejun Heo 		schedule_work(&work_buf->work);
801b034bd9SOleg Nesterov 		return true;
81bbf1bb3eSTejun Heo 	}
821b034bd9SOleg Nesterov 
831b034bd9SOleg Nesterov 	return false;
84bbf1bb3eSTejun Heo }
85bbf1bb3eSTejun Heo 
print_stop_info(const char * log_lvl,struct task_struct * task)86a8b62fd0SPeter Zijlstra static inline void print_stop_info(const char *log_lvl, struct task_struct *task) { }
87a8b62fd0SPeter Zijlstra 
88bbf1bb3eSTejun Heo #endif	/* CONFIG_SMP */
89bbf1bb3eSTejun Heo 
901142d810STejun Heo /*
911142d810STejun Heo  * stop_machine "Bogolock": stop the entire machine, disable
921142d810STejun Heo  * interrupts.  This is a very heavy lock, which is equivalent to
931142d810STejun Heo  * grabbing every spinlock (and more).  So the "read" side to such a
941816315bSJonathan Neuschäfer  * lock is anything which disables preemption.
951142d810STejun Heo  */
9686fffe4aSChris Wilson #if defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU)
971142d810STejun Heo 
981da177e4SLinus Torvalds /**
99eeec4fadSRusty Russell  * stop_machine: freeze the machine on all CPUs and run this function
1001da177e4SLinus Torvalds  * @fn: the function to run
1011da177e4SLinus Torvalds  * @data: the data ptr for the @fn()
102eeec4fadSRusty Russell  * @cpus: the cpus to run the @fn() on (NULL = any online cpu)
1031da177e4SLinus Torvalds  *
104ffdb5976SRusty Russell  * Description: This causes a thread to be scheduled on every cpu,
105ffdb5976SRusty Russell  * each of which disables interrupts.  The result is that no one is
106ffdb5976SRusty Russell  * holding a spinlock or inside any other preempt-disabled region when
107ffdb5976SRusty Russell  * @fn() runs.
1081da177e4SLinus Torvalds  *
1091da177e4SLinus Torvalds  * This can be thought of as a very heavy write lock, equivalent to
110fe5595c0SSebastian Andrzej Siewior  * grabbing every spinlock in the kernel.
111fe5595c0SSebastian Andrzej Siewior  *
112fe5595c0SSebastian Andrzej Siewior  * Protects against CPU hotplug.
113fe5595c0SSebastian Andrzej Siewior  */
1149a301f22SOleg Nesterov int stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus);
1151da177e4SLinus Torvalds 
116fe5595c0SSebastian Andrzej Siewior /**
117fe5595c0SSebastian Andrzej Siewior  * stop_machine_cpuslocked: freeze the machine on all CPUs and run this function
118fe5595c0SSebastian Andrzej Siewior  * @fn: the function to run
119fe5595c0SSebastian Andrzej Siewior  * @data: the data ptr for the @fn()
120fe5595c0SSebastian Andrzej Siewior  * @cpus: the cpus to run the @fn() on (NULL = any online cpu)
121fe5595c0SSebastian Andrzej Siewior  *
122fe5595c0SSebastian Andrzej Siewior  * Same as above. Must be called from with in a cpus_read_lock() protected
123fe5595c0SSebastian Andrzej Siewior  * region. Avoids nested calls to cpus_read_lock().
124fe5595c0SSebastian Andrzej Siewior  */
125fe5595c0SSebastian Andrzej Siewior int stop_machine_cpuslocked(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus);
126fe5595c0SSebastian Andrzej Siewior 
127*2760f5a4SPeter Zijlstra /**
128*2760f5a4SPeter Zijlstra  * stop_core_cpuslocked: - stop all threads on just one core
129*2760f5a4SPeter Zijlstra  * @cpu: any cpu in the targeted core
130*2760f5a4SPeter Zijlstra  * @fn: the function to run
131*2760f5a4SPeter Zijlstra  * @data: the data ptr for @fn()
132*2760f5a4SPeter Zijlstra  *
133*2760f5a4SPeter Zijlstra  * Same as above, but instead of every CPU, only the logical CPUs of a
134*2760f5a4SPeter Zijlstra  * single core are affected.
135*2760f5a4SPeter Zijlstra  *
136*2760f5a4SPeter Zijlstra  * Context: Must be called from within a cpus_read_lock() protected region.
137*2760f5a4SPeter Zijlstra  *
138*2760f5a4SPeter Zijlstra  * Return: 0 if all executions of @fn returned 0, any non zero return
139*2760f5a4SPeter Zijlstra  * value if any returned non zero.
140*2760f5a4SPeter Zijlstra  */
141*2760f5a4SPeter Zijlstra int stop_core_cpuslocked(unsigned int cpu, cpu_stop_fn_t fn, void *data);
142*2760f5a4SPeter Zijlstra 
1439a301f22SOleg Nesterov int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
144f740e6cdSTejun Heo 				   const struct cpumask *cpus);
14586fffe4aSChris Wilson #else	/* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
1461da177e4SLinus Torvalds 
stop_machine_cpuslocked(cpu_stop_fn_t fn,void * data,const struct cpumask * cpus)147cbf78d85SArnd Bergmann static __always_inline int stop_machine_cpuslocked(cpu_stop_fn_t fn, void *data,
14841c7bb95SRusty Russell 					  const struct cpumask *cpus)
1491da177e4SLinus Torvalds {
150f740e6cdSTejun Heo 	unsigned long flags;
1511da177e4SLinus Torvalds 	int ret;
152f740e6cdSTejun Heo 	local_irq_save(flags);
1531da177e4SLinus Torvalds 	ret = fn(data);
154f740e6cdSTejun Heo 	local_irq_restore(flags);
1551da177e4SLinus Torvalds 	return ret;
1561da177e4SLinus Torvalds }
1579ea09af3SHeiko Carstens 
158cbf78d85SArnd Bergmann static __always_inline int
stop_machine(cpu_stop_fn_t fn,void * data,const struct cpumask * cpus)159cbf78d85SArnd Bergmann stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus)
160fe5595c0SSebastian Andrzej Siewior {
161fe5595c0SSebastian Andrzej Siewior 	return stop_machine_cpuslocked(fn, data, cpus);
162fe5595c0SSebastian Andrzej Siewior }
163fe5595c0SSebastian Andrzej Siewior 
164cbf78d85SArnd Bergmann static __always_inline int
stop_machine_from_inactive_cpu(cpu_stop_fn_t fn,void * data,const struct cpumask * cpus)165cbf78d85SArnd Bergmann stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
166f740e6cdSTejun Heo 			       const struct cpumask *cpus)
167f740e6cdSTejun Heo {
1687eeb088eSOleg Nesterov 	return stop_machine(fn, data, cpus);
169f740e6cdSTejun Heo }
170f740e6cdSTejun Heo 
17186fffe4aSChris Wilson #endif	/* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
1721da177e4SLinus Torvalds #endif	/* _LINUX_STOP_MACHINE */
173