linux/drivers/powercap/idle_inject.c
Linus Torvalds 1b72607d73 Thermal control updates for 6.3-rc1
- Rework a large bunch of drivers to use the generic thermal trip
    structure and use the opportunity to do more cleanups by removing
    unused functions from the OF code (Daniel Lezcano).
 
  - Remove core header inclusion from drivers (Daniel Lezcano).
 
  - Fix some locking issues related to the generic thermal trip rework
    (Johan Hovold).
 
  - Fix a crash when requesting the critical temperature on tegra, which
    is related to the generic trip point work (Jon Hunter).
 
  - Clean up thermal device unregistration code (Viresh Kumar).
 
  - Fix and clean up thermal control core initialization error code
    paths (Daniel Lezcano).
 
  - Relocate the trip points handling code into a separate file (Daniel
    Lezcano).
 
  - Make the thermal core fail registration of thermal zones and cooling
    devices if the thermal class has not been registered (Rafael Wysocki).
 
  - Add trip point initialization helper functions for ACPI-defined trip
    points and modify two thermal drivers to use them (Rafael Wysocki,
    Daniel Lezcano).
 
  - Make the core thermal control code use sysfs_emit_at() instead of
    scnprintf() where applicable (ye xingchen).
 
  - Consolidate code accessing the Intel TCC (Thermal Control Circuitry)
    MSRs by introducing library functions for that and making the
    TCC-related code in thermal drivers use them (Zhang Rui).
 
  - Enhance the x86_pkg_temp_thermal driver to support dynamic tjmax
    changes (Zhang Rui).
 
  - Address an "unsigned expression compared with zero" warning in the
    intel_soc_dts_iosf thermal driver (Yang Li).
 
  - Update comments regarding two functions in the Intel Menlow thermal
    driver (Deming Wang).
 
  - Use sysfs_emit_at() instead of scnprintf() in the int340x thermal
    driver (ye xingchen).
 
  - Make the intel_pch thermal driver support the Wellsburg PCH (Tim
    Zimmermann).
 
  - Modify the intel_pch and processor_thermal_device_pci thermal drivers
    use generic trip point tables instead of thermal zone trip point
    callbacks (Daniel Lezcano).
 
  - Add production mode attribute sysfs attribute to the int340x thermal
    driver (Srinivas Pandruvada).
 
  - Rework dynamic trip point updates handling and locking in the int340x
    thermal driver (Rafael Wysocki).
 
  - Make the int340x thermal driver use a generic trip points table
    instead of thermal zone trip point callbacks (Rafael Wysocki, Daniel
    Lezcano).
 
  - Clean up and improve the int340x thermal driver (Rafael Wysocki).
 
  - Simplify and clean up the intel_pch thermal driver (Rafael Wysocki).
 
  - Fix the Intel powerclamp thermal driver and make it use the common
    idle injection framework (Srinivas Pandruvada).
 
  - Add two module parameters, cpumask and max_idle, to the Intel powerclamp
    thermal driver to allow it to affect only a specific subset of CPUs
    instead of all of them (Srinivas Pandruvada).
 
  - Make the Intel quark_dts thermal driver Use generic trip point
    objects instead of its own trip point representation (Daniel
    Lezcano).
 
  - Add toctree entry for thermal documents and fix two issues in the
    Intel powerclamp driver documentation (Bagas Sanjaya).
 
  - Use strscpy() to instead of strncpy() in the thermal core (Xu Panda).
 
  - Fix thermal_sampling_exit() (Vincent Guittot).
 
  - Add Mediatek Low Voltage Thermal Sensor (LVTS) driver (Balsam Chihi).
 
  - Add r8a779g0 RCar support to the rcar_gen3 thermal driver (Geert
    Uytterhoeven).
 
  - Fix useless call to set_trips() when resuming in the rcar_gen3
    thermal control driver and add interrupt support detection at init
    time to it (Niklas Söderlund).
 
  - Fix memory corruption in the hi3660 thermal driver (Yongqin Liu).
 
  - Fix include path for libnl3 in pkg-config file for libthermal (Vibhav
    Pant).
 
  - Remove syscfg-based driver for st as the platform is not supported
    any more (Alain Volmat).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmPuJuESHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxef0P/3h73rPjGEyuDlvXaazyXsJ2ItIoGeXF
 v9sDwK3IPeFTNwAu80RySXQViOG6G1e5Cl8Ee+LuuMZfPRlBnr3n35BazejDDK0N
 u3YAhPqtNOvWqr31T3A27dYtK+feFR2QL9SGFP0E4yxS1jpMOSO4Q24z7yaXdegT
 hD8YT1HbTW4Cra7A17qdXsG8LkIe0+GQXy7Ig/Dul1eqXTM4RSReGTmXic66hGpv
 lutqIQl8VdjmVBcQtTustpdycAD9zj07xd9BvOyM0lmF90zt6S0VOWFDsk+8u1jA
 FCiuRLBAM1xbguxGubahTVOM051J/MdfM5WqGgPtesNIXlDq4Je2WUGC07jGvSfV
 DMjNNb+nTkD3BK+BEe+rgv3KZBngj4p2sGHFW19v3EPdGftzohqDD5Oqn0GpsKR0
 J4GaT04T66A6jlNdzY/nPfOIw5FYEAsMwx4hR0qtEWDMT4uYtXQYM5iml9TBDoDx
 Kqyx+N8KhaKnQ4PLZ0MwtusyZydKQC1S1YK6G2eo+bXeJEre07FjZkiNfURi5gv9
 lrKS5nbAGBqUrNV4XnS18RmGAC+bxuQrNA5Gr0ouYaaLMT+jGzcdu1yCMeWJxwZI
 fFGAwE6sOU8EtmdGJrQdJt4eKCnpzOS7I1XuMDTBstl8Wv92x/YbH39vOl9wbJVs
 rmSkM+4t+sXb
 =tZwm
 -----END PGP SIGNATURE-----

