1 /* 2 * QEMU System Emulator 3 * 4 * Copyright (c) 2003-2008 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "qemu/thread.h" 27 #include "hw/core/cpu.h" 28 #include "qemu/main-loop.h" 29 #include "sysemu/cpus.h" 30 #include "sysemu/cpu-throttle.h" 31 32 /* vcpu throttling controls */ 33 static QEMUTimer *throttle_timer; 34 static unsigned int throttle_percentage; 35 36 #define CPU_THROTTLE_PCT_MIN 1 37 #define CPU_THROTTLE_PCT_MAX 99 38 #define CPU_THROTTLE_TIMESLICE_NS 10000000 39 40 static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque) 41 { 42 double pct; 43 double throttle_ratio; 44 int64_t sleeptime_ns, endtime_ns; 45 46 if (!cpu_throttle_get_percentage()) { 47 return; 48 } 49 50 pct = (double)cpu_throttle_get_percentage() / 100; 51 throttle_ratio = pct / (1 - pct); 52 /* Add 1ns to fix double's rounding error (like 0.9999999...) */ 53 sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1); 54 endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns; 55 while (sleeptime_ns > 0 && !cpu->stop) { 56 if (sleeptime_ns > SCALE_MS) { 57 qemu_cond_timedwait_bql(cpu->halt_cond, 58 sleeptime_ns / SCALE_MS); 59 } else { 60 bql_unlock(); 61 g_usleep(sleeptime_ns / SCALE_US); 62 bql_lock(); 63 } 64 sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME); 65 } 66 qatomic_set(&cpu->throttle_thread_scheduled, 0); 67 } 68 69 static void cpu_throttle_timer_tick(void *opaque) 70 { 71 CPUState *cpu; 72 double pct; 73 74 /* Stop the timer if needed */ 75 if (!cpu_throttle_get_percentage()) { 76 return; 77 } 78 CPU_FOREACH(cpu) { 79 if (!qatomic_xchg(&cpu->throttle_thread_scheduled, 1)) { 80 async_run_on_cpu(cpu, cpu_throttle_thread, 81 RUN_ON_CPU_NULL); 82 } 83 } 84 85 pct = (double)cpu_throttle_get_percentage() / 100; 86 timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 87 CPU_THROTTLE_TIMESLICE_NS / (1 - pct)); 88 } 89 90 void cpu_throttle_set(int new_throttle_pct) 91 { 92 /* 93 * boolean to store whether throttle is already active or not, 94 * before modifying throttle_percentage 95 */ 96 bool throttle_active = cpu_throttle_active(); 97 98 /* Ensure throttle percentage is within valid range */ 99 new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX); 100 new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN); 101 102 qatomic_set(&throttle_percentage, new_throttle_pct); 103 104 if (!throttle_active) { 105 cpu_throttle_timer_tick(NULL); 106 } 107 } 108 109 void cpu_throttle_stop(void) 110 { 111 qatomic_set(&throttle_percentage, 0); 112 } 113 114 bool cpu_throttle_active(void) 115 { 116 return (cpu_throttle_get_percentage() != 0); 117 } 118 119 int cpu_throttle_get_percentage(void) 120 { 121 return qatomic_read(&throttle_percentage); 122 } 123 124 void cpu_throttle_init(void) 125 { 126 throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, 127 cpu_throttle_timer_tick, NULL); 128 } 129