/* * menu.c - the menu idle governor * * Copyright (C) 2006-2007 Adam Belay * * This code is licenced under the GPL. */ #include #include #include #include #include #include #include #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);