Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

K. Y. Srinivasan and committed by
Greg Kroah-Hartman
ca9357bd bc609cb4

+94
+2
arch/x86/include/uapi/asm/hyperv.h
··· 27 27 #define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) 28 28 /* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ 29 29 #define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) 30 + /* Partition reference TSC MSR is available */ 31 + #define HV_X64_MSR_REFERENCE_TSC_AVAILABLE (1 << 9) 30 32 31 33 /* A partition's reference time stamp counter (TSC) page */ 32 34 #define HV_X64_MSR_REFERENCE_TSC 0x40000021
+83
drivers/hv/hv.c
··· 133 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 187 * hv_init - Main initialization routine. 138 188 * ··· 192 142 { 193 143 int max_leaf; 194 144 union hv_x64_msr_hypercall_contents hypercall_msr; 145 + union hv_x64_msr_hypercall_contents tsc_msr; 195 146 void *virtaddr = NULL; 147 + void *va_tsc = NULL; 196 148 197 149 memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); 198 150 memset(hv_context.synic_message_page, 0, ··· 238 186 239 187 hv_context.hypercall_page = virtaddr; 240 188 189 + #ifdef CONFIG_X86_64 190 + if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { 191 + va_tsc = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL); 192 + if (!va_tsc) 193 + goto cleanup; 194 + hv_context.tsc_page = va_tsc; 195 + 196 + rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); 197 + 198 + tsc_msr.enable = 1; 199 + tsc_msr.guest_physical_address = vmalloc_to_pfn(va_tsc); 200 + 201 + wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); 202 + clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); 203 + } 204 + #endif 241 205 return 0; 242 206 243 207 cleanup: ··· 287 219 vfree(hv_context.hypercall_page); 288 220 hv_context.hypercall_page = NULL; 289 221 } 222 + 223 + #ifdef CONFIG_X86_64 224 + /* 225 + * Cleanup the TSC page based CS. 226 + */ 227 + if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { 228 + clocksource_change_rating(&hyperv_cs_tsc, 10); 229 + clocksource_unregister(&hyperv_cs_tsc); 230 + 231 + hypercall_msr.as_uint64 = 0; 232 + wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); 233 + vfree(hv_context.tsc_page); 234 + hv_context.tsc_page = NULL; 235 + } 236 + #endif 290 237 } 291 238 292 239 /*
+9
drivers/hv/hyperv_vmbus.h
··· 517 517 u64 guestid; 518 518 519 519 void *hypercall_page; 520 + void *tsc_page; 520 521 521 522 bool synic_initialized; 522 523 ··· 560 559 }; 561 560 562 561 extern struct hv_context hv_context; 562 + 563 + struct ms_hyperv_tsc_page { 564 + volatile u32 tsc_sequence; 565 + u32 reserved1; 566 + volatile u64 tsc_scale; 567 + volatile s64 tsc_offset; 568 + u64 reserved2[509]; 569 + }; 563 570 564 571 struct hv_ring_buffer_debug_info { 565 572 u32 current_interrupt_mask;