#include <linux/cpufreq.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
+#include <linux/module.h>
#include <linux/string.h>
+#include <linux/interrupt.h>
#include <asm/processor.h>
#include <asm/msr.h>
#include <asm/msr-index.h>
*****************************************************************/
+
/*
* This stub governor is simply a placeholder for preventing
* frequency changes from the Linux side. For now, we simply leave
};
+static struct workqueue_struct *pstate_wq;
+
+
+typedef struct {
+ struct work_struct work;
+ uint64_t freq;
+} pstate_work_t;
+
static inline void pstate_register_linux_governor(void)
{
cpufreq_register_governor(&stub_governor);
}
+static int pstate_linux_init(void)
+{
+ pstate_register_linux_governor();
+ pstate_wq = create_workqueue("v3vee_pstate_wq");
+ if (!pstate_wq) {
+ ERROR("Could not create work queue\n");
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ pstate_unregister_linux_governor();
+ return -1;
+}
+
+
+static void pstate_linux_deinit(void)
+{
+ pstate_unregister_linux_governor();
+ flush_workqueue(pstate_wq);
+ destroy_workqueue(pstate_wq);
+}
+
+
static int get_current_governor(char **buf, unsigned int cpu)
{
struct cpufreq_policy * policy = palacios_alloc(sizeof(struct cpufreq_policy));
/*
* Switch governors
* @s - the governor to switch to
+ * TODO: this should probably be submitted to a work queue
+ * so we don't have to run it in interrupt context
*/
static int governor_switch(char * s, unsigned int cpu)
{
unsigned int i = 0;
unsigned int count = 0;
+
policy = palacios_alloc(sizeof(struct cpufreq_policy));
if (!policy) {
ERROR("Could not allocate policy struct\n");
return policy->cur;
}
+static void
+pstate_switch_workfn (struct work_struct *work)
+{
+ pstate_work_t * pwork = (pstate_work_t*)work;
+ struct cpufreq_policy * policy = NULL;
+ int cpu = get_cpu();
+ put_cpu();
+
+ policy = palacios_alloc(sizeof(struct cpufreq_policy));
+ if (!policy) {
+ ERROR("Could not allocate space for cpufreq policy\n");
+ goto out;
+ }
+
+ if (cpufreq_get_policy(policy, cpu) != 0) {
+ ERROR("Could not get cpufreq policy\n");
+ goto out1;
+ }
+
+ INFO("P-state: setting frequency on core %u to %llu\n", cpu, pwork->freq);
+ cpufreq_driver_target(policy, pwork->freq, CPUFREQ_RELATION_H);
+
+ get_cpu_var(core_state).cur_freq_khz = pwork->freq;
+ put_cpu_var(core_state);
+
+out1:
+ palacios_free(policy);
+out:
+ palacios_free(work);
+}
+
static int linux_set_pstate(uint8_t p)
{
struct cpufreq_policy * policy = NULL;
struct cpufreq_frequency_table *table;
+ pstate_work_t * work = NULL;
int cpu = get_cpu();
unsigned int i = 0;
unsigned int count = 0;
int state_set = 0;
int last_valid = 0;
+ put_cpu();
policy = palacios_alloc(sizeof(struct cpufreq_policy));
if (!policy) {
return -1;
}
+ work = (pstate_work_t*)palacios_alloc(sizeof(pstate_work_t));
+ if (!work) {
+ ERROR("Could not allocate work struct\n");
+ goto out_err;
+ }
+
if (cpufreq_get_policy(policy, cpu)) {
ERROR("Could not get current policy\n");
- goto out_err;
+ goto out_err1;
}
table = cpufreq_frequency_get_table(cpu);
}
if (count == p) {
- cpufreq_driver_target(policy, table[i].frequency, CPUFREQ_RELATION_H);
+
+ INIT_WORK((struct work_struct*)work, pstate_switch_workfn);
+ work->freq = table[i].frequency;
+ queue_work(pstate_wq, (struct work_struct*)work);
+
state_set = 1;
+ break;
}
count++;
/* we need to deal with the case in which we get a number > max pstate */
if (!state_set) {
- cpufreq_driver_target(policy, table[last_valid].frequency, CPUFREQ_RELATION_H);
+ INIT_WORK((struct work_struct*)work, pstate_switch_workfn);
+ work->freq = table[last_valid].frequency;
+ queue_work(pstate_wq, (struct work_struct*)work);
}
palacios_free(policy);
return 0;
+out_err1:
+ palacios_free(work);
out_err:
palacios_free(policy);
return -1;
static int linux_set_freq(uint64_t f)
{
struct cpufreq_policy * policy = NULL;
- int cpu = get_cpu();
+ pstate_work_t * work = NULL;
uint64_t freq;
+ int cpu = get_cpu();
+ put_cpu();
policy = palacios_alloc(sizeof(struct cpufreq_policy));
if (!policy) {
return -1;
}
- cpufreq_get_policy(policy, cpu);
+ work = (pstate_work_t*)palacios_alloc(sizeof(pstate_work_t));
+ if (!work) {
+ ERROR("Could not allocate work struct\n");
+ goto out_err;
+ }
+
+ if (cpufreq_get_policy(policy, cpu) != 0) {
+ ERROR("Could not get cpufreq policy\n");
+ goto out_err1;
+ }
if (f < policy->min) {
freq = policy->min;
freq = f;
}
- cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_H);
+ INIT_WORK((struct work_struct*)work, pstate_switch_workfn);
+ work->freq = freq;
+ queue_work(pstate_wq, (struct work_struct*)work);
palacios_free(policy);
return 0;
+
+out_err1:
+ palacios_free(work);
+out_err:
+ palacios_free(policy);
+ return -1;
}
get_cpu_var(core_state).have_cpufreq = 1;
get_cpu_var(core_state).min_freq_khz=p->min;
get_cpu_var(core_state).max_freq_khz=p->max;
- get_cpu_var(core_state).cur_freq_khz=p->cur;
- }
-
- cpufreq_cpu_put(p);
-
+ get_cpu_var(core_state).cur_freq_khz=p->cur; } cpufreq_cpu_put(p);
put_cpu_var(core_state);
for (i=0;i<get_cpu_var(processors)->performance->state_count; i++) {
{
DEBUG("P-State Core Deinit\n");
palacios_pstate_ctrl_release();
+
}
} else if (get_cpu_var(core_state).mode==V3_PSTATE_EXTERNAL_CONTROL) {
put_cpu_var(core_state);
linux_set_pstate(p);
- }
+ } else {
+ put_cpu_var(core_state);
+ }
}
if (get_cpu_var(core_state).mode==V3_PSTATE_EXTERNAL_CONTROL) {
put_cpu_var(core_state);
linux_set_freq(p);
- }
- put_cpu_var(core_state);
+ } else {
+ put_cpu_var(core_state);
+ }
}
put_cpu_var(core_state);
ERROR("No cpufreq - cannot switch to external...\n");
return -1;
- }
+ }
put_cpu_var(core_state);
linux_setup_palacios_governor();
// The implementation would set the policy and governor to peg cpu
// regardless of load
linux_setup_palacios_governor();
+ } else {
+ put_cpu_var(core_state);
}
if (machine_state.funcs && machine_state.funcs->arch_init) {
put_cpu_var(core_state);
DEBUG("switch to internal on machine with cpu freq\n");
linux_setup_palacios_governor();
+ } else {
+ put_cpu_var(core_state);
}
get_cpu_var(core_state).mode=V3_PSTATE_INTERNAL_CONTROL;
ERROR("No cpufreq - how did we get here... external...\n");
return -1;
}
+ put_cpu_var(core_state);
DEBUG("Switching back to host control from external\n");
if (get_cpu_var(core_state).have_cpufreq) {
- linux_restore_defaults();
+ put_cpu_var(core_state);
+ linux_restore_defaults();
+ } else {
+ put_cpu_var(core_state);
}
get_cpu_var(core_state).mode = V3_PSTATE_HOST_CONTROL;
-
put_cpu_var(core_state);
return 0;
machine_state.funcs->arch_deinit();
if (get_cpu_var(core_state).have_cpufreq) {
+ put_cpu_var(core_state);
linux_restore_defaults();
+ } else {
+ put_cpu_var(core_state);
}
get_cpu_var(core_state).mode=V3_PSTATE_HOST_CONTROL;
DEBUG("Switching back to host control from internal\n");
if (get_cpu_var(core_state).have_cpufreq) {
+ put_cpu_var(core_state);
// ERROR("Unimplemented: switch from internal on machine with cpu freq - will just pretend to do so\n");
// The implementation would switch back to default policy and governor
linux_restore_defaults();
+ } else {
+ put_cpu_var(core_state);
}
get_cpu_var(core_state).mode=V3_PSTATE_HOST_CONTROL;
void palacios_pstate_ctrl_acquire(uint32_t type)
{
if (get_cpu_var(core_state).mode != V3_PSTATE_HOST_CONTROL) {
+ put_cpu_var(core_state);
palacios_pstate_ctrl_release();
+ } else {
+ put_cpu_var(core_state);
}
- put_cpu_var(core_state);
-
switch (type) {
case V3_PSTATE_EXTERNAL_CONTROL:
switch_to_external();
if (get_cpu_var(core_state).mode == V3_PSTATE_HOST_CONTROL) {
put_cpu_var(core_state);
return;
- }
+ }
+ put_cpu_var(core_state);
switch (get_cpu_var(core_state).mode) {
case V3_PSTATE_EXTERNAL_CONTROL:
+ put_cpu_var(core_state);
switch_from_external();
break;
case V3_PSTATE_DIRECT_CONTROL:
+ put_cpu_var(core_state);
switch_from_direct();
break;
case V3_PSTATE_INTERNAL_CONTROL:
+ put_cpu_var(core_state);
switch_from_internal();
break;
default:
+ put_cpu_var(core_state);
ERROR("Unknown pstate control type %u\n",core_state.mode);
break;
}
-
- put_cpu_var(core_state);
-
}
pstate_user_setup();
- pstate_register_linux_governor();
+ pstate_linux_init();
INFO("P-State Control Initialized\n");
unsigned int cpu;
unsigned int numcpus=num_online_cpus();
- pstate_unregister_linux_governor();
+ pstate_linux_deinit();
pstate_user_teardown();