diff options
Diffstat (limited to 'arch/x86/kernel/pvclock.c')
-rw-r--r-- | arch/x86/kernel/pvclock.c | 143 |
1 files changed, 91 insertions, 52 deletions
diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c index 42eb3300dfc6..85c39590c1a4 100644 --- a/arch/x86/kernel/pvclock.c +++ b/arch/x86/kernel/pvclock.c | |||
@@ -17,23 +17,13 @@ | |||
17 | 17 | ||
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/percpu.h> | 19 | #include <linux/percpu.h> |
20 | #include <linux/notifier.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/gfp.h> | ||
23 | #include <linux/bootmem.h> | ||
24 | #include <asm/fixmap.h> | ||
20 | #include <asm/pvclock.h> | 25 | #include <asm/pvclock.h> |
21 | 26 | ||
22 | /* | ||
23 | * These are perodically updated | ||
24 | * xen: magic shared_info page | ||
25 | * kvm: gpa registered via msr | ||
26 | * and then copied here. | ||
27 | */ | ||
28 | struct pvclock_shadow_time { | ||
29 | u64 tsc_timestamp; /* TSC at last update of time vals. */ | ||
30 | u64 system_timestamp; /* Time, in nanosecs, since boot. */ | ||
31 | u32 tsc_to_nsec_mul; | ||
32 | int tsc_shift; | ||
33 | u32 version; | ||
34 | u8 flags; | ||
35 | }; | ||
36 | |||
37 | static u8 valid_flags __read_mostly = 0; | 27 | static u8 valid_flags __read_mostly = 0; |
38 | 28 | ||
39 | void pvclock_set_flags(u8 flags) | 29 | void pvclock_set_flags(u8 flags) |
@@ -41,34 +31,6 @@ void pvclock_set_flags(u8 flags) | |||
41 | valid_flags = flags; | 31 | valid_flags = flags; |
42 | } | 32 | } |
43 | 33 | ||
44 | static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow) | ||
45 | { | ||
46 | u64 delta = native_read_tsc() - shadow->tsc_timestamp; | ||
47 | return pvclock_scale_delta(delta, shadow->tsc_to_nsec_mul, | ||
48 | shadow->tsc_shift); | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * Reads a consistent set of time-base values from hypervisor, | ||
53 | * into a shadow data area. | ||
54 | */ | ||
55 | static unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst, | ||
56 | struct pvclock_vcpu_time_info *src) | ||
57 | { | ||
58 | do { | ||
59 | dst->version = src->version; | ||
60 | rmb(); /* fetch version before data */ | ||
61 | dst->tsc_timestamp = src->tsc_timestamp; | ||
62 | dst->system_timestamp = src->system_time; | ||
63 | dst->tsc_to_nsec_mul = src->tsc_to_system_mul; | ||
64 | dst->tsc_shift = src->tsc_shift; | ||
65 | dst->flags = src->flags; | ||
66 | rmb(); /* test version after fetching data */ | ||
67 | } while ((src->version & 1) || (dst->version != src->version)); | ||
68 | |||
69 | return dst->version; | ||
70 | } | ||
71 | |||
72 | unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src) | 34 | unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src) |
73 | { | 35 | { |
74 | u64 pv_tsc_khz = 1000000ULL << 32; | 36 | u64 pv_tsc_khz = 1000000ULL << 32; |
@@ -88,23 +50,32 @@ void pvclock_resume(void) | |||
88 | atomic64_set(&last_value, 0); | 50 | atomic64_set(&last_value, 0); |
89 | } | 51 | } |
90 | 52 | ||
53 | u8 pvclock_read_flags(struct pvclock_vcpu_time_info *src) | ||
54 | { | ||
55 | unsigned version; | ||
56 | cycle_t ret; | ||
57 | u8 flags; | ||
58 | |||
59 | do { | ||
60 | version = __pvclock_read_cycles(src, &ret, &flags); | ||
61 | } while ((src->version & 1) || version != src->version); | ||
62 | |||
63 | return flags & valid_flags; | ||
64 | } | ||
65 | |||
91 | cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src) | 66 | cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src) |
92 | { | 67 | { |
93 | struct pvclock_shadow_time shadow; | ||
94 | unsigned version; | 68 | unsigned version; |
95 | cycle_t ret, offset; | 69 | cycle_t ret; |
96 | u64 last; | 70 | u64 last; |
71 | u8 flags; | ||
97 | 72 | ||
98 | do { | 73 | do { |
99 | version = pvclock_get_time_values(&shadow, src); | 74 | version = __pvclock_read_cycles(src, &ret, &flags); |
100 | barrier(); | 75 | } while ((src->version & 1) || version != src->version); |
101 | offset = pvclock_get_nsec_offset(&shadow); | ||
102 | ret = shadow.system_timestamp + offset; | ||
103 | barrier(); | ||
104 | } while (version != src->version); | ||
105 | 76 | ||
106 | if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) && | 77 | if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) && |
107 | (shadow.flags & PVCLOCK_TSC_STABLE_BIT)) | 78 | (flags & PVCLOCK_TSC_STABLE_BIT)) |
108 | return ret; | 79 | return ret; |
109 | 80 | ||
110 | /* | 81 | /* |
@@ -156,3 +127,71 @@ void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock, | |||
156 | 127 | ||
157 | set_normalized_timespec(ts, now.tv_sec, now.tv_nsec); | 128 | set_normalized_timespec(ts, now.tv_sec, now.tv_nsec); |
158 | } | 129 | } |
130 | |||
131 | static struct pvclock_vsyscall_time_info *pvclock_vdso_info; | ||
132 | |||
133 | static struct pvclock_vsyscall_time_info * | ||
134 | pvclock_get_vsyscall_user_time_info(int cpu) | ||
135 | { | ||
136 | if (!pvclock_vdso_info) { | ||
137 | BUG(); | ||
138 | return NULL; | ||
139 | } | ||
140 | |||
141 | return &pvclock_vdso_info[cpu]; | ||
142 | } | ||
143 | |||
144 | struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info(int cpu) | ||
145 | { | ||
146 | return &pvclock_get_vsyscall_user_time_info(cpu)->pvti; | ||
147 | } | ||
148 | |||
149 | #ifdef CONFIG_X86_64 | ||
150 | static int pvclock_task_migrate(struct notifier_block *nb, unsigned long l, | ||
151 | void *v) | ||
152 | { | ||
153 | struct task_migration_notifier *mn = v; | ||
154 | struct pvclock_vsyscall_time_info *pvti; | ||
155 | |||
156 | pvti = pvclock_get_vsyscall_user_time_info(mn->from_cpu); | ||
157 | |||
158 | /* this is NULL when pvclock vsyscall is not initialized */ | ||
159 | if (unlikely(pvti == NULL)) | ||
160 | return NOTIFY_DONE; | ||
161 | |||
162 | pvti->migrate_count++; | ||
163 | |||
164 | return NOTIFY_DONE; | ||
165 | } | ||
166 | |||
167 | static struct notifier_block pvclock_migrate = { | ||
168 | .notifier_call = pvclock_task_migrate, | ||
169 | }; | ||
170 | |||
171 | /* | ||
172 | * Initialize the generic pvclock vsyscall state. This will allocate | ||
173 | * a/some page(s) for the per-vcpu pvclock information, set up a | ||
174 | * fixmap mapping for the page(s) | ||
175 | */ | ||
176 | |||
177 | int __init pvclock_init_vsyscall(struct pvclock_vsyscall_time_info *i, | ||
178 | int size) | ||
179 | { | ||
180 | int idx; | ||
181 | |||
182 | WARN_ON (size != PVCLOCK_VSYSCALL_NR_PAGES*PAGE_SIZE); | ||
183 | |||
184 | pvclock_vdso_info = i; | ||
185 | |||
186 | for (idx = 0; idx <= (PVCLOCK_FIXMAP_END-PVCLOCK_FIXMAP_BEGIN); idx++) { | ||
187 | __set_fixmap(PVCLOCK_FIXMAP_BEGIN + idx, | ||
188 | __pa_symbol(i) + (idx*PAGE_SIZE), | ||
189 | PAGE_KERNEL_VVAR); | ||
190 | } | ||
191 | |||
192 | |||
193 | register_task_migration_notifier(&pvclock_migrate); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | #endif | ||