diff --git a/drivers/powercap/idle_inject.c b/drivers/powercap/idle_inject.c
index a25eb2018acd..24646b8be9ac 100644
--- a/drivers/powercap/idle_inject.c
+++ b/drivers/powercap/idle_inject.c
@@ -10,11 +10,9 @@
* It relies on the smpboot kthreads feature providing common code for CPU
* hotplug and thread [un]parking.
*
- * All of the kthreads used for idle injection are created at init time.
- *
- * Next, the users of the idle injection framework provide a cpumask via
- * its register function. The kthreads will be synchronized with respect to
- * this cpumask.
+ * Users of the idle injection framework provide a cpumask via its register
+ * function. The kthreads are created on demand for CPUs in this mask and
+ * will be synchronized with respect to it.
*
* The idle + run duration is specified via separate helpers and that allows
* idle injection to be started.
@@ -40,6 +38,7 @@
#include <linux/cpu.h>
#include <linux/hrtimer.h>
#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smpboot.h>
@@ -60,6 +59,7 @@ struct idle_inject_thread {
/**
* struct idle_inject_device - idle injection data
* @timer: idle injection period timer
+ * @threads: per-device CPU hotplug thread descriptor
* @idle_duration_us: duration of CPU idle time to inject
* @run_duration_us: duration of CPU run time to allow
* @latency_us: max allowed latency@@ -82,6 +82,7 @@ struct idle_inject_thread {
*/
struct idle_inject_device {
struct hrtimer timer;
+ struct smp_hotplug_thread threads;
unsigned int idle_duration_us;
unsigned int run_duration_us;
unsigned int latency_us;@@ -92,6 +93,9 @@ struct idle_inject_device {
static DEFINE_PER_CPU(struct idle_inject_thread, idle_inject_thread);
static DEFINE_PER_CPU(struct idle_inject_device *, idle_inject_device);
+/* Serialize CPU ownership changes and the associated thread lifecycle. */
+static DEFINE_MUTEX(idle_inject_lock);
+
/**
* idle_inject_wakeup - Wake up idle injection threads
* @ii_dev: target idle injection device@@ -279,7 +283,8 @@ void idle_inject_stop(struct idle_inject_device *ii_dev)
iit = per_cpu_ptr(&idle_inject_thread, cpu);
iit->should_run = 0;
- wait_task_inactive(iit->tsk, TASK_ANY);
+ if (iit->tsk)
+ wait_task_inactive(iit->tsk, TASK_ANY);
}
cpu_hotplug_enable();
@@ -319,12 +324,12 @@ static int idle_inject_should_run(unsigned int cpu)
* idle
*
* This function creates an idle injection control device structure for the
- * given set of CPUs and initializes the timer associated with it. This
+ * given set of CPUs, registers their kthreads and initializes the timer. This
* function also allows to register update()callback.
* It does not start any injection cycles.
*
- * Return: NULL if memory allocation fails, idle injection control device
- * pointer on success.
+ * Return: NULL if allocation or thread registration fails, or a CPU is already
+ * registered; idle injection control device pointer on success.
*/
struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
@@ -342,6 +347,8 @@ struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
ii_dev->latency_us = UINT_MAX;
ii_dev->update = update;
+ mutex_lock(&idle_inject_lock);
+
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) {
if (per_cpu(idle_inject_device, cpu)) {@@ -352,6 +359,20 @@ struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
per_cpu(idle_inject_device, cpu) = ii_dev;
}
+ ii_dev->threads = (struct smp_hotplug_thread) {
+ .store = &idle_inject_thread.tsk,
+ .setup = idle_inject_setup,
+ .thread_fn = idle_inject_fn,
+ .thread_comm = "idle_inject/%u",
+ .thread_should_run = idle_inject_should_run,
+ .cpumask = to_cpumask(ii_dev->cpumask),
+ };
+
+ if (smpboot_register_percpu_thread(&ii_dev->threads))
+ goto out_rollback;
+
+ mutex_unlock(&idle_inject_lock);
+
return ii_dev;
out_rollback:@@ -361,6 +382,8 @@ struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
per_cpu(idle_inject_device, cpu_rb) = NULL;
}
+ mutex_unlock(&idle_inject_lock);
+
kfree(ii_dev);
return NULL;
@@ -372,11 +395,11 @@ EXPORT_SYMBOL_NS_GPL(idle_inject_register_full, "IDLE_INJECT");
* @cpumask: CPUs to be affected by idle injection
*
* This function creates an idle injection control device structure for the
- * given set of CPUs and initializes the timer associated with it. It does not
- * start any injection cycles.
+ * given set of CPUs, registers their kthreads and initializes the timer. It
+ * does not start any injection cycles.
*
- * Return: NULL if memory allocation fails, idle injection control device
- * pointer on success.
+ * Return: NULL if allocation or thread registration fails, or a CPU is already
+ * registered; idle injection control device pointer on success.
*/
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
{@@ -398,23 +421,15 @@ void idle_inject_unregister(struct idle_inject_device *ii_dev)
idle_inject_stop(ii_dev);
+ mutex_lock(&idle_inject_lock);
+
+ smpboot_unregister_percpu_thread(&ii_dev->threads);
+
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask))
per_cpu(idle_inject_device, cpu) = NULL;
+ mutex_unlock(&idle_inject_lock);
+
kfree(ii_dev);
}
EXPORT_SYMBOL_NS_GPL(idle_inject_unregister, "IDLE_INJECT");
-
-static struct smp_hotplug_thread idle_inject_threads = {
- .store = &idle_inject_thread.tsk,
- .setup = idle_inject_setup,
- .thread_fn = idle_inject_fn,
- .thread_comm = "idle_inject/%u",
- .thread_should_run = idle_inject_should_run,
-};
-
-static int __init idle_inject_init(void)
-{
- return smpboot_register_percpu_thread(&idle_inject_threads);
-}
-early_initcall(idle_inject_init);--
2.43.0