On 08/09/2015 19:12, Jason J. Herne wrote: > 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 <jjhe...@linux.vnet.ibm.com> > Reviewed-by: Matthew Rosato <mjros...@linux.vnet.ibm.com>
Reviewed-by: Paolo Bonzini <pbonz...@redhat.com> Acked-by: Paolo Bonzini <pbonz...@redhat.com> Juan, please merge through your tree. Paolo > --- > cpus.c | 78 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > include/qom/cpu.h | 42 ++++++++++++++++++++++++++++++ > 2 files changed, 120 insertions(+) > > diff --git a/cpus.c b/cpus.c > index de6469f..b5ff9c9 100644 > --- a/cpus.c > +++ b/cpus.c > @@ -68,6 +68,14 @@ static CPUState *next_cpu; > int64_t max_delay; > 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) > { > return cpu->stopped || !runstate_is_running(); > @@ -486,10 +494,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) > { > seqlock_init(&timers_state.vm_clock_seqlock, NULL); > 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) > diff --git a/include/qom/cpu.h b/include/qom/cpu.h > index 39f0f19..db6ec1e 100644 > --- a/include/qom/cpu.h > +++ b/include/qom/cpu.h > @@ -310,6 +310,11 @@ struct CPUState { > uint32_t can_do_io; > 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 > offset from AREG0. Leave this field at the end so as to make the > (absolute value) offset as small as possible. This reduces code > @@ -553,6 +558,43 @@ CPUState *qemu_get_cpu(int index); > */ > 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 > > typedef void (*CPUInterruptHandler)(CPUState *, int); >