123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- /*
- * menu.c - the menu idle governor
- *
- * Copyright (C) 2006-2007 Adam Belay <abelay@novell.com>
- *
- * This code is licenced under the GPL.
- */
- #include <linux/kernel.h>
- #include <linux/cpuidle.h>
- #include <linux/pm_qos_params.h>
- #include <linux/time.h>
- #include <linux/ktime.h>
- #include <linux/hrtimer.h>
- #include <linux/tick.h>
- #define BREAK_FUZZ 4 /* 4 us */
- #define PRED_HISTORY_PCT 50
- struct menu_device {
- int last_state_idx;
- unsigned int expected_us;
- unsigned int predicted_us;
- unsigned int current_predicted_us;
- unsigned int last_measured_us;
- unsigned int elapsed_us;
- };
- static DEFINE_PER_CPU(struct menu_device, menu_devices);
- /**
- * menu_select - selects the next idle state to enter
- * @dev: the CPU
- */
- static int menu_select(struct cpuidle_device *dev)
- {
- struct menu_device *data = &__get_cpu_var(menu_devices);
- int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY);
- int i;
- /* Special case when user has set very strict latency requirement */
- if (unlikely(latency_req == 0)) {
- data->last_state_idx = 0;
- return 0;
- }
- /* determine the expected residency time */
- data->expected_us =
- (u32) ktime_to_ns(tick_nohz_get_sleep_length()) / 1000;
- /* Recalculate predicted_us based on prediction_history_pct */
- data->predicted_us *= PRED_HISTORY_PCT;
- data->predicted_us += (100 - PRED_HISTORY_PCT) *
- data->current_predicted_us;
- data->predicted_us /= 100;
- /* find the deepest idle state that satisfies our constraints */
- for (i = CPUIDLE_DRIVER_STATE_START + 1; i < dev->state_count; i++) {
- struct cpuidle_state *s = &dev->states[i];
- if (s->target_residency > data->expected_us)
- break;
- if (s->target_residency > data->predicted_us)
- break;
- if (s->exit_latency > latency_req)
- break;
- }
- data->last_state_idx = i - 1;
- return i - 1;
- }
- /**
- * menu_reflect - attempts to guess what happened after entry
- * @dev: the CPU
- *
- * NOTE: it's important to be fast here because this operation will add to
- * the overall exit latency.
- */
- static void menu_reflect(struct cpuidle_device *dev)
- {
- struct menu_device *data = &__get_cpu_var(menu_devices);
- int last_idx = data->last_state_idx;
- unsigned int last_idle_us = cpuidle_get_last_residency(dev);
- struct cpuidle_state *target = &dev->states[last_idx];
- unsigned int measured_us;
- /*
- * Ugh, this idle state doesn't support residency measurements, so we
- * are basically lost in the dark. As a compromise, assume we slept
- * for one full standard timer tick. However, be aware that this
- * could potentially result in a suboptimal state transition.
- */
- if (unlikely(!(target->flags & CPUIDLE_FLAG_TIME_VALID)))
- last_idle_us = USEC_PER_SEC / HZ;
- /*
- * measured_us and elapsed_us are the cumulative idle time, since the
- * last time we were woken out of idle by an interrupt.
- */
- if (data->elapsed_us <= data->elapsed_us + last_idle_us)
- measured_us = data->elapsed_us + last_idle_us;
- else
- measured_us = -1;
- /* Predict time until next break event */
- data->current_predicted_us = max(measured_us, data->last_measured_us);
- if (last_idle_us + BREAK_FUZZ <
- data->expected_us - target->exit_latency) {
- data->last_measured_us = measured_us;
- data->elapsed_us = 0;
- } else {
- data->elapsed_us = measured_us;
- }
- }
- /**
- * menu_enable_device - scans a CPU's states and does setup
- * @dev: the CPU
- */
- static int menu_enable_device(struct cpuidle_device *dev)
- {
- struct menu_device *data = &per_cpu(menu_devices, dev->cpu);
- memset(data, 0, sizeof(struct menu_device));
- return 0;
- }
- static struct cpuidle_governor menu_governor = {
- .name = "menu",
- .rating = 20,
- .enable = menu_enable_device,
- .select = menu_select,
- .reflect = menu_reflect,
- .owner = THIS_MODULE,
- };
- /**
- * init_menu - initializes the governor
- */
- static int __init init_menu(void)
- {
- return cpuidle_register_governor(&menu_governor);
- }
- /**
- * exit_menu - exits the governor
- */
- static void __exit exit_menu(void)
- {
- cpuidle_unregister_governor(&menu_governor);
- }
- MODULE_LICENSE("GPL");
- module_init(init_menu);
- module_exit(exit_menu);
|