stop_machine.h 4.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2
#ifndef _LINUX_STOP_MACHINE
#define _LINUX_STOP_MACHINE
T
Tejun Heo 已提交
3

L
Linus Torvalds 已提交
4
#include <linux/cpu.h>
5
#include <linux/cpumask.h>
6
#include <linux/smp.h>
T
Tejun Heo 已提交
7
#include <linux/list.h>
L
Linus Torvalds 已提交
8

T
Tejun Heo 已提交
9 10 11 12 13 14 15 16 17 18 19 20
/*
 * stop_cpu[s]() is simplistic per-cpu maximum priority cpu
 * monopolization mechanism.  The caller can specify a non-sleeping
 * function to be executed on a single or multiple cpus preempting all
 * other processes and monopolizing those cpus until it finishes.
 *
 * Resources for this mechanism are preallocated when a cpu is brought
 * up and requests are guaranteed to be served as long as the target
 * cpus are online.
 */
typedef int (*cpu_stop_fn_t)(void *arg);

21 22
#ifdef CONFIG_SMP

T
Tejun Heo 已提交
23 24 25 26 27 28 29 30
struct cpu_stop_work {
	struct list_head	list;		/* cpu_stopper->works */
	cpu_stop_fn_t		fn;
	void			*arg;
	struct cpu_stop_done	*done;
};

int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg);
31
int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg);
T
Tejun Heo 已提交
32 33 34 35
void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
			 struct cpu_stop_work *work_buf);
int stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg);
int try_stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg);
36
void stop_machine_park(int cpu);
37
void stop_machine_unpark(int cpu);
T
Tejun Heo 已提交
38

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#else	/* CONFIG_SMP */

#include <linux/workqueue.h>

struct cpu_stop_work {
	struct work_struct	work;
	cpu_stop_fn_t		fn;
	void			*arg;
};

static inline int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg)
{
	int ret = -ENOENT;
	preempt_disable();
	if (cpu == smp_processor_id())
		ret = fn(arg);
	preempt_enable();
	return ret;
}

static void stop_one_cpu_nowait_workfn(struct work_struct *work)
{
	struct cpu_stop_work *stwork =
		container_of(work, struct cpu_stop_work, work);
	preempt_disable();
	stwork->fn(stwork->arg);
	preempt_enable();
}

static inline void stop_one_cpu_nowait(unsigned int cpu,
				       cpu_stop_fn_t fn, void *arg,
				       struct cpu_stop_work *work_buf)
{
	if (cpu == smp_processor_id()) {
		INIT_WORK(&work_buf->work, stop_one_cpu_nowait_workfn);
		work_buf->fn = fn;
		work_buf->arg = arg;
		schedule_work(&work_buf->work);
	}
}

static inline int stop_cpus(const struct cpumask *cpumask,
			    cpu_stop_fn_t fn, void *arg)
{
	if (cpumask_test_cpu(raw_smp_processor_id(), cpumask))
		return stop_one_cpu(raw_smp_processor_id(), fn, arg);
	return -ENOENT;
}

static inline int try_stop_cpus(const struct cpumask *cpumask,
				cpu_stop_fn_t fn, void *arg)
{
	return stop_cpus(cpumask, fn, arg);
}

#endif	/* CONFIG_SMP */

T
Tejun Heo 已提交
96 97 98 99
/*
 * stop_machine "Bogolock": stop the entire machine, disable
 * interrupts.  This is a very heavy lock, which is equivalent to
 * grabbing every spinlock (and more).  So the "read" side to such a
100
 * lock is anything which disables preemption.
T
Tejun Heo 已提交
101
 */
102
#if defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU)
T
Tejun Heo 已提交
103

L
Linus Torvalds 已提交
104
/**
105
 * stop_machine: freeze the machine on all CPUs and run this function
L
Linus Torvalds 已提交
106 107
 * @fn: the function to run
 * @data: the data ptr for the @fn()
108
 * @cpus: the cpus to run the @fn() on (NULL = any online cpu)
L
Linus Torvalds 已提交
109
 *
R
Rusty Russell 已提交
110
 * Description: This causes a thread to be scheduled on every cpu,
L
Lucas De Marchi 已提交
111
 * each of which disables interrupts.  The result is that no one is
R
Rusty Russell 已提交
112 113
 * holding a spinlock or inside any other preempt-disabled region when
 * @fn() runs.
L
Linus Torvalds 已提交
114 115 116
 *
 * This can be thought of as a very heavy write lock, equivalent to
 * grabbing every spinlock in the kernel. */
117
int stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus);
L
Linus Torvalds 已提交
118

119
int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
120
				   const struct cpumask *cpus);
121
#else	/* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
L
Linus Torvalds 已提交
122

123
static inline int stop_machine(cpu_stop_fn_t fn, void *data,
124
				 const struct cpumask *cpus)
L
Linus Torvalds 已提交
125
{
126
	unsigned long flags;
L
Linus Torvalds 已提交
127
	int ret;
128
	local_irq_save(flags);
L
Linus Torvalds 已提交
129
	ret = fn(data);
130
	local_irq_restore(flags);
L
Linus Torvalds 已提交
131 132
	return ret;
}
133

134
static inline int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
135 136
						 const struct cpumask *cpus)
{
137
	return stop_machine(fn, data, cpus);
138 139
}

140
#endif	/* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
141
#endif	/* _LINUX_STOP_MACHINE */