cpu: Provide vcpu throttling interface

Provide a method to throttle guest cpu execution. CPUState is augmented with
timeout controls and throttle start/stop functions. To throttle the guest cpu
the caller simply has to call the throttle set function and provide a percentage
of throttle time.

Signed-off-by: Jason J. Herne <jjherne@linux.vnet.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.vnet.ibm.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
This commit is contained in:
Jason J. Herne 2015-09-08 13:12:33 -04:00 committed by Juan Quintela
parent 2a6e6e59df
commit 2adcc85d40
2 changed files with 120 additions and 0 deletions

78
cpus.c
View File

@ -69,6 +69,14 @@ static CPUState *next_cpu;
int64_t max_delay; int64_t max_delay;
int64_t max_advance; int64_t max_advance;
/* vcpu throttling controls */
static QEMUTimer *throttle_timer;
static unsigned int throttle_percentage;
#define CPU_THROTTLE_PCT_MIN 1
#define CPU_THROTTLE_PCT_MAX 99
#define CPU_THROTTLE_TIMESLICE_NS 10000000
bool cpu_is_stopped(CPUState *cpu) bool cpu_is_stopped(CPUState *cpu)
{ {
return cpu->stopped || !runstate_is_running(); return cpu->stopped || !runstate_is_running();
@ -505,10 +513,80 @@ static const VMStateDescription vmstate_timers = {
} }
}; };
static void cpu_throttle_thread(void *opaque)
{
CPUState *cpu = opaque;
double pct;
double throttle_ratio;
long sleeptime_ns;
if (!cpu_throttle_get_percentage()) {
return;
}
pct = (double)cpu_throttle_get_percentage()/100;
throttle_ratio = pct / (1 - pct);
sleeptime_ns = (long)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS);
qemu_mutex_unlock_iothread();
atomic_set(&cpu->throttle_thread_scheduled, 0);
g_usleep(sleeptime_ns / 1000); /* Convert ns to us for usleep call */
qemu_mutex_lock_iothread();
}
static void cpu_throttle_timer_tick(void *opaque)
{
CPUState *cpu;
double pct;
/* Stop the timer if needed */
if (!cpu_throttle_get_percentage()) {
return;
}
CPU_FOREACH(cpu) {
if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
async_run_on_cpu(cpu, cpu_throttle_thread, cpu);
}
}
pct = (double)cpu_throttle_get_percentage()/100;
timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
CPU_THROTTLE_TIMESLICE_NS / (1-pct));
}
void cpu_throttle_set(int new_throttle_pct)
{
/* Ensure throttle percentage is within valid range */
new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);
new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);
atomic_set(&throttle_percentage, new_throttle_pct);
timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
CPU_THROTTLE_TIMESLICE_NS);
}
void cpu_throttle_stop(void)
{
atomic_set(&throttle_percentage, 0);
}
bool cpu_throttle_active(void)
{
return (cpu_throttle_get_percentage() != 0);
}
int cpu_throttle_get_percentage(void)
{
return atomic_read(&throttle_percentage);
}
void cpu_ticks_init(void) void cpu_ticks_init(void)
{ {
seqlock_init(&timers_state.vm_clock_seqlock, NULL); seqlock_init(&timers_state.vm_clock_seqlock, NULL);
vmstate_register(NULL, 0, &vmstate_timers, &timers_state); vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
cpu_throttle_timer_tick, NULL);
} }
void configure_icount(QemuOpts *opts, Error **errp) void configure_icount(QemuOpts *opts, Error **errp)

View File

@ -321,6 +321,11 @@ struct CPUState {
uint32_t can_do_io; uint32_t can_do_io;
int32_t exception_index; /* used by m68k TCG */ int32_t exception_index; /* used by m68k TCG */
/* Used to keep track of an outstanding cpu throttle thread for migration
* autoconverge
*/
bool throttle_thread_scheduled;
/* Note that this is accessed at the start of every TB via a negative /* Note that this is accessed at the start of every TB via a negative
offset from AREG0. Leave this field at the end so as to make the offset from AREG0. Leave this field at the end so as to make the
(absolute value) offset as small as possible. This reduces code (absolute value) offset as small as possible. This reduces code
@ -565,6 +570,43 @@ CPUState *qemu_get_cpu(int index);
*/ */
bool cpu_exists(int64_t id); bool cpu_exists(int64_t id);
/**
* cpu_throttle_set:
* @new_throttle_pct: Percent of sleep time. Valid range is 1 to 99.
*
* Throttles all vcpus by forcing them to sleep for the given percentage of
* time. A throttle_percentage of 25 corresponds to a 75% duty cycle roughly.
* (example: 10ms sleep for every 30ms awake).
*
* cpu_throttle_set can be called as needed to adjust new_throttle_pct.
* Once the throttling starts, it will remain in effect until cpu_throttle_stop
* is called.
*/
void cpu_throttle_set(int new_throttle_pct);
/**
* cpu_throttle_stop:
*
* Stops the vcpu throttling started by cpu_throttle_set.
*/
void cpu_throttle_stop(void);
/**
* cpu_throttle_active:
*
* Returns: %true if the vcpus are currently being throttled, %false otherwise.
*/
bool cpu_throttle_active(void);
/**
* cpu_throttle_get_percentage:
*
* Returns the vcpu throttle percentage. See cpu_throttle_set for details.
*
* Returns: The throttle percentage in range 1 to 99.
*/
int cpu_throttle_get_percentage(void);
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
typedef void (*CPUInterruptHandler)(CPUState *, int); typedef void (*CPUInterruptHandler)(CPUState *, int);