diff options
author | Jeremy Fitzhardinge <jeremy@xensource.com> | 2007-07-17 21:37:04 -0400 |
---|---|---|
committer | Jeremy Fitzhardinge <jeremy@goop.org> | 2007-07-18 11:47:42 -0400 |
commit | 688340ea34c61ad12473ccd837325b59aada9a93 (patch) | |
tree | 2862f4dca8d47fc4e6ecfaba2243d813344e3cd2 | |
parent | d572929cdd12a60732c3522f7cf011bfa29165cf (diff) |
Add a sched_clock paravirt_op
The tsc-based get_scheduled_cycles interface is not a good match for
Xen's runstate accounting, which reports everything in nanoseconds.
This patch replaces this interface with a sched_clock interface, which
matches both Xen and VMI's requirements.
In order to do this, we:
1. replace get_scheduled_cycles with sched_clock
2. hoist cycles_2_ns into a common header
3. update vmi accordingly
One thing to note: because sched_clock is implemented as a weak
function in kernel/sched.c, we must define a real function in order to
override this weak binding. This means the usual paravirt_ops
technique of using an inline function won't work in this case.
Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
Cc: Zachary Amsden <zach@vmware.com>
Cc: Dan Hecht <dhecht@vmware.com>
Cc: john stultz <johnstul@us.ibm.com>
-rw-r--r-- | arch/i386/kernel/paravirt.c | 2 | ||||
-rw-r--r-- | arch/i386/kernel/tsc.c | 23 | ||||
-rw-r--r-- | arch/i386/kernel/vmi.c | 2 | ||||
-rw-r--r-- | arch/i386/kernel/vmiclock.c | 6 | ||||
-rw-r--r-- | include/asm-i386/paravirt.h | 7 | ||||
-rw-r--r-- | include/asm-i386/timer.h | 32 | ||||
-rw-r--r-- | include/asm-i386/vmi_time.h | 2 |
7 files changed, 57 insertions, 17 deletions
diff --git a/arch/i386/kernel/paravirt.c b/arch/i386/kernel/paravirt.c index 60e08b9b50a4..53f07a8275e3 100644 --- a/arch/i386/kernel/paravirt.c +++ b/arch/i386/kernel/paravirt.c | |||
@@ -302,7 +302,7 @@ struct paravirt_ops paravirt_ops = { | |||
302 | .write_msr = native_write_msr_safe, | 302 | .write_msr = native_write_msr_safe, |
303 | .read_tsc = native_read_tsc, | 303 | .read_tsc = native_read_tsc, |
304 | .read_pmc = native_read_pmc, | 304 | .read_pmc = native_read_pmc, |
305 | .get_scheduled_cycles = native_read_tsc, | 305 | .sched_clock = native_sched_clock, |
306 | .get_cpu_khz = native_calculate_cpu_khz, | 306 | .get_cpu_khz = native_calculate_cpu_khz, |
307 | .load_tr_desc = native_load_tr_desc, | 307 | .load_tr_desc = native_load_tr_desc, |
308 | .set_ldt = native_set_ldt, | 308 | .set_ldt = native_set_ldt, |
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index ea63a30ca3e8..252f9010f283 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c | |||
@@ -84,7 +84,7 @@ static inline int check_tsc_unstable(void) | |||
84 | * | 84 | * |
85 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" | 85 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" |
86 | */ | 86 | */ |
87 | static unsigned long cyc2ns_scale __read_mostly; | 87 | unsigned long cyc2ns_scale __read_mostly; |
88 | 88 | ||
89 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ | 89 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ |
90 | 90 | ||
@@ -93,15 +93,10 @@ static inline void set_cyc2ns_scale(unsigned long cpu_khz) | |||
93 | cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; | 93 | cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; |
94 | } | 94 | } |
95 | 95 | ||
96 | static inline unsigned long long cycles_2_ns(unsigned long long cyc) | ||
97 | { | ||
98 | return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; | ||
99 | } | ||
100 | |||
101 | /* | 96 | /* |
102 | * Scheduler clock - returns current time in nanosec units. | 97 | * Scheduler clock - returns current time in nanosec units. |
103 | */ | 98 | */ |
104 | unsigned long long sched_clock(void) | 99 | unsigned long long native_sched_clock(void) |
105 | { | 100 | { |
106 | unsigned long long this_offset; | 101 | unsigned long long this_offset; |
107 | 102 | ||
@@ -118,12 +113,24 @@ unsigned long long sched_clock(void) | |||
118 | return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); | 113 | return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); |
119 | 114 | ||
120 | /* read the Time Stamp Counter: */ | 115 | /* read the Time Stamp Counter: */ |
121 | get_scheduled_cycles(this_offset); | 116 | rdtscll(this_offset); |
122 | 117 | ||
123 | /* return the value in ns */ | 118 | /* return the value in ns */ |
124 | return cycles_2_ns(this_offset); | 119 | return cycles_2_ns(this_offset); |
125 | } | 120 | } |
126 | 121 | ||
122 | /* We need to define a real function for sched_clock, to override the | ||
123 | weak default version */ | ||
124 | #ifdef CONFIG_PARAVIRT | ||
125 | unsigned long long sched_clock(void) | ||
126 | { | ||
127 | return paravirt_sched_clock(); | ||
128 | } | ||
129 | #else | ||
130 | unsigned long long sched_clock(void) | ||
131 | __attribute__((alias("native_sched_clock"))); | ||
132 | #endif | ||
133 | |||
127 | unsigned long native_calculate_cpu_khz(void) | 134 | unsigned long native_calculate_cpu_khz(void) |
128 | { | 135 | { |
129 | unsigned long long start, end; | 136 | unsigned long long start, end; |
diff --git a/arch/i386/kernel/vmi.c b/arch/i386/kernel/vmi.c index 234bd6ff518d..72042bb7ec94 100644 --- a/arch/i386/kernel/vmi.c +++ b/arch/i386/kernel/vmi.c | |||
@@ -891,7 +891,7 @@ static inline int __init activate_vmi(void) | |||
891 | paravirt_ops.setup_boot_clock = vmi_time_bsp_init; | 891 | paravirt_ops.setup_boot_clock = vmi_time_bsp_init; |
892 | paravirt_ops.setup_secondary_clock = vmi_time_ap_init; | 892 | paravirt_ops.setup_secondary_clock = vmi_time_ap_init; |
893 | #endif | 893 | #endif |
894 | paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles; | 894 | paravirt_ops.sched_clock = vmi_sched_clock; |
895 | paravirt_ops.get_cpu_khz = vmi_cpu_khz; | 895 | paravirt_ops.get_cpu_khz = vmi_cpu_khz; |
896 | 896 | ||
897 | /* We have true wallclock functions; disable CMOS clock sync */ | 897 | /* We have true wallclock functions; disable CMOS clock sync */ |
diff --git a/arch/i386/kernel/vmiclock.c b/arch/i386/kernel/vmiclock.c index 26a37f8a8762..f9b845f4e692 100644 --- a/arch/i386/kernel/vmiclock.c +++ b/arch/i386/kernel/vmiclock.c | |||
@@ -64,10 +64,10 @@ int vmi_set_wallclock(unsigned long now) | |||
64 | return 0; | 64 | return 0; |
65 | } | 65 | } |
66 | 66 | ||
67 | /* paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles */ | 67 | /* paravirt_ops.sched_clock = vmi_sched_clock */ |
68 | unsigned long long vmi_get_sched_cycles(void) | 68 | unsigned long long vmi_sched_clock(void) |
69 | { | 69 | { |
70 | return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE); | 70 | return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE)); |
71 | } | 71 | } |
72 | 72 | ||
73 | /* paravirt_ops.get_cpu_khz = vmi_cpu_khz */ | 73 | /* paravirt_ops.get_cpu_khz = vmi_cpu_khz */ |
diff --git a/include/asm-i386/paravirt.h b/include/asm-i386/paravirt.h index 690ada22437a..7df88be2dd9e 100644 --- a/include/asm-i386/paravirt.h +++ b/include/asm-i386/paravirt.h | |||
@@ -118,7 +118,7 @@ struct paravirt_ops | |||
118 | 118 | ||
119 | u64 (*read_tsc)(void); | 119 | u64 (*read_tsc)(void); |
120 | u64 (*read_pmc)(void); | 120 | u64 (*read_pmc)(void); |
121 | u64 (*get_scheduled_cycles)(void); | 121 | unsigned long long (*sched_clock)(void); |
122 | unsigned long (*get_cpu_khz)(void); | 122 | unsigned long (*get_cpu_khz)(void); |
123 | 123 | ||
124 | /* Segment descriptor handling */ | 124 | /* Segment descriptor handling */ |
@@ -566,7 +566,10 @@ static inline u64 paravirt_read_tsc(void) | |||
566 | 566 | ||
567 | #define rdtscll(val) (val = paravirt_read_tsc()) | 567 | #define rdtscll(val) (val = paravirt_read_tsc()) |
568 | 568 | ||
569 | #define get_scheduled_cycles(val) (val = paravirt_ops.get_scheduled_cycles()) | 569 | static inline unsigned long long paravirt_sched_clock(void) |
570 | { | ||
571 | return PVOP_CALL0(unsigned long long, sched_clock); | ||
572 | } | ||
570 | #define calculate_cpu_khz() (paravirt_ops.get_cpu_khz()) | 573 | #define calculate_cpu_khz() (paravirt_ops.get_cpu_khz()) |
571 | 574 | ||
572 | #define write_tsc(val1,val2) wrmsr(0x10, val1, val2) | 575 | #define write_tsc(val1,val2) wrmsr(0x10, val1, val2) |
diff --git a/include/asm-i386/timer.h b/include/asm-i386/timer.h index 153770e25faa..51a713e33a9e 100644 --- a/include/asm-i386/timer.h +++ b/include/asm-i386/timer.h | |||
@@ -15,8 +15,38 @@ extern int no_sync_cmos_clock; | |||
15 | extern int recalibrate_cpu_khz(void); | 15 | extern int recalibrate_cpu_khz(void); |
16 | 16 | ||
17 | #ifndef CONFIG_PARAVIRT | 17 | #ifndef CONFIG_PARAVIRT |
18 | #define get_scheduled_cycles(val) rdtscll(val) | ||
19 | #define calculate_cpu_khz() native_calculate_cpu_khz() | 18 | #define calculate_cpu_khz() native_calculate_cpu_khz() |
20 | #endif | 19 | #endif |
21 | 20 | ||
21 | /* Accellerators for sched_clock() | ||
22 | * convert from cycles(64bits) => nanoseconds (64bits) | ||
23 | * basic equation: | ||
24 | * ns = cycles / (freq / ns_per_sec) | ||
25 | * ns = cycles * (ns_per_sec / freq) | ||
26 | * ns = cycles * (10^9 / (cpu_khz * 10^3)) | ||
27 | * ns = cycles * (10^6 / cpu_khz) | ||
28 | * | ||
29 | * Then we use scaling math (suggested by george@mvista.com) to get: | ||
30 | * ns = cycles * (10^6 * SC / cpu_khz) / SC | ||
31 | * ns = cycles * cyc2ns_scale / SC | ||
32 | * | ||
33 | * And since SC is a constant power of two, we can convert the div | ||
34 | * into a shift. | ||
35 | * | ||
36 | * We can use khz divisor instead of mhz to keep a better percision, since | ||
37 | * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits. | ||
38 | * (mathieu.desnoyers@polymtl.ca) | ||
39 | * | ||
40 | * -johnstul@us.ibm.com "math is hard, lets go shopping!" | ||
41 | */ | ||
42 | extern unsigned long cyc2ns_scale __read_mostly; | ||
43 | |||
44 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ | ||
45 | |||
46 | static inline unsigned long long cycles_2_ns(unsigned long long cyc) | ||
47 | { | ||
48 | return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; | ||
49 | } | ||
50 | |||
51 | |||
22 | #endif | 52 | #endif |
diff --git a/include/asm-i386/vmi_time.h b/include/asm-i386/vmi_time.h index 213930b995cb..478188130328 100644 --- a/include/asm-i386/vmi_time.h +++ b/include/asm-i386/vmi_time.h | |||
@@ -49,7 +49,7 @@ extern struct vmi_timer_ops { | |||
49 | extern void __init vmi_time_init(void); | 49 | extern void __init vmi_time_init(void); |
50 | extern unsigned long vmi_get_wallclock(void); | 50 | extern unsigned long vmi_get_wallclock(void); |
51 | extern int vmi_set_wallclock(unsigned long now); | 51 | extern int vmi_set_wallclock(unsigned long now); |
52 | extern unsigned long long vmi_get_sched_cycles(void); | 52 | extern unsigned long long vmi_sched_clock(void); |
53 | extern unsigned long vmi_cpu_khz(void); | 53 | extern unsigned long vmi_cpu_khz(void); |
54 | 54 | ||
55 | #ifdef CONFIG_X86_LOCAL_APIC | 55 | #ifdef CONFIG_X86_LOCAL_APIC |