Merge tag 'thermal-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull thermal control updates from Rafael Wysocki:
 "The majority of changes here are related to the general switch-over to
  using arrays of generic trip point structures registered along with a
  thermal zone instead of trip point callbacks (this has been done
  mostly by Daniel Lezcano with some help from yours truly on the Intel
  drivers front).

  Apart from that and the related reorganization of code, there are some
  enhancements of the existing driver and a new Mediatek Low Voltage
  Thermal Sensor (LVTS) driver. The Intel powerclamp undergoes a major
  rework so it will use the generic idle_inject facility for CPU idle
  time injection going forward and it will take additional module
  parameters for specifying the subset of CPUs to be affected by it
  (work done by Srinivas Pandruvada).

  Also included are assorted fixes and a whole bunch of cleanups.

  Specifics:

   - Rework a large bunch of drivers to use the generic thermal trip
     structure and use the opportunity to do more cleanups by removing
     unused functions from the OF code (Daniel Lezcano)

   - Remove core header inclusion from drivers (Daniel Lezcano)

   - Fix some locking issues related to the generic thermal trip rework
     (Johan Hovold)

   - Fix a crash when requesting the critical temperature on tegra,
     which is related to the generic trip point work (Jon Hunter)

   - Clean up thermal device unregistration code (Viresh Kumar)

   - Fix and clean up thermal control core initialization error code
     paths (Daniel Lezcano)

   - Relocate the trip points handling code into a separate file (Daniel
     Lezcano)

   - Make the thermal core fail registration of thermal zones and
     cooling devices if the thermal class has not been registered
     (Rafael Wysocki)

   - Add trip point initialization helper functions for ACPI-defined
     trip points and modify two thermal drivers to use them (Rafael
     Wysocki, Daniel Lezcano)

   - Make the core thermal control code use sysfs_emit_at() instead of
     scnprintf() where applicable (ye xingchen)

   - Consolidate code accessing the Intel TCC (Thermal Control
     Circuitry) MSRs by introducing library functions for that and
     making the TCC-related code in thermal drivers use them (Zhang Rui)

   - Enhance the x86_pkg_temp_thermal driver to support dynamic tjmax
     changes (Zhang Rui)

   - Address an "unsigned expression compared with zero" warning in the
     intel_soc_dts_iosf thermal driver (Yang Li)

   - Update comments regarding two functions in the Intel Menlow thermal
     driver (Deming Wang)

   - Use sysfs_emit_at() instead of scnprintf() in the int340x thermal
     driver (ye xingchen)

   - Make the intel_pch thermal driver support the Wellsburg PCH (Tim
     Zimmermann)

   - Modify the intel_pch and processor_thermal_device_pci thermal
     drivers use generic trip point tables instead of thermal zone trip
     point callbacks (Daniel Lezcano)

   - Add production mode attribute sysfs attribute to the int340x
     thermal driver (Srinivas Pandruvada)

   - Rework dynamic trip point updates handling and locking in the
     int340x thermal driver (Rafael Wysocki)

   - Make the int340x thermal driver use a generic trip points table
     instead of thermal zone trip point callbacks (Rafael Wysocki,
     Daniel Lezcano)

   - Clean up and improve the int340x thermal driver (Rafael Wysocki)

   - Simplify and clean up the intel_pch thermal driver (Rafael Wysocki)

   - Fix the Intel powerclamp thermal driver and make it use the common
     idle injection framework (Srinivas Pandruvada)

   - Add two module parameters, cpumask and max_idle, to the Intel
     powerclamp thermal driver to allow it to affect only a specific
     subset of CPUs instead of all of them (Srinivas Pandruvada)

   - Make the Intel quark_dts thermal driver Use generic trip point
     objects instead of its own trip point representation (Daniel
     Lezcano)

   - Add toctree entry for thermal documents and fix two issues in the
     Intel powerclamp driver documentation (Bagas Sanjaya)

   - Use strscpy() to instead of strncpy() in the thermal core (Xu
     Panda)

   - Fix thermal_sampling_exit() (Vincent Guittot)

   - Add Mediatek Low Voltage Thermal Sensor (LVTS) driver (Balsam
     Chihi)

   - Add r8a779g0 RCar support to the rcar_gen3 thermal driver (Geert
     Uytterhoeven)

   - Fix useless call to set_trips() when resuming in the rcar_gen3
     thermal control driver and add interrupt support detection at init
     time to it (Niklas Söderlund)

   - Fix memory corruption in the hi3660 thermal driver (Yongqin Liu)

   - Fix include path for libnl3 in pkg-config file for libthermal
     (Vibhav Pant)

   - Remove syscfg-based driver for st as the platform is not supported
     any more (Alain Volmat)"

* tag 'thermal-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (135 commits)
  thermal/drivers/st: Remove syscfg based driver
  thermal: Remove core header inclusion from drivers
  tools/lib/thermal: Fix include path for libnl3 in pkg-config file.
  thermal/drivers/hisi: Drop second sensor hi3660
  thermal/drivers/rcar_gen3_thermal: Fix device initialization
  thermal/drivers/rcar_gen3_thermal: Create device local ops struct
  thermal/drivers/rcar_gen3_thermal: Do not call set_trips() when resuming
  thermal/drivers/rcar_gen3: Add support for R-Car V4H
  dt-bindings: thermal: rcar-gen3-thermal: Add r8a779g0 support
  thermal/drivers/mediatek: Add the Low Voltage Thermal Sensor driver
  dt-bindings: thermal: mediatek: Add LVTS thermal controllers
  thermal/drivers/mediatek: Relocate driver to mediatek folder
  tools/lib/thermal: Fix thermal_sampling_exit()
  Documentation: powerclamp: Fix numbered lists formatting
  Documentation: powerclamp: Escape wildcard in cpumask description
  Documentation: admin-guide: Add toctree entry for thermal docs
  thermal: intel: powerclamp: Add two module parameters
  Documentation: admin-guide: Move intel_powerclamp documentation
  thermal: core: Use sysfs_emit_at() instead of scnprintf()
  thermal: intel: powerclamp: Fix duration module parameter
  ...
2023-02-21 12:32:05 -08:00

423 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2018 Linaro Limited
*
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
*
* The idle injection framework provides a way to force CPUs to enter idle
* states for a specified fraction of time over a specified period.
*
* 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.
*
* The idle + run duration is specified via separate helpers and that allows
* idle injection to be started.
*
* The idle injection kthreads will call play_idle_precise() with the idle
* duration and max allowed latency specified as per the above.
*
* After all of them have been woken up, a timer is set to start the next idle
* injection cycle.
*
* The timer interrupt handler will wake up the idle injection kthreads for
* all of the CPUs in the cpumask provided by the user.
*
* Idle injection is stopped synchronously and no leftover idle injection
* kthread activity after its completion is guaranteed.
*
* It is up to the user of this framework to provide a lock for higher-level
* synchronization to prevent race conditions like starting idle injection
* while unregistering from the framework.
*/
#define pr_fmt(fmt) "ii_dev: " fmt
#include <linux/cpu.h>
#include <linux/hrtimer.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smpboot.h>
#include <linux/idle_inject.h>
#include <uapi/linux/sched/types.h>
/**
* struct idle_inject_thread - task on/off switch structure
* @tsk: task injecting the idle cycles
* @should_run: whether or not to run the task (for the smpboot kthread API)
*/
struct idle_inject_thread {
struct task_struct *tsk;
int should_run;
};
/**
* struct idle_inject_device - idle injection data
* @timer: idle injection period timer
* @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
* @update: Optional callback deciding whether or not to skip idle
* injection in the given cycle.
* @cpumask: mask of CPUs affected by idle injection
*
* This structure is used to define per instance idle inject device data. Each
* instance has an idle duration, a run duration and mask of CPUs to inject
* idle.
*
* Actual CPU idle time is injected by calling kernel scheduler interface
* play_idle_precise(). There is one optional callback that can be registered
* by calling idle_inject_register_full():
*
* update() - This callback is invoked just before waking up CPUs to inject
* idle. If it returns false, CPUs are not woken up to inject idle in the given
* cycle. It also allows the caller to readjust the idle and run duration by
* calling idle_inject_set_duration() for the next cycle.
*/
struct idle_inject_device {
struct hrtimer timer;
unsigned int idle_duration_us;
unsigned int run_duration_us;
unsigned int latency_us;
bool (*update)(void);
unsigned long cpumask[];
};
static DEFINE_PER_CPU(struct idle_inject_thread, idle_inject_thread);
static DEFINE_PER_CPU(struct idle_inject_device *, idle_inject_device);
/**
* idle_inject_wakeup - Wake up idle injection threads
* @ii_dev: target idle injection device
*
* Every idle injection task associated with the given idle injection device
* and running on an online CPU will be woken up.
*/
static void idle_inject_wakeup(struct idle_inject_device *ii_dev)
{
struct idle_inject_thread *iit;
unsigned int cpu;
for_each_cpu_and(cpu, to_cpumask(ii_dev->cpumask), cpu_online_mask) {
iit = per_cpu_ptr(&idle_inject_thread, cpu);
iit->should_run = 1;
wake_up_process(iit->tsk);
}
}
/**
* idle_inject_timer_fn - idle injection timer function
* @timer: idle injection hrtimer
*
* This function is called when the idle injection timer expires. It wakes up
* idle injection tasks associated with the timer and they, in turn, invoke
* play_idle_precise() to inject a specified amount of CPU idle time.
*
* Return: HRTIMER_RESTART.
*/
static enum hrtimer_restart idle_inject_timer_fn(struct hrtimer *timer)
{
unsigned int duration_us;
struct idle_inject_device *ii_dev =
container_of(timer, struct idle_inject_device, timer);
if (!ii_dev->update || (ii_dev->update && ii_dev->update()))
idle_inject_wakeup(ii_dev);
duration_us = READ_ONCE(ii_dev->run_duration_us);
duration_us += READ_ONCE(ii_dev->idle_duration_us);
hrtimer_forward_now(timer, ns_to_ktime(duration_us * NSEC_PER_USEC));
return HRTIMER_RESTART;
}
/**
* idle_inject_fn - idle injection work function
* @cpu: the CPU owning the task
*
* This function calls play_idle_precise() to inject a specified amount of CPU
* idle time.
*/
static void idle_inject_fn(unsigned int cpu)
{
struct idle_inject_device *ii_dev;
struct idle_inject_thread *iit;
ii_dev = per_cpu(idle_inject_device, cpu);
iit = per_cpu_ptr(&idle_inject_thread, cpu);
/*
* Let the smpboot main loop know that the task should not run again.
*/
iit->should_run = 0;
play_idle_precise(READ_ONCE(ii_dev->idle_duration_us) * NSEC_PER_USEC,
READ_ONCE(ii_dev->latency_us) * NSEC_PER_USEC);
}
/**
* idle_inject_set_duration - idle and run duration update helper
* @ii_dev: idle injection control device structure
* @run_duration_us: CPU run time to allow in microseconds
* @idle_duration_us: CPU idle time to inject in microseconds
*/
void idle_inject_set_duration(struct idle_inject_device *ii_dev,
unsigned int run_duration_us,
unsigned int idle_duration_us)
{
if (run_duration_us + idle_duration_us) {
WRITE_ONCE(ii_dev->run_duration_us, run_duration_us);
WRITE_ONCE(ii_dev->idle_duration_us, idle_duration_us);
}
if (!run_duration_us)
pr_debug("CPU is forced to 100 percent idle\n");
}
EXPORT_SYMBOL_NS_GPL(idle_inject_set_duration, IDLE_INJECT);
/**
* idle_inject_get_duration - idle and run duration retrieval helper
* @ii_dev: idle injection control device structure
* @run_duration_us: memory location to store the current CPU run time
* @idle_duration_us: memory location to store the current CPU idle time
*/
void idle_inject_get_duration(struct idle_inject_device *ii_dev,
unsigned int *run_duration_us,
unsigned int *idle_duration_us)
{
*run_duration_us = READ_ONCE(ii_dev->run_duration_us);
*idle_duration_us = READ_ONCE(ii_dev->idle_duration_us);
}
EXPORT_SYMBOL_NS_GPL(idle_inject_get_duration, IDLE_INJECT);
/**
* idle_inject_set_latency - set the maximum latency allowed
* @ii_dev: idle injection control device structure
* @latency_us: set the latency requirement for the idle state
*/
void idle_inject_set_latency(struct idle_inject_device *ii_dev,
unsigned int latency_us)
{
WRITE_ONCE(ii_dev->latency_us, latency_us);
}
EXPORT_SYMBOL_NS_GPL(idle_inject_set_latency, IDLE_INJECT);
/**
* idle_inject_start - start idle injections
* @ii_dev: idle injection control device structure
*
* The function starts idle injection by first waking up all of the idle
* injection kthreads associated with @ii_dev to let them inject CPU idle time
* sets up a timer to start the next idle injection period.
*
* Return: -EINVAL if the CPU idle or CPU run time is not set or 0 on success.
*/
int idle_inject_start(struct idle_inject_device *ii_dev)
{
unsigned int idle_duration_us = READ_ONCE(ii_dev->idle_duration_us);
unsigned int run_duration_us = READ_ONCE(ii_dev->run_duration_us);
if (!(idle_duration_us + run_duration_us))
return -EINVAL;
pr_debug("Starting injecting idle cycles on CPUs '%*pbl'\n",
cpumask_pr_args(to_cpumask(ii_dev->cpumask)));
idle_inject_wakeup(ii_dev);
hrtimer_start(&ii_dev->timer,
ns_to_ktime((idle_duration_us + run_duration_us) *
NSEC_PER_USEC),
HRTIMER_MODE_REL);
return 0;
}
EXPORT_SYMBOL_NS_GPL(idle_inject_start, IDLE_INJECT);
/**
* idle_inject_stop - stops idle injections
* @ii_dev: idle injection control device structure
*
* The function stops idle injection and waits for the threads to finish work.
* If CPU idle time is being injected when this function runs, then it will
* wait until the end of the cycle.
*
* When it returns, there is no more idle injection kthread activity. The
* kthreads are scheduled out and the periodic timer is off.
*/
void idle_inject_stop(struct idle_inject_device *ii_dev)
{
struct idle_inject_thread *iit;
unsigned int cpu;
pr_debug("Stopping idle injection on CPUs '%*pbl'\n",
cpumask_pr_args(to_cpumask(ii_dev->cpumask)));
hrtimer_cancel(&ii_dev->timer);
/*
* Stopping idle injection requires all of the idle injection kthreads
* associated with the given cpumask to be parked and stay that way, so
* prevent CPUs from going online at this point. Any CPUs going online
* after the loop below will be covered by clearing the should_run flag
* that will cause the smpboot main loop to schedule them out.
*/
cpu_hotplug_disable();
/*
* Iterate over all (online + offline) CPUs here in case one of them
* goes offline with the should_run flag set so as to prevent its idle
* injection kthread from running when the CPU goes online again after
* the ii_dev has been freed.
*/
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) {
iit = per_cpu_ptr(&idle_inject_thread, cpu);
iit->should_run = 0;
wait_task_inactive(iit->tsk, TASK_ANY);
}
cpu_hotplug_enable();
}
EXPORT_SYMBOL_NS_GPL(idle_inject_stop, IDLE_INJECT);
/**
* idle_inject_setup - prepare the current task for idle injection
* @cpu: not used
*
* Called once, this function is in charge of setting the current task's
* scheduler parameters to make it an RT task.
*/
static void idle_inject_setup(unsigned int cpu)
{
sched_set_fifo(current);
}
/**
* idle_inject_should_run - function helper for the smpboot API
* @cpu: CPU the kthread is running on
*
* Return: whether or not the thread can run.
*/
static int idle_inject_should_run(unsigned int cpu)
{
struct idle_inject_thread *iit =
per_cpu_ptr(&idle_inject_thread, cpu);
return iit->should_run;
}
/**
* idle_inject_register_full - initialize idle injection on a set of CPUs
* @cpumask: CPUs to be affected by idle injection
* @update: This callback is called just before waking up CPUs to inject
* idle
*
* This function creates an idle injection control device structure for the
* given set of CPUs and initializes the timer associated with it. 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.
*/
struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
bool (*update)(void))
{
struct idle_inject_device *ii_dev;
int cpu, cpu_rb;
ii_dev = kzalloc(sizeof(*ii_dev) + cpumask_size(), GFP_KERNEL);
if (!ii_dev)
return NULL;
cpumask_copy(to_cpumask(ii_dev->cpumask), cpumask);
hrtimer_init(&ii_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ii_dev->timer.function = idle_inject_timer_fn;
ii_dev->latency_us = UINT_MAX;
ii_dev->update = update;
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) {
if (per_cpu(idle_inject_device, cpu)) {
pr_err("cpu%d is already registered\n", cpu);
goto out_rollback;
}
per_cpu(idle_inject_device, cpu) = ii_dev;
}
return ii_dev;
out_rollback:
for_each_cpu(cpu_rb, to_cpumask(ii_dev->cpumask)) {
if (cpu == cpu_rb)
break;
per_cpu(idle_inject_device, cpu_rb) = NULL;
}
kfree(ii_dev);
return NULL;
}
EXPORT_SYMBOL_NS_GPL(idle_inject_register_full, IDLE_INJECT);
/**
* idle_inject_register - initialize idle injection on a set of CPUs
* @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.
*
* Return: NULL if memory allocation fails, idle injection control device
* pointer on success.
*/
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
{
return idle_inject_register_full(cpumask, NULL);
}
EXPORT_SYMBOL_NS_GPL(idle_inject_register, IDLE_INJECT);
/**
* idle_inject_unregister - unregister idle injection control device
* @ii_dev: idle injection control device to unregister
*
* The function stops idle injection for the given control device,
* unregisters its kthreads and frees memory allocated when that device was
* created.
*/
void idle_inject_unregister(struct idle_inject_device *ii_dev)
{
unsigned int cpu;
idle_inject_stop(ii_dev);
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask))
per_cpu(idle_inject_device, cpu) = NULL;
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);