18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * ladder.c - the residency ladder algorithm 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 58c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2005 Dominik Brodowski <linux@brodo.de> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> 98c2ecf20Sopenharmony_ci * Shaohua Li <shaohua.li@intel.com> 108c2ecf20Sopenharmony_ci * Adam Belay <abelay@novell.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This code is licenced under the GPL. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/cpuidle.h> 178c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 188c2ecf20Sopenharmony_ci#include <linux/tick.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/io.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PROMOTION_COUNT 4 248c2ecf20Sopenharmony_ci#define DEMOTION_COUNT 1 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct ladder_device_state { 278c2ecf20Sopenharmony_ci struct { 288c2ecf20Sopenharmony_ci u32 promotion_count; 298c2ecf20Sopenharmony_ci u32 demotion_count; 308c2ecf20Sopenharmony_ci u64 promotion_time_ns; 318c2ecf20Sopenharmony_ci u64 demotion_time_ns; 328c2ecf20Sopenharmony_ci } threshold; 338c2ecf20Sopenharmony_ci struct { 348c2ecf20Sopenharmony_ci int promotion_count; 358c2ecf20Sopenharmony_ci int demotion_count; 368c2ecf20Sopenharmony_ci } stats; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct ladder_device { 408c2ecf20Sopenharmony_ci struct ladder_device_state states[CPUIDLE_STATE_MAX]; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct ladder_device, ladder_devices); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * ladder_do_selection - prepares private data for a state change 478c2ecf20Sopenharmony_ci * @ldev: the ladder device 488c2ecf20Sopenharmony_ci * @old_idx: the current state index 498c2ecf20Sopenharmony_ci * @new_idx: the new target state index 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic inline void ladder_do_selection(struct cpuidle_device *dev, 528c2ecf20Sopenharmony_ci struct ladder_device *ldev, 538c2ecf20Sopenharmony_ci int old_idx, int new_idx) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci ldev->states[old_idx].stats.promotion_count = 0; 568c2ecf20Sopenharmony_ci ldev->states[old_idx].stats.demotion_count = 0; 578c2ecf20Sopenharmony_ci dev->last_state_idx = new_idx; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * ladder_select_state - selects the next state to enter 628c2ecf20Sopenharmony_ci * @drv: cpuidle driver 638c2ecf20Sopenharmony_ci * @dev: the CPU 648c2ecf20Sopenharmony_ci * @dummy: not used 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic int ladder_select_state(struct cpuidle_driver *drv, 678c2ecf20Sopenharmony_ci struct cpuidle_device *dev, bool *dummy) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); 708c2ecf20Sopenharmony_ci struct ladder_device_state *last_state; 718c2ecf20Sopenharmony_ci int last_idx = dev->last_state_idx; 728c2ecf20Sopenharmony_ci int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0; 738c2ecf20Sopenharmony_ci s64 latency_req = cpuidle_governor_latency_req(dev->cpu); 748c2ecf20Sopenharmony_ci s64 last_residency; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Special case when user has set very strict latency requirement */ 778c2ecf20Sopenharmony_ci if (unlikely(latency_req == 0)) { 788c2ecf20Sopenharmony_ci ladder_do_selection(dev, ldev, last_idx, 0); 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci last_state = &ldev->states[last_idx]; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci last_residency = dev->last_residency_ns - drv->states[last_idx].exit_latency_ns; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* consider promotion */ 878c2ecf20Sopenharmony_ci if (last_idx < drv->state_count - 1 && 888c2ecf20Sopenharmony_ci !dev->states_usage[last_idx + 1].disable && 898c2ecf20Sopenharmony_ci last_residency > last_state->threshold.promotion_time_ns && 908c2ecf20Sopenharmony_ci drv->states[last_idx + 1].exit_latency_ns <= latency_req) { 918c2ecf20Sopenharmony_ci last_state->stats.promotion_count++; 928c2ecf20Sopenharmony_ci last_state->stats.demotion_count = 0; 938c2ecf20Sopenharmony_ci if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { 948c2ecf20Sopenharmony_ci ladder_do_selection(dev, ldev, last_idx, last_idx + 1); 958c2ecf20Sopenharmony_ci return last_idx + 1; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* consider demotion */ 1008c2ecf20Sopenharmony_ci if (last_idx > first_idx && 1018c2ecf20Sopenharmony_ci (dev->states_usage[last_idx].disable || 1028c2ecf20Sopenharmony_ci drv->states[last_idx].exit_latency_ns > latency_req)) { 1038c2ecf20Sopenharmony_ci int i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (i = last_idx - 1; i > first_idx; i--) { 1068c2ecf20Sopenharmony_ci if (drv->states[i].exit_latency_ns <= latency_req) 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci ladder_do_selection(dev, ldev, last_idx, i); 1108c2ecf20Sopenharmony_ci return i; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (last_idx > first_idx && 1148c2ecf20Sopenharmony_ci last_residency < last_state->threshold.demotion_time_ns) { 1158c2ecf20Sopenharmony_ci last_state->stats.demotion_count++; 1168c2ecf20Sopenharmony_ci last_state->stats.promotion_count = 0; 1178c2ecf20Sopenharmony_ci if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) { 1188c2ecf20Sopenharmony_ci ladder_do_selection(dev, ldev, last_idx, last_idx - 1); 1198c2ecf20Sopenharmony_ci return last_idx - 1; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* otherwise remain at the current state */ 1248c2ecf20Sopenharmony_ci return last_idx; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/** 1288c2ecf20Sopenharmony_ci * ladder_enable_device - setup for the governor 1298c2ecf20Sopenharmony_ci * @drv: cpuidle driver 1308c2ecf20Sopenharmony_ci * @dev: the CPU 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic int ladder_enable_device(struct cpuidle_driver *drv, 1338c2ecf20Sopenharmony_ci struct cpuidle_device *dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int i; 1368c2ecf20Sopenharmony_ci int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0; 1378c2ecf20Sopenharmony_ci struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu); 1388c2ecf20Sopenharmony_ci struct ladder_device_state *lstate; 1398c2ecf20Sopenharmony_ci struct cpuidle_state *state; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dev->last_state_idx = first_idx; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (i = first_idx; i < drv->state_count; i++) { 1448c2ecf20Sopenharmony_ci state = &drv->states[i]; 1458c2ecf20Sopenharmony_ci lstate = &ldev->states[i]; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci lstate->stats.promotion_count = 0; 1488c2ecf20Sopenharmony_ci lstate->stats.demotion_count = 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci lstate->threshold.promotion_count = PROMOTION_COUNT; 1518c2ecf20Sopenharmony_ci lstate->threshold.demotion_count = DEMOTION_COUNT; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (i < drv->state_count - 1) 1548c2ecf20Sopenharmony_ci lstate->threshold.promotion_time_ns = state->exit_latency_ns; 1558c2ecf20Sopenharmony_ci if (i > first_idx) 1568c2ecf20Sopenharmony_ci lstate->threshold.demotion_time_ns = state->exit_latency_ns; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/** 1638c2ecf20Sopenharmony_ci * ladder_reflect - update the correct last_state_idx 1648c2ecf20Sopenharmony_ci * @dev: the CPU 1658c2ecf20Sopenharmony_ci * @index: the index of actual state entered 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic void ladder_reflect(struct cpuidle_device *dev, int index) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci if (index > 0) 1708c2ecf20Sopenharmony_ci dev->last_state_idx = index; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic struct cpuidle_governor ladder_governor = { 1748c2ecf20Sopenharmony_ci .name = "ladder", 1758c2ecf20Sopenharmony_ci .rating = 10, 1768c2ecf20Sopenharmony_ci .enable = ladder_enable_device, 1778c2ecf20Sopenharmony_ci .select = ladder_select_state, 1788c2ecf20Sopenharmony_ci .reflect = ladder_reflect, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/** 1828c2ecf20Sopenharmony_ci * init_ladder - initializes the governor 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistatic int __init init_ladder(void) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci /* 1878c2ecf20Sopenharmony_ci * When NO_HZ is disabled, or when booting with nohz=off, the ladder 1888c2ecf20Sopenharmony_ci * governor is better so give it a higher rating than the menu 1898c2ecf20Sopenharmony_ci * governor. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (!tick_nohz_enabled) 1928c2ecf20Sopenharmony_ci ladder_governor.rating = 25; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return cpuidle_register_governor(&ladder_governor); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cipostcore_initcall(init_ladder); 198