I'm working on getting cpufreq (specifically PowerNow!) support
for Xen. My current approach is to have a platform hypervisor
call occur after the processor switches frequency. The platform
call adjusts the CPU's time records to a scale of the new
frequency.
This usually seems to work, but sometimes Xen seems to alter
its record of the CPU frequency in a way that is very wrong.
The bad frequency causes subsequent cpufreq changes to scale
the new frequency wrong also, and sooner or later time briefly
goes wrong. I had thought this was possibly happening in the
timer interrupt but I couldn't confirm it. Does anyone have
any suggestions on what else I need to implement?
Thanks in advance for any advice.
-Mark Langsdorf
Operating System Research Center
AMD
diff -r a70de77dd8d3 arch/i386/kernel/time-xen.c
--- a/arch/i386/kernel/time-xen.c Tue Jul 10 10:18:24 2007 +0100
+++ b/arch/i386/kernel/time-xen.c Fri Aug 03 15:01:00 2007 -0500
@@ -50,6 +50,7 @@
#include <linux/percpu.h>
#include <linux/kernel_stat.h>
#include <linux/posix-timers.h>
+#include <linux/cpufreq.h>
#include <asm/io.h>
#include <asm/smp.h>
@@ -1118,6 +1122,74 @@ void local_teardown_timer(unsigned int c
BUG_ON(cpu == 0);
unbind_from_irqhandler(per_cpu(timer_irq, cpu), NULL);
}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+/*
+ * cpufreq scaling handling
+ */
+static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ struct vcpu_time_info *info = &vcpu_info(freq->cpu)->time;
+ struct xen_platform_op op;
+
+ if (cpu_has(&cpu_data[freq->cpu], X86_FEATURE_CONSTANT_TSC))
+ return 0;
+
+ if (val == CPUFREQ_PRECHANGE)
+ return 0;
+
+ /* re order time per Ian's instructions */
+ op.cmd = XENPF_change_freq;
+ op.u.change_freq.info = info;
+ op.u.change_freq.old = freq->old;
+ op.u.change_freq.new = freq->new;
+ HYPERVISOR_platform_op(&op);
+ update_wallclock();
+
+ return 0;
+}
+
+static struct notifier_block time_cpufreq_notifier_block = {
+ .notifier_call = time_cpufreq_notifier
+};
+
+static int __init cpufreq_time_setup(void)
+{
+ if (!cpufreq_register_notifier(&time_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER)) {
+ printk(KERN_ERR "failed to set up cpufreq notifier\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+core_initcall(cpufreq_time_setup);
#endif
/*
diff -r 5682f899c7ae xen/arch/x86/platform_hypercall.c
--- a/xen/arch/x86/platform_hypercall.c Fri Jul 27 09:06:58 2007 +0100
+++ b/xen/arch/x86/platform_hypercall.c Fri Aug 03 14:51:11 2007 -0500
@@ -252,6 +252,13 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xe
ret = acpi_enter_sleep(&op->u.enter_acpi_sleep);
break;
+ case XENPF_change_freq:
+ {
+ do_change_freq((struct vcpu_time_info *) op->u.change_freq.info,
op->u.change_freq.old, op->u.change_freq.new);
+ ret = 0;
+ }
+ break;
+
default:
ret = -ENOSYS;
break;
diff -r 5682f899c7ae xen/arch/x86/time.c
--- a/xen/arch/x86/time.c Fri Jul 27 09:06:58 2007 +0100
+++ b/xen/arch/x86/time.c Fri Aug 03 14:51:11 2007 -0500
@@ -723,6 +723,35 @@ void update_domain_wallclock_time(struct
spin_unlock(&wc_lock);
}
+void do_change_freq(struct vcpu_time_info *info, unsigned int old, unsigned
int new)
+{
+ u64 new_mult;
+ s8 new_shift;
+ struct cpu_time *t;
+
+ t = &this_cpu(cpu_time);
+ new_mult = info->tsc_to_system_mul;
+ do_div(new_mult, new / 1000);
+ new_mult *= (old / 1000);
+ new_shift = info->tsc_shift;
+ while (new_mult > (1LL <<32)) {
+ new_shift += 1;
+ new_mult = new_mult >> 1;
+ }
+ while (new_mult < (1LL << 31)) {
+ new_shift -= 1;
+ new_mult = new_mult << 1;
+ }
+ version_update_begin(&info->version);
+ info->tsc_timestamp = t->local_tsc_stamp;
+ info->system_time = t->stime_local_stamp;
+ info->tsc_to_system_mul = new_mult;
+ t->tsc_scale.mul_frac = new_mult;
+ t->tsc_scale.shift = new_shift;
+ info->tsc_shift = new_shift;
+ version_update_end(&info->version);
+}
+
/* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
void do_settime(unsigned long secs, unsigned long nsecs, u64 system_time_base)
{
diff -r 5682f899c7ae xen/arch/x86/traps.c
--- a/xen/arch/x86/traps.c Fri Jul 27 09:06:58 2007 +0100
+++ b/xen/arch/x86/traps.c Fri Aug 03 14:51:11 2007 -0500
@@ -1724,6 +1724,15 @@ static int emulate_privileged_op(struct
((u64)regs->edx << 32) | regs->eax;
break;
#endif
+ case MSR_K8_FIDVID_STATUS:
+ case MSR_K8_FIDVID_CTL:
+ if ( IS_COMPAT(v->domain) )
+ goto fail;
+ if ( wrmsr_safe(regs->ecx, regs->eax, regs->edx) )
+ goto fail;
+ v->arch.guest_context.gs_base_user =
+ ((u64)regs->edx << 32) | regs->eax;
+ break;
default:
if ( wrmsr_hypervisor_regs(regs->ecx, regs->eax, regs->edx) )
break;
@@ -1760,6 +1769,13 @@ static int emulate_privileged_op(struct
regs->edx = v->arch.guest_context.gs_base_user >> 32;
break;
#endif
+ case MSR_K8_FIDVID_CTL:
+ case MSR_K8_FIDVID_STATUS:
+ if ( IS_COMPAT(v->domain) )
+ goto fail;
+ if ( rdmsr_safe(regs->ecx, regs->eax, regs->edx) )
+ goto fail;
+ break;
case MSR_EFER:
if ( rdmsr_safe(regs->ecx, regs->eax, regs->edx) )
goto fail;
diff -r 5682f899c7ae xen/include/asm-x86/msr.h
--- a/xen/include/asm-x86/msr.h Fri Jul 27 09:06:58 2007 +0100
+++ b/xen/include/asm-x86/msr.h Fri Aug 03 14:51:11 2007 -0500
@@ -352,6 +352,9 @@ static inline void write_efer(__u64 val)
#define MSR_K8_VM_CR 0xC0010114
#define MSR_K8_VM_HSAVE_PA 0xC0010117
+#define MSR_K8_FIDVID_CTL 0xC0010041
+#define MSR_K8_FIDVID_STATUS 0xC0010042
+
/* MSR_K8_VM_CR bits: */
#define _K8_VMCR_SVME_DISABLE 4
#define K8_VMCR_SVME_DISABLE (1 << _K8_VMCR_SVME_DISABLE)
diff -r 5682f899c7ae xen/include/public/platform.h
--- a/xen/include/public/platform.h Fri Jul 27 09:06:58 2007 +0100
+++ b/xen/include/public/platform.h Fri Aug 03 14:51:11 2007 -0500
@@ -164,6 +164,16 @@ typedef struct xenpf_enter_acpi_sleep xe
typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t;
DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t);
+#define XENPF_change_freq 52
+struct xenpf_change_freq {
+ /* IN variables */
+ struct vcpu_time_info *info; /* vcpu time info for changing vcpu */
+ uint32_t old; /* original frequency */
+ uint32_t new; /* new frequency */
+};
+typedef struct xenpf_change_freq xenpf_change_freq_t;
+DEFINE_XEN_GUEST_HANDLE(xenpf_change_freq_t);
+
struct xen_platform_op {
uint32_t cmd;
uint32_t interface_version; /* XENPF_INTERFACE_VERSION */
@@ -176,6 +186,7 @@ struct xen_platform_op {
struct xenpf_platform_quirk platform_quirk;
struct xenpf_firmware_info firmware_info;
struct xenpf_enter_acpi_sleep enter_acpi_sleep;
+ struct xenpf_change_freq change_freq;
uint8_t pad[128];
} u;
};
diff -r 5682f899c7ae xen/include/xen/time.h
--- a/xen/include/xen/time.h Fri Jul 27 09:06:58 2007 +0100
+++ b/xen/include/xen/time.h Fri Aug 03 14:51:11 2007 -0500
@@ -74,6 +74,8 @@ extern void do_settime(
extern void do_settime(
unsigned long secs, unsigned long nsecs, u64 system_time_base);
+extern void do_change_freq(struct vcpu_time_info *info, unsigned int old,
unsigned int new);
+
extern void send_timer_event(struct vcpu *v);
#endif /* __XEN_TIME_H__ */
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|