diff options
author | K. Y. Srinivasan <kys@microsoft.com> | 2015-08-05 03:52:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-08-05 14:44:29 -0400 |
commit | ca9357bd26c2f8e7b909321eedd651f52cc30d04 (patch) | |
tree | f9417defaed1252c6bc0442285314ee9ca777258 /drivers/hv | |
parent | bc609cb47fb2e74654e23cef0a1d4db38b6570a3 (diff) |
Drivers: hv: vmbus: Implement a clocksource based on the TSC page
The current Hyper-V clock source is based on the per-partition reference counter
and this counter is being accessed via s synthetic MSR - HV_X64_MSR_TIME_REF_COUNT.
Hyper-V has a more efficient way of computing the per-partition reference
counter value that does not involve reading a synthetic MSR. We implement
a time source based on this mechanism.
Tested-by: Vivek Yadav <vyadav@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv')
-rw-r--r-- | drivers/hv/hv.c | 83 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 9 |
2 files changed, 92 insertions, 0 deletions
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index c641faf92e60..6341be8739ae 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c | |||
@@ -133,6 +133,56 @@ static u64 do_hypercall(u64 control, void *input, void *output) | |||
133 | #endif /* !x86_64 */ | 133 | #endif /* !x86_64 */ |
134 | } | 134 | } |
135 | 135 | ||
136 | #ifdef CONFIG_X86_64 | ||
137 | static cycle_t read_hv_clock_tsc(struct clocksource *arg) | ||
138 | { | ||
139 | cycle_t current_tick; | ||
140 | struct ms_hyperv_tsc_page *tsc_pg = hv_context.tsc_page; | ||
141 | |||
142 | if (tsc_pg->tsc_sequence != -1) { | ||
143 | /* | ||
144 | * Use the tsc page to compute the value. | ||
145 | */ | ||
146 | |||
147 | while (1) { | ||
148 | cycle_t tmp; | ||
149 | u32 sequence = tsc_pg->tsc_sequence; | ||
150 | u64 cur_tsc; | ||
151 | u64 scale = tsc_pg->tsc_scale; | ||
152 | s64 offset = tsc_pg->tsc_offset; | ||
153 | |||
154 | rdtscll(cur_tsc); | ||
155 | /* current_tick = ((cur_tsc *scale) >> 64) + offset */ | ||
156 | asm("mulq %3" | ||
157 | : "=d" (current_tick), "=a" (tmp) | ||
158 | : "a" (cur_tsc), "r" (scale)); | ||
159 | |||
160 | current_tick += offset; | ||
161 | if (tsc_pg->tsc_sequence == sequence) | ||
162 | return current_tick; | ||
163 | |||
164 | if (tsc_pg->tsc_sequence != -1) | ||
165 | continue; | ||
166 | /* | ||
167 | * Fallback using MSR method. | ||
168 | */ | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); | ||
173 | return current_tick; | ||
174 | } | ||
175 | |||
176 | static struct clocksource hyperv_cs_tsc = { | ||
177 | .name = "hyperv_clocksource_tsc_page", | ||
178 | .rating = 425, | ||
179 | .read = read_hv_clock_tsc, | ||
180 | .mask = CLOCKSOURCE_MASK(64), | ||
181 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
182 | }; | ||
183 | #endif | ||
184 | |||
185 | |||
136 | /* | 186 | /* |
137 | * hv_init - Main initialization routine. | 187 | * hv_init - Main initialization routine. |
138 | * | 188 | * |
@@ -142,7 +192,9 @@ int hv_init(void) | |||
142 | { | 192 | { |
143 | int max_leaf; | 193 | int max_leaf; |
144 | union hv_x64_msr_hypercall_contents hypercall_msr; | 194 | union hv_x64_msr_hypercall_contents hypercall_msr; |
195 | union hv_x64_msr_hypercall_contents tsc_msr; | ||
145 | void *virtaddr = NULL; | 196 | void *virtaddr = NULL; |
197 | void *va_tsc = NULL; | ||
146 | 198 | ||
147 | memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); | 199 | memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); |
148 | memset(hv_context.synic_message_page, 0, | 200 | memset(hv_context.synic_message_page, 0, |
@@ -186,6 +238,22 @@ int hv_init(void) | |||
186 | 238 | ||
187 | hv_context.hypercall_page = virtaddr; | 239 | hv_context.hypercall_page = virtaddr; |
188 | 240 | ||
241 | #ifdef CONFIG_X86_64 | ||
242 | if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { | ||
243 | va_tsc = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL); | ||
244 | if (!va_tsc) | ||
245 | goto cleanup; | ||
246 | hv_context.tsc_page = va_tsc; | ||
247 | |||
248 | rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); | ||
249 | |||
250 | tsc_msr.enable = 1; | ||
251 | tsc_msr.guest_physical_address = vmalloc_to_pfn(va_tsc); | ||
252 | |||
253 | wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); | ||
254 | clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); | ||
255 | } | ||
256 | #endif | ||
189 | return 0; | 257 | return 0; |
190 | 258 | ||
191 | cleanup: | 259 | cleanup: |
@@ -219,6 +287,21 @@ void hv_cleanup(void) | |||
219 | vfree(hv_context.hypercall_page); | 287 | vfree(hv_context.hypercall_page); |
220 | hv_context.hypercall_page = NULL; | 288 | hv_context.hypercall_page = NULL; |
221 | } | 289 | } |
290 | |||
291 | #ifdef CONFIG_X86_64 | ||
292 | /* | ||
293 | * Cleanup the TSC page based CS. | ||
294 | */ | ||
295 | if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { | ||
296 | clocksource_change_rating(&hyperv_cs_tsc, 10); | ||
297 | clocksource_unregister(&hyperv_cs_tsc); | ||
298 | |||
299 | hypercall_msr.as_uint64 = 0; | ||
300 | wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); | ||
301 | vfree(hv_context.tsc_page); | ||
302 | hv_context.tsc_page = NULL; | ||
303 | } | ||
304 | #endif | ||
222 | } | 305 | } |
223 | 306 | ||
224 | /* | 307 | /* |
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 6f258255ac94..3d70e36c918e 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h | |||
@@ -517,6 +517,7 @@ struct hv_context { | |||
517 | u64 guestid; | 517 | u64 guestid; |
518 | 518 | ||
519 | void *hypercall_page; | 519 | void *hypercall_page; |
520 | void *tsc_page; | ||
520 | 521 | ||
521 | bool synic_initialized; | 522 | bool synic_initialized; |
522 | 523 | ||
@@ -560,6 +561,14 @@ struct hv_context { | |||
560 | 561 | ||
561 | extern struct hv_context hv_context; | 562 | extern struct hv_context hv_context; |
562 | 563 | ||
564 | struct ms_hyperv_tsc_page { | ||
565 | volatile u32 tsc_sequence; | ||
566 | u32 reserved1; | ||
567 | volatile u64 tsc_scale; | ||
568 | volatile s64 tsc_offset; | ||
569 | u64 reserved2[509]; | ||
570 | }; | ||
571 | |||
563 | struct hv_ring_buffer_debug_info { | 572 | struct hv_ring_buffer_debug_info { |
564 | u32 current_interrupt_mask; | 573 | u32 current_interrupt_mask; |
565 | u32 current_read_index; | 574 | u32 current_read_index; |