Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_STOP_MACHINE #define _LINUX_STOP_MACHINE #include <linux/cpu.h> #include <linux/cpumask.h> #include <linux/smp.h> #include <linux/list.h> /* * 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); #ifdef CONFIG_SMP 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); int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg); bool 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); void stop_machine_park(int cpu); void stop_machine_unpark(int cpu); #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 bool 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); return true; } return false; } 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 */ /* * 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 * lock is anything which disables preemption. */ #if defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU) /** * stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn() * @cpus: the cpus to run the @fn() on (NULL = any online cpu) * * Description: This causes a thread to be scheduled on every cpu, * each of which disables interrupts. The result is that no one is * holding a spinlock or inside any other preempt-disabled region when * @fn() runs. * * This can be thought of as a very heavy write lock, equivalent to * grabbing every spinlock in the kernel. * * Protects against CPU hotplug. */ int stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus); /** * stop_machine_cpuslocked: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn() * @cpus: the cpus to run the @fn() on (NULL = any online cpu) * * Same as above. Must be called from with in a cpus_read_lock() protected * region. Avoids nested calls to cpus_read_lock(). */ int stop_machine_cpuslocked(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus); int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus); #else /* CONFIG_SMP || CONFIG_HOTPLUG_CPU */ static __always_inline int stop_machine_cpuslocked(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus) { unsigned long flags; int ret; local_irq_save(flags); ret = fn(data); local_irq_restore(flags); return ret; } static __always_inline int stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus) { return stop_machine_cpuslocked(fn, data, cpus); } static __always_inline int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus) { return stop_machine(fn, data, cpus); } #endif /* CONFIG_SMP || CONFIG_HOTPLUG_CPU */ #endif /* _LINUX_STOP_MACHINE */ |