18d7f2e76SPhilippe Mathieu-Daudé /* 28d7f2e76SPhilippe Mathieu-Daudé * QEMU System Emulator 38d7f2e76SPhilippe Mathieu-Daudé * 48d7f2e76SPhilippe Mathieu-Daudé * Copyright (c) 2003-2008 Fabrice Bellard 58d7f2e76SPhilippe Mathieu-Daudé * 68d7f2e76SPhilippe Mathieu-Daudé * Permission is hereby granted, free of charge, to any person obtaining a copy 78d7f2e76SPhilippe Mathieu-Daudé * of this software and associated documentation files (the "Software"), to deal 88d7f2e76SPhilippe Mathieu-Daudé * in the Software without restriction, including without limitation the rights 98d7f2e76SPhilippe Mathieu-Daudé * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 108d7f2e76SPhilippe Mathieu-Daudé * copies of the Software, and to permit persons to whom the Software is 118d7f2e76SPhilippe Mathieu-Daudé * furnished to do so, subject to the following conditions: 128d7f2e76SPhilippe Mathieu-Daudé * 138d7f2e76SPhilippe Mathieu-Daudé * The above copyright notice and this permission notice shall be included in 148d7f2e76SPhilippe Mathieu-Daudé * all copies or substantial portions of the Software. 158d7f2e76SPhilippe Mathieu-Daudé * 168d7f2e76SPhilippe Mathieu-Daudé * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 178d7f2e76SPhilippe Mathieu-Daudé * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 188d7f2e76SPhilippe Mathieu-Daudé * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 198d7f2e76SPhilippe Mathieu-Daudé * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 208d7f2e76SPhilippe Mathieu-Daudé * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 218d7f2e76SPhilippe Mathieu-Daudé * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 228d7f2e76SPhilippe Mathieu-Daudé * THE SOFTWARE. 238d7f2e76SPhilippe Mathieu-Daudé */ 248d7f2e76SPhilippe Mathieu-Daudé 258d7f2e76SPhilippe Mathieu-Daudé #include "qemu/osdep.h" 268d7f2e76SPhilippe Mathieu-Daudé #include "qemu/thread.h" 278d7f2e76SPhilippe Mathieu-Daudé #include "hw/core/cpu.h" 288d7f2e76SPhilippe Mathieu-Daudé #include "qemu/main-loop.h" 298d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/cpus.h" 308d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/cpu-throttle.h" 318d7f2e76SPhilippe Mathieu-Daudé 328d7f2e76SPhilippe Mathieu-Daudé /* vcpu throttling controls */ 338d7f2e76SPhilippe Mathieu-Daudé static QEMUTimer *throttle_timer; 348d7f2e76SPhilippe Mathieu-Daudé static unsigned int throttle_percentage; 358d7f2e76SPhilippe Mathieu-Daudé 368d7f2e76SPhilippe Mathieu-Daudé #define CPU_THROTTLE_PCT_MIN 1 378d7f2e76SPhilippe Mathieu-Daudé #define CPU_THROTTLE_PCT_MAX 99 388d7f2e76SPhilippe Mathieu-Daudé #define CPU_THROTTLE_TIMESLICE_NS 10000000 398d7f2e76SPhilippe Mathieu-Daudé 408d7f2e76SPhilippe Mathieu-Daudé static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque) 418d7f2e76SPhilippe Mathieu-Daudé { 428d7f2e76SPhilippe Mathieu-Daudé double pct; 438d7f2e76SPhilippe Mathieu-Daudé double throttle_ratio; 448d7f2e76SPhilippe Mathieu-Daudé int64_t sleeptime_ns, endtime_ns; 458d7f2e76SPhilippe Mathieu-Daudé 468d7f2e76SPhilippe Mathieu-Daudé if (!cpu_throttle_get_percentage()) { 478d7f2e76SPhilippe Mathieu-Daudé return; 488d7f2e76SPhilippe Mathieu-Daudé } 498d7f2e76SPhilippe Mathieu-Daudé 508d7f2e76SPhilippe Mathieu-Daudé pct = (double)cpu_throttle_get_percentage() / 100; 518d7f2e76SPhilippe Mathieu-Daudé throttle_ratio = pct / (1 - pct); 528d7f2e76SPhilippe Mathieu-Daudé /* Add 1ns to fix double's rounding error (like 0.9999999...) */ 538d7f2e76SPhilippe Mathieu-Daudé sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1); 548d7f2e76SPhilippe Mathieu-Daudé endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns; 558d7f2e76SPhilippe Mathieu-Daudé while (sleeptime_ns > 0 && !cpu->stop) { 568d7f2e76SPhilippe Mathieu-Daudé if (sleeptime_ns > SCALE_MS) { 578d7f2e76SPhilippe Mathieu-Daudé qemu_cond_timedwait_iothread(cpu->halt_cond, 588d7f2e76SPhilippe Mathieu-Daudé sleeptime_ns / SCALE_MS); 598d7f2e76SPhilippe Mathieu-Daudé } else { 608d7f2e76SPhilippe Mathieu-Daudé qemu_mutex_unlock_iothread(); 618d7f2e76SPhilippe Mathieu-Daudé g_usleep(sleeptime_ns / SCALE_US); 628d7f2e76SPhilippe Mathieu-Daudé qemu_mutex_lock_iothread(); 638d7f2e76SPhilippe Mathieu-Daudé } 648d7f2e76SPhilippe Mathieu-Daudé sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME); 658d7f2e76SPhilippe Mathieu-Daudé } 668d7f2e76SPhilippe Mathieu-Daudé qatomic_set(&cpu->throttle_thread_scheduled, 0); 678d7f2e76SPhilippe Mathieu-Daudé } 688d7f2e76SPhilippe Mathieu-Daudé 698d7f2e76SPhilippe Mathieu-Daudé static void cpu_throttle_timer_tick(void *opaque) 708d7f2e76SPhilippe Mathieu-Daudé { 718d7f2e76SPhilippe Mathieu-Daudé CPUState *cpu; 728d7f2e76SPhilippe Mathieu-Daudé double pct; 738d7f2e76SPhilippe Mathieu-Daudé 748d7f2e76SPhilippe Mathieu-Daudé /* Stop the timer if needed */ 758d7f2e76SPhilippe Mathieu-Daudé if (!cpu_throttle_get_percentage()) { 768d7f2e76SPhilippe Mathieu-Daudé return; 778d7f2e76SPhilippe Mathieu-Daudé } 788d7f2e76SPhilippe Mathieu-Daudé CPU_FOREACH(cpu) { 798d7f2e76SPhilippe Mathieu-Daudé if (!qatomic_xchg(&cpu->throttle_thread_scheduled, 1)) { 808d7f2e76SPhilippe Mathieu-Daudé async_run_on_cpu(cpu, cpu_throttle_thread, 818d7f2e76SPhilippe Mathieu-Daudé RUN_ON_CPU_NULL); 828d7f2e76SPhilippe Mathieu-Daudé } 838d7f2e76SPhilippe Mathieu-Daudé } 848d7f2e76SPhilippe Mathieu-Daudé 858d7f2e76SPhilippe Mathieu-Daudé pct = (double)cpu_throttle_get_percentage() / 100; 868d7f2e76SPhilippe Mathieu-Daudé timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 878d7f2e76SPhilippe Mathieu-Daudé CPU_THROTTLE_TIMESLICE_NS / (1 - pct)); 888d7f2e76SPhilippe Mathieu-Daudé } 898d7f2e76SPhilippe Mathieu-Daudé 908d7f2e76SPhilippe Mathieu-Daudé void cpu_throttle_set(int new_throttle_pct) 918d7f2e76SPhilippe Mathieu-Daudé { 928d7f2e76SPhilippe Mathieu-Daudé /* 938d7f2e76SPhilippe Mathieu-Daudé * boolean to store whether throttle is already active or not, 948d7f2e76SPhilippe Mathieu-Daudé * before modifying throttle_percentage 958d7f2e76SPhilippe Mathieu-Daudé */ 968d7f2e76SPhilippe Mathieu-Daudé bool throttle_active = cpu_throttle_active(); 978d7f2e76SPhilippe Mathieu-Daudé 988d7f2e76SPhilippe Mathieu-Daudé /* Ensure throttle percentage is within valid range */ 998d7f2e76SPhilippe Mathieu-Daudé new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX); 1008d7f2e76SPhilippe Mathieu-Daudé new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN); 1018d7f2e76SPhilippe Mathieu-Daudé 1028d7f2e76SPhilippe Mathieu-Daudé qatomic_set(&throttle_percentage, new_throttle_pct); 1038d7f2e76SPhilippe Mathieu-Daudé 1048d7f2e76SPhilippe Mathieu-Daudé if (!throttle_active) { 1058d7f2e76SPhilippe Mathieu-Daudé cpu_throttle_timer_tick(NULL); 1068d7f2e76SPhilippe Mathieu-Daudé } 1078d7f2e76SPhilippe Mathieu-Daudé } 1088d7f2e76SPhilippe Mathieu-Daudé 1098d7f2e76SPhilippe Mathieu-Daudé void cpu_throttle_stop(void) 1108d7f2e76SPhilippe Mathieu-Daudé { 1118d7f2e76SPhilippe Mathieu-Daudé qatomic_set(&throttle_percentage, 0); 1128d7f2e76SPhilippe Mathieu-Daudé } 1138d7f2e76SPhilippe Mathieu-Daudé 1148d7f2e76SPhilippe Mathieu-Daudé bool cpu_throttle_active(void) 1158d7f2e76SPhilippe Mathieu-Daudé { 1168d7f2e76SPhilippe Mathieu-Daudé return (cpu_throttle_get_percentage() != 0); 1178d7f2e76SPhilippe Mathieu-Daudé } 1188d7f2e76SPhilippe Mathieu-Daudé 1198d7f2e76SPhilippe Mathieu-Daudé int cpu_throttle_get_percentage(void) 1208d7f2e76SPhilippe Mathieu-Daudé { 1218d7f2e76SPhilippe Mathieu-Daudé return qatomic_read(&throttle_percentage); 1228d7f2e76SPhilippe Mathieu-Daudé } 1238d7f2e76SPhilippe Mathieu-Daudé 1248d7f2e76SPhilippe Mathieu-Daudé void cpu_throttle_init(void) 1258d7f2e76SPhilippe Mathieu-Daudé { 1268d7f2e76SPhilippe Mathieu-Daudé throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, 1278d7f2e76SPhilippe Mathieu-Daudé cpu_throttle_timer_tick, NULL); 1288d7f2e76SPhilippe Mathieu-Daudé } 129