| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * intel_powerclamp.c - package c-state idle injection
- *
- * Copyright (c) 2012-2023, Intel Corporation.
- *
- * Authors:
- * Arjan van de Ven <arjan@linux.intel.com>
- * Jacob Pan <jacob.jun.pan@linux.intel.com>
- *
- * TODO:
- * 1. better handle wakeup from external interrupts, currently a fixed
- * compensation is added to clamping duration when excessive amount
- * of wakeups are observed during idle time. the reason is that in
- * case of external interrupts without need for ack, clamping down
- * cpu in non-irq context does not reduce irq. for majority of the
- * cases, clamping down cpu does help reduce irq as well, we should
- * be able to differentiate the two cases and give a quantitative
- * solution for the irqs that we can control. perhaps based on
- * get_cpu_iowait_time_us()
- *
- * 2. synchronization with other hw blocks
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/delay.h>
- #include <linux/cpu.h>
- #include <linux/thermal.h>
- #include <linux/debugfs.h>
- #include <linux/seq_file.h>
- #include <linux/idle_inject.h>
- #include <asm/msr.h>
- #include <asm/mwait.h>
- #include <asm/cpu_device_id.h>
- #define MAX_TARGET_RATIO (100U)
- /* For each undisturbed clamping period (no extra wake ups during idle time),
- * we increment the confidence counter for the given target ratio.
- * CONFIDENCE_OK defines the level where runtime calibration results are
- * valid.
- */
- #define CONFIDENCE_OK (3)
- /* Default idle injection duration, driver adjust sleep time to meet target
- * idle ratio. Similar to frequency modulation.
- */
- #define DEFAULT_DURATION_JIFFIES (6)
- static struct dentry *debug_dir;
- static bool poll_pkg_cstate_enable;
- /* Idle ratio observed using package C-state counters */
- static unsigned int current_ratio;
- /* Skip the idle injection till set to true */
- static bool should_skip;
- struct powerclamp_data {
- unsigned int cpu;
- unsigned int count;
- unsigned int guard;
- unsigned int window_size_now;
- unsigned int target_ratio;
- bool clamping;
- };
- static struct powerclamp_data powerclamp_data;
- static struct thermal_cooling_device *cooling_dev;
- static DEFINE_MUTEX(powerclamp_lock);
- /* This duration is in microseconds */
- static unsigned int duration;
- static unsigned int pkg_cstate_ratio_cur;
- static unsigned int window_size;
- static int duration_set(const char *arg, const struct kernel_param *kp)
- {
- int ret = 0;
- unsigned long new_duration;
- ret = kstrtoul(arg, 10, &new_duration);
- if (ret)
- goto exit;
- if (new_duration > 25 || new_duration < 6) {
- pr_err("Out of recommended range %lu, between 6-25ms\n",
- new_duration);
- ret = -EINVAL;
- goto exit;
- }
- mutex_lock(&powerclamp_lock);
- duration = clamp(new_duration, 6ul, 25ul) * 1000;
- mutex_unlock(&powerclamp_lock);
- exit:
- return ret;
- }
- static int duration_get(char *buf, const struct kernel_param *kp)
- {
- int ret;
- mutex_lock(&powerclamp_lock);
- ret = sysfs_emit(buf, "%d\n", duration / 1000);
- mutex_unlock(&powerclamp_lock);
- return ret;
- }
- static const struct kernel_param_ops duration_ops = {
- .set = duration_set,
- .get = duration_get,
- };
- module_param_cb(duration, &duration_ops, NULL, 0644);
- MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
- #define DEFAULT_MAX_IDLE 50
- #define MAX_ALL_CPU_IDLE 75
- static u8 max_idle = DEFAULT_MAX_IDLE;
- static cpumask_var_t idle_injection_cpu_mask;
- static int allocate_copy_idle_injection_mask(const struct cpumask *copy_mask)
- {
- if (cpumask_available(idle_injection_cpu_mask))
- goto copy_mask;
- /* This mask is allocated only one time and freed during module exit */
- if (!alloc_cpumask_var(&idle_injection_cpu_mask, GFP_KERNEL))
- return -ENOMEM;
- copy_mask:
- cpumask_copy(idle_injection_cpu_mask, copy_mask);
- return 0;
- }
- /* Return true if the cpumask and idle percent combination is invalid */
- static bool check_invalid(cpumask_var_t mask, u8 idle)
- {
- if (cpumask_equal(cpu_present_mask, mask) && idle > MAX_ALL_CPU_IDLE)
- return true;
- return false;
- }
- static int cpumask_set(const char *arg, const struct kernel_param *kp)
- {
- cpumask_var_t new_mask;
- int ret;
- mutex_lock(&powerclamp_lock);
- /* Can't set mask when cooling device is in use */
- if (powerclamp_data.clamping) {
- ret = -EAGAIN;
- goto skip_cpumask_set;
- }
- ret = alloc_cpumask_var(&new_mask, GFP_KERNEL);
- if (!ret)
- goto skip_cpumask_set;
- ret = bitmap_parse(arg, strlen(arg), cpumask_bits(new_mask),
- nr_cpumask_bits);
- if (ret)
- goto free_cpumask_set;
- if (cpumask_empty(new_mask) || check_invalid(new_mask, max_idle)) {
- ret = -EINVAL;
- goto free_cpumask_set;
- }
- /*
- * When module parameters are passed from kernel command line
- * during insmod, the module parameter callback is called
- * before powerclamp_init(), so we can't assume that some
- * cpumask can be allocated and copied before here. Also
- * in this case this cpumask is used as the default mask.
- */
- ret = allocate_copy_idle_injection_mask(new_mask);
- free_cpumask_set:
- free_cpumask_var(new_mask);
- skip_cpumask_set:
- mutex_unlock(&powerclamp_lock);
- return ret;
- }
- static int cpumask_get(char *buf, const struct kernel_param *kp)
- {
- if (!cpumask_available(idle_injection_cpu_mask))
- return -ENODEV;
- return bitmap_print_to_pagebuf(false, buf, cpumask_bits(idle_injection_cpu_mask),
- nr_cpumask_bits);
- }
- static const struct kernel_param_ops cpumask_ops = {
- .set = cpumask_set,
- .get = cpumask_get,
- };
- module_param_cb(cpumask, &cpumask_ops, NULL, 0644);
- MODULE_PARM_DESC(cpumask, "Mask of CPUs to use for idle injection.");
- static int max_idle_set(const char *arg, const struct kernel_param *kp)
- {
- u8 new_max_idle;
- int ret = 0;
- mutex_lock(&powerclamp_lock);
- /* Can't set mask when cooling device is in use */
- if (powerclamp_data.clamping) {
- ret = -EAGAIN;
- goto skip_limit_set;
- }
- ret = kstrtou8(arg, 10, &new_max_idle);
- if (ret)
- goto skip_limit_set;
- if (new_max_idle > MAX_TARGET_RATIO) {
- ret = -EINVAL;
- goto skip_limit_set;
- }
- if (!cpumask_available(idle_injection_cpu_mask)) {
- ret = allocate_copy_idle_injection_mask(cpu_present_mask);
- if (ret)
- goto skip_limit_set;
- }
- if (check_invalid(idle_injection_cpu_mask, new_max_idle)) {
- ret = -EINVAL;
- goto skip_limit_set;
- }
- max_idle = new_max_idle;
- skip_limit_set:
- mutex_unlock(&powerclamp_lock);
- return ret;
- }
- static const struct kernel_param_ops max_idle_ops = {
- .set = max_idle_set,
- .get = param_get_byte,
- };
- module_param_cb(max_idle, &max_idle_ops, &max_idle, 0644);
- MODULE_PARM_DESC(max_idle, "maximum injected idle time to the total CPU time ratio in percent range:1-100");
- struct powerclamp_calibration_data {
- unsigned long confidence; /* used for calibration, basically a counter
- * gets incremented each time a clamping
- * period is completed without extra wakeups
- * once that counter is reached given level,
- * compensation is deemed usable.
- */
- unsigned long steady_comp; /* steady state compensation used when
- * no extra wakeups occurred.
- */
- unsigned long dynamic_comp; /* compensate excessive wakeup from idle
- * mostly from external interrupts.
- */
- };
- static struct powerclamp_calibration_data cal_data[MAX_TARGET_RATIO];
- static int window_size_set(const char *arg, const struct kernel_param *kp)
- {
- int ret = 0;
- unsigned long new_window_size;
- ret = kstrtoul(arg, 10, &new_window_size);
- if (ret)
- goto exit_win;
- if (new_window_size > 10 || new_window_size < 2) {
- pr_err("Out of recommended window size %lu, between 2-10\n",
- new_window_size);
- ret = -EINVAL;
- }
- window_size = clamp(new_window_size, 2ul, 10ul);
- smp_mb();
- exit_win:
- return ret;
- }
- static const struct kernel_param_ops window_size_ops = {
- .set = window_size_set,
- .get = param_get_int,
- };
- module_param_cb(window_size, &window_size_ops, &window_size, 0644);
- MODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n"
- "\tpowerclamp controls idle ratio within this window. larger\n"
- "\twindow size results in slower response time but more smooth\n"
- "\tclamping results. default to 2.");
- struct pkg_cstate_info {
- bool skip;
- int msr_index;
- int cstate_id;
- };
- #define PKG_CSTATE_INIT(id) { \
- .msr_index = MSR_PKG_C##id##_RESIDENCY, \
- .cstate_id = id \
- }
- static struct pkg_cstate_info pkg_cstates[] = {
- PKG_CSTATE_INIT(2),
- PKG_CSTATE_INIT(3),
- PKG_CSTATE_INIT(6),
- PKG_CSTATE_INIT(7),
- PKG_CSTATE_INIT(8),
- PKG_CSTATE_INIT(9),
- PKG_CSTATE_INIT(10),
- {NULL},
- };
- static bool has_pkg_state_counter(void)
- {
- u64 val;
- struct pkg_cstate_info *info = pkg_cstates;
- /* check if any one of the counter msrs exists */
- while (info->msr_index) {
- if (!rdmsrl_safe(info->msr_index, &val))
- return true;
- info++;
- }
- return false;
- }
- static u64 pkg_state_counter(void)
- {
- u64 val;
- u64 count = 0;
- struct pkg_cstate_info *info = pkg_cstates;
- while (info->msr_index) {
- if (!info->skip) {
- if (!rdmsrl_safe(info->msr_index, &val))
- count += val;
- else
- info->skip = true;
- }
- info++;
- }
- return count;
- }
- static unsigned int get_compensation(int ratio)
- {
- unsigned int comp = 0;
- if (!poll_pkg_cstate_enable)
- return 0;
- /* we only use compensation if all adjacent ones are good */
- if (ratio == 1 &&
- cal_data[ratio].confidence >= CONFIDENCE_OK &&
- cal_data[ratio + 1].confidence >= CONFIDENCE_OK &&
- cal_data[ratio + 2].confidence >= CONFIDENCE_OK) {
- comp = (cal_data[ratio].steady_comp +
- cal_data[ratio + 1].steady_comp +
- cal_data[ratio + 2].steady_comp) / 3;
- } else if (ratio == MAX_TARGET_RATIO - 1 &&
- cal_data[ratio].confidence >= CONFIDENCE_OK &&
- cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
- cal_data[ratio - 2].confidence >= CONFIDENCE_OK) {
- comp = (cal_data[ratio].steady_comp +
- cal_data[ratio - 1].steady_comp +
- cal_data[ratio - 2].steady_comp) / 3;
- } else if (cal_data[ratio].confidence >= CONFIDENCE_OK &&
- cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
- cal_data[ratio + 1].confidence >= CONFIDENCE_OK) {
- comp = (cal_data[ratio].steady_comp +
- cal_data[ratio - 1].steady_comp +
- cal_data[ratio + 1].steady_comp) / 3;
- }
- /* do not exceed limit */
- if (comp + ratio >= MAX_TARGET_RATIO)
- comp = MAX_TARGET_RATIO - ratio - 1;
- return comp;
- }
- static void adjust_compensation(int target_ratio, unsigned int win)
- {
- int delta;
- struct powerclamp_calibration_data *d = &cal_data[target_ratio];
- /*
- * adjust compensations if confidence level has not been reached.
- */
- if (d->confidence >= CONFIDENCE_OK)
- return;
- delta = powerclamp_data.target_ratio - current_ratio;
- /* filter out bad data */
- if (delta >= 0 && delta <= (1+target_ratio/10)) {
- if (d->steady_comp)
- d->steady_comp =
- roundup(delta+d->steady_comp, 2)/2;
- else
- d->steady_comp = delta;
- d->confidence++;
- }
- }
- static bool powerclamp_adjust_controls(unsigned int target_ratio,
- unsigned int guard, unsigned int win)
- {
- static u64 msr_last, tsc_last;
- u64 msr_now, tsc_now;
- u64 val64;
- /* check result for the last window */
- msr_now = pkg_state_counter();
- tsc_now = rdtsc();
- /* calculate pkg cstate vs tsc ratio */
- if (!msr_last || !tsc_last)
- current_ratio = 1;
- else if (tsc_now-tsc_last) {
- val64 = 100*(msr_now-msr_last);
- do_div(val64, (tsc_now-tsc_last));
- current_ratio = val64;
- }
- /* update record */
- msr_last = msr_now;
- tsc_last = tsc_now;
- adjust_compensation(target_ratio, win);
- /* if we are above target+guard, skip */
- return powerclamp_data.target_ratio + guard <= current_ratio;
- }
- /*
- * This function calculates runtime from the current target ratio.
- * This function gets called under powerclamp_lock.
- */
- static unsigned int get_run_time(void)
- {
- unsigned int compensated_ratio;
- unsigned int runtime;
- /*
- * make sure user selected ratio does not take effect until
- * the next round. adjust target_ratio if user has changed
- * target such that we can converge quickly.
- */
- powerclamp_data.guard = 1 + powerclamp_data.target_ratio / 20;
- powerclamp_data.window_size_now = window_size;
- /*
- * systems may have different ability to enter package level
- * c-states, thus we need to compensate the injected idle ratio
- * to achieve the actual target reported by the HW.
- */
- compensated_ratio = powerclamp_data.target_ratio +
- get_compensation(powerclamp_data.target_ratio);
- if (compensated_ratio <= 0)
- compensated_ratio = 1;
- runtime = duration * 100 / compensated_ratio - duration;
- return runtime;
- }
- /*
- * 1 HZ polling while clamping is active, useful for userspace
- * to monitor actual idle ratio.
- */
- static void poll_pkg_cstate(struct work_struct *dummy);
- static DECLARE_DELAYED_WORK(poll_pkg_cstate_work, poll_pkg_cstate);
- static void poll_pkg_cstate(struct work_struct *dummy)
- {
- static u64 msr_last;
- static u64 tsc_last;
- u64 msr_now;
- u64 tsc_now;
- u64 val64;
- msr_now = pkg_state_counter();
- tsc_now = rdtsc();
- /* calculate pkg cstate vs tsc ratio */
- if (!msr_last || !tsc_last)
- pkg_cstate_ratio_cur = 1;
- else {
- if (tsc_now - tsc_last) {
- val64 = 100 * (msr_now - msr_last);
- do_div(val64, (tsc_now - tsc_last));
- pkg_cstate_ratio_cur = val64;
- }
- }
- /* update record */
- msr_last = msr_now;
- tsc_last = tsc_now;
- mutex_lock(&powerclamp_lock);
- if (powerclamp_data.clamping)
- schedule_delayed_work(&poll_pkg_cstate_work, HZ);
- mutex_unlock(&powerclamp_lock);
- }
- static struct idle_inject_device *ii_dev;
- /*
- * This function is called from idle injection core on timer expiry
- * for the run duration. This allows powerclamp to readjust or skip
- * injecting idle for this cycle.
- */
- static bool idle_inject_update(void)
- {
- bool update = false;
- /* We can't sleep in this callback */
- if (!mutex_trylock(&powerclamp_lock))
- return true;
- if (!(powerclamp_data.count % powerclamp_data.window_size_now)) {
- should_skip = powerclamp_adjust_controls(powerclamp_data.target_ratio,
- powerclamp_data.guard,
- powerclamp_data.window_size_now);
- update = true;
- }
- if (update) {
- unsigned int runtime = get_run_time();
- idle_inject_set_duration(ii_dev, runtime, duration);
- }
- powerclamp_data.count++;
- mutex_unlock(&powerclamp_lock);
- if (should_skip)
- return false;
- return true;
- }
- /* This function starts idle injection by calling idle_inject_start() */
- static void trigger_idle_injection(void)
- {
- unsigned int runtime = get_run_time();
- idle_inject_set_duration(ii_dev, runtime, duration);
- idle_inject_start(ii_dev);
- powerclamp_data.clamping = true;
- }
- /*
- * This function is called from start_power_clamp() to register
- * CPUS with powercap idle injection register and set default
- * idle duration and latency.
- */
- static int powerclamp_idle_injection_register(void)
- {
- poll_pkg_cstate_enable = false;
- if (cpumask_equal(cpu_present_mask, idle_injection_cpu_mask)) {
- ii_dev = idle_inject_register_full(idle_injection_cpu_mask, idle_inject_update);
- if (topology_max_packages() == 1 && topology_max_dies_per_package() == 1)
- poll_pkg_cstate_enable = true;
- } else {
- ii_dev = idle_inject_register(idle_injection_cpu_mask);
- }
- if (!ii_dev) {
- pr_err("powerclamp: idle_inject_register failed\n");
- return -EAGAIN;
- }
- idle_inject_set_duration(ii_dev, TICK_USEC, duration);
- idle_inject_set_latency(ii_dev, UINT_MAX);
- return 0;
- }
- /*
- * This function is called from end_power_clamp() to stop idle injection
- * and unregister CPUS from powercap idle injection core.
- */
- static void remove_idle_injection(void)
- {
- if (!powerclamp_data.clamping)
- return;
- powerclamp_data.clamping = false;
- idle_inject_stop(ii_dev);
- }
- /*
- * This function is called when user change the cooling device
- * state from zero to some other value.
- */
- static int start_power_clamp(void)
- {
- int ret;
- ret = powerclamp_idle_injection_register();
- if (!ret) {
- trigger_idle_injection();
- if (poll_pkg_cstate_enable)
- schedule_delayed_work(&poll_pkg_cstate_work, 0);
- }
- return ret;
- }
- /*
- * This function is called when user change the cooling device
- * state from non zero value zero.
- */
- static void end_power_clamp(void)
- {
- if (powerclamp_data.clamping) {
- remove_idle_injection();
- idle_inject_unregister(ii_dev);
- }
- }
- static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
- unsigned long *state)
- {
- *state = MAX_TARGET_RATIO;
- return 0;
- }
- static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev,
- unsigned long *state)
- {
- mutex_lock(&powerclamp_lock);
- *state = powerclamp_data.target_ratio;
- mutex_unlock(&powerclamp_lock);
- return 0;
- }
- static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev,
- unsigned long new_target_ratio)
- {
- int ret = 0;
- mutex_lock(&powerclamp_lock);
- new_target_ratio = clamp(new_target_ratio, 0UL,
- (unsigned long) (max_idle - 1));
- if (powerclamp_data.target_ratio == new_target_ratio)
- goto exit_set;
- if (!powerclamp_data.target_ratio && new_target_ratio > 0) {
- pr_info("Start idle injection to reduce power\n");
- powerclamp_data.target_ratio = new_target_ratio;
- ret = start_power_clamp();
- if (ret)
- powerclamp_data.target_ratio = 0;
- goto exit_set;
- } else if (powerclamp_data.target_ratio > 0 && new_target_ratio == 0) {
- pr_info("Stop forced idle injection\n");
- end_power_clamp();
- powerclamp_data.target_ratio = 0;
- } else /* adjust currently running */ {
- unsigned int runtime;
- powerclamp_data.target_ratio = new_target_ratio;
- runtime = get_run_time();
- idle_inject_set_duration(ii_dev, runtime, duration);
- }
- exit_set:
- mutex_unlock(&powerclamp_lock);
- return ret;
- }
- /* bind to generic thermal layer as cooling device*/
- static const struct thermal_cooling_device_ops powerclamp_cooling_ops = {
- .get_max_state = powerclamp_get_max_state,
- .get_cur_state = powerclamp_get_cur_state,
- .set_cur_state = powerclamp_set_cur_state,
- };
- static const struct x86_cpu_id __initconst intel_powerclamp_ids[] = {
- X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_MWAIT, NULL),
- {}
- };
- MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
- static int __init powerclamp_probe(void)
- {
- if (!x86_match_cpu(intel_powerclamp_ids)) {
- pr_err("CPU does not support MWAIT\n");
- return -ENODEV;
- }
- /* The goal for idle time alignment is to achieve package cstate. */
- if (!has_pkg_state_counter()) {
- pr_info("No package C-state available\n");
- return -ENODEV;
- }
- return 0;
- }
- static int powerclamp_debug_show(struct seq_file *m, void *unused)
- {
- int i = 0;
- seq_printf(m, "pct confidence steady dynamic (compensation)\n");
- for (i = 0; i < MAX_TARGET_RATIO; i++) {
- seq_printf(m, "%d\t%lu\t%lu\t%lu\n",
- i,
- cal_data[i].confidence,
- cal_data[i].steady_comp,
- cal_data[i].dynamic_comp);
- }
- return 0;
- }
- DEFINE_SHOW_ATTRIBUTE(powerclamp_debug);
- static inline void powerclamp_create_debug_files(void)
- {
- debug_dir = debugfs_create_dir("intel_powerclamp", NULL);
- debugfs_create_file("powerclamp_calib", S_IRUGO, debug_dir, cal_data,
- &powerclamp_debug_fops);
- }
- static int __init powerclamp_init(void)
- {
- int retval;
- /* probe cpu features and ids here */
- retval = powerclamp_probe();
- if (retval)
- return retval;
- mutex_lock(&powerclamp_lock);
- if (!cpumask_available(idle_injection_cpu_mask))
- retval = allocate_copy_idle_injection_mask(cpu_present_mask);
- mutex_unlock(&powerclamp_lock);
- if (retval)
- return retval;
- /* set default limit, maybe adjusted during runtime based on feedback */
- window_size = 2;
- cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL,
- &powerclamp_cooling_ops);
- if (IS_ERR(cooling_dev))
- return -ENODEV;
- if (!duration)
- duration = jiffies_to_usecs(DEFAULT_DURATION_JIFFIES);
- powerclamp_create_debug_files();
- return 0;
- }
- module_init(powerclamp_init);
- static void __exit powerclamp_exit(void)
- {
- mutex_lock(&powerclamp_lock);
- end_power_clamp();
- mutex_unlock(&powerclamp_lock);
- thermal_cooling_device_unregister(cooling_dev);
- cancel_delayed_work_sync(&poll_pkg_cstate_work);
- debugfs_remove_recursive(debug_dir);
- if (cpumask_available(idle_injection_cpu_mask))
- free_cpumask_var(idle_injection_cpu_mask);
- }
- module_exit(powerclamp_exit);
- MODULE_IMPORT_NS(IDLE_INJECT);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
- MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
- MODULE_DESCRIPTION("Package Level C-state Idle Injection for Intel CPUs");
|