Add a new cpuhp_offline_cb() API that allows us to offline a set of CPUs one-by-one, run the given callback function and then bring those CPUs back online again while inhibiting any concurrent CPU hotplug operations from happening.
This new API can be used to enable runtime adjustment of nohz_full and isolcpus boot command line options. A new cpuhp_offline_cb_mode flag is also added to signal that the system is in this offline callback transient state so that some hotplug operations can be optimized out if we choose to.
Signed-off-by: Waiman Long longman@redhat.com --- include/linux/cpuhplock.h | 9 ++++++++ kernel/cpu.c | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+)
diff --git a/include/linux/cpuhplock.h b/include/linux/cpuhplock.h index f7aa20f62b87..b42b81361abc 100644 --- a/include/linux/cpuhplock.h +++ b/include/linux/cpuhplock.h @@ -9,7 +9,9 @@
#include <linux/cleanup.h> #include <linux/errno.h> +#include <linux/cpumask_types.h>
+typedef int (*cpuhp_cb_t)(void *arg); struct device;
extern int lockdep_is_cpus_held(void); @@ -28,6 +30,8 @@ void clear_tasks_mm_cpumask(int cpu); int remove_cpu(unsigned int cpu); int cpu_device_down(struct device *dev); void smp_shutdown_nonboot_cpus(unsigned int primary_cpu); +int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg); +extern bool cpuhp_offline_cb_mode;
#else /* CONFIG_HOTPLUG_CPU */
@@ -42,6 +46,11 @@ static inline void cpu_hotplug_disable(void) { } static inline void cpu_hotplug_enable(void) { } static inline int remove_cpu(unsigned int cpu) { return -EPERM; } static inline void smp_shutdown_nonboot_cpus(unsigned int primary_cpu) { } +static inline int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg) +{ + return -EPERM; +} +#define cpuhp_offline_cb_mode false #endif /* !CONFIG_HOTPLUG_CPU */
DEFINE_LOCK_GUARD_0(cpus_read_lock, cpus_read_lock(), cpus_read_unlock()) diff --git a/kernel/cpu.c b/kernel/cpu.c index faf0f23fc5d8..b6364a1950b1 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1534,6 +1534,53 @@ int remove_cpu(unsigned int cpu) } EXPORT_SYMBOL_GPL(remove_cpu);
+bool cpuhp_offline_cb_mode; + +/** + * cpuhp_offline_cb - offline CPUs, invoke callback function & online CPUs afterward + * @mask: A mask of CPUs to be taken offline and then online + * @func: A callback function to be invoked while the given CPUs are offline + * @arg: Argument to be passed back to the callback function + * Return: 0 if successful, an error code otherwise + */ +int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg) +{ + int cpu, ret, ret2 = 0; + + if (WARN_ON_ONCE(cpumask_empty(mask))) + return -EINVAL; + + lock_device_hotplug(); + cpuhp_offline_cb_mode = true; + for_each_cpu(cpu, mask) { + ret = device_offline(get_cpu_device(cpu)); + if (unlikely(ret)) { + int cpu2; + + /* Online the offline CPUs before returning */ + for_each_cpu(cpu2, mask) { + if (cpu2 == cpu) + break; + device_online(get_cpu_device(cpu2)); + } + goto out; + } + } + ret = func(arg); + + /* Bring CPUs back online */ + for_each_cpu(cpu, mask) { + int ret3 = device_online(get_cpu_device(cpu)); + + if (ret3 && !ret2) + ret2 = ret3; + } +out: + cpuhp_offline_cb_mode = false; + unlock_device_hotplug(); + return ret ? ret : (ret2 ? ret2 : 0); +} + void smp_shutdown_nonboot_cpus(unsigned int primary_cpu) { unsigned int cpu;