aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hv
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2015-08-05 03:52:42 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-08-05 14:44:29 -0400
commitca9357bd26c2f8e7b909321eedd651f52cc30d04 (patch)
treef9417defaed1252c6bc0442285314ee9ca777258 /drivers/hv
parentbc609cb47fb2e74654e23cef0a1d4db38b6570a3 (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.c83
-rw-r--r--drivers/hv/hyperv_vmbus.h9
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
137static 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
176static 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
191cleanup: 259cleanup:
@@ -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
561extern struct hv_context hv_context; 562extern struct hv_context hv_context;
562 563
564struct 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
563struct hv_ring_buffer_debug_info { 572struct 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;