diff options
author | Vitaly Kuznetsov <vkuznets@redhat.com> | 2017-03-03 08:21:42 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2017-03-11 08:47:28 -0500 |
commit | 90b20432aeb850ef84086a72893cd9411479d896 (patch) | |
tree | 507f4ef6a4098f8f8919f625d57bc464170cefe7 | |
parent | 0733379b512ce36ba0b10942f9597b74f579f063 (diff) |
x86/vdso: Add VCLOCK_HVCLOCK vDSO clock read method
Hyper-V TSC page clocksource is suitable for vDSO, however, the protocol
defined by the hypervisor is different from VCLOCK_PVCLOCK. Implement the
required support by adding hvclock_page VVAR.
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: Stephen Hemminger <sthemmin@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Dexuan Cui <decui@microsoft.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: devel@linuxdriverproject.org
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: virtualization@lists.linux-foundation.org
Link: http://lkml.kernel.org/r/20170303132142.25595-4-vkuznets@redhat.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/entry/vdso/vclock_gettime.c | 24 | ||||
-rw-r--r-- | arch/x86/entry/vdso/vdso-layout.lds.S | 3 | ||||
-rw-r--r-- | arch/x86/entry/vdso/vdso2c.c | 3 | ||||
-rw-r--r-- | arch/x86/entry/vdso/vma.c | 7 | ||||
-rw-r--r-- | arch/x86/hyperv/hv_init.c | 3 | ||||
-rw-r--r-- | arch/x86/include/asm/clocksource.h | 3 | ||||
-rw-r--r-- | arch/x86/include/asm/vdso.h | 1 |
7 files changed, 42 insertions, 2 deletions
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c index 9d4d6e138311..fa8dbfcf7ed3 100644 --- a/arch/x86/entry/vdso/vclock_gettime.c +++ b/arch/x86/entry/vdso/vclock_gettime.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <asm/unistd.h> | 17 | #include <asm/unistd.h> |
18 | #include <asm/msr.h> | 18 | #include <asm/msr.h> |
19 | #include <asm/pvclock.h> | 19 | #include <asm/pvclock.h> |
20 | #include <asm/mshyperv.h> | ||
20 | #include <linux/math64.h> | 21 | #include <linux/math64.h> |
21 | #include <linux/time.h> | 22 | #include <linux/time.h> |
22 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
@@ -32,6 +33,11 @@ extern u8 pvclock_page | |||
32 | __attribute__((visibility("hidden"))); | 33 | __attribute__((visibility("hidden"))); |
33 | #endif | 34 | #endif |
34 | 35 | ||
36 | #ifdef CONFIG_HYPERV_TSCPAGE | ||
37 | extern u8 hvclock_page | ||
38 | __attribute__((visibility("hidden"))); | ||
39 | #endif | ||
40 | |||
35 | #ifndef BUILD_VDSO32 | 41 | #ifndef BUILD_VDSO32 |
36 | 42 | ||
37 | notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) | 43 | notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) |
@@ -141,6 +147,20 @@ static notrace u64 vread_pvclock(int *mode) | |||
141 | return last; | 147 | return last; |
142 | } | 148 | } |
143 | #endif | 149 | #endif |
150 | #ifdef CONFIG_HYPERV_TSCPAGE | ||
151 | static notrace u64 vread_hvclock(int *mode) | ||
152 | { | ||
153 | const struct ms_hyperv_tsc_page *tsc_pg = | ||
154 | (const struct ms_hyperv_tsc_page *)&hvclock_page; | ||
155 | u64 current_tick = hv_read_tsc_page(tsc_pg); | ||
156 | |||
157 | if (current_tick != U64_MAX) | ||
158 | return current_tick; | ||
159 | |||
160 | *mode = VCLOCK_NONE; | ||
161 | return 0; | ||
162 | } | ||
163 | #endif | ||
144 | 164 | ||
145 | notrace static u64 vread_tsc(void) | 165 | notrace static u64 vread_tsc(void) |
146 | { | 166 | { |
@@ -173,6 +193,10 @@ notrace static inline u64 vgetsns(int *mode) | |||
173 | else if (gtod->vclock_mode == VCLOCK_PVCLOCK) | 193 | else if (gtod->vclock_mode == VCLOCK_PVCLOCK) |
174 | cycles = vread_pvclock(mode); | 194 | cycles = vread_pvclock(mode); |
175 | #endif | 195 | #endif |
196 | #ifdef CONFIG_HYPERV_TSCPAGE | ||
197 | else if (gtod->vclock_mode == VCLOCK_HVCLOCK) | ||
198 | cycles = vread_hvclock(mode); | ||
199 | #endif | ||
176 | else | 200 | else |
177 | return 0; | 201 | return 0; |
178 | v = (cycles - gtod->cycle_last) & gtod->mask; | 202 | v = (cycles - gtod->cycle_last) & gtod->mask; |
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S index a708aa90b507..8ebb4b6454fe 100644 --- a/arch/x86/entry/vdso/vdso-layout.lds.S +++ b/arch/x86/entry/vdso/vdso-layout.lds.S | |||
@@ -25,7 +25,7 @@ SECTIONS | |||
25 | * segment. | 25 | * segment. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | vvar_start = . - 2 * PAGE_SIZE; | 28 | vvar_start = . - 3 * PAGE_SIZE; |
29 | vvar_page = vvar_start; | 29 | vvar_page = vvar_start; |
30 | 30 | ||
31 | /* Place all vvars at the offsets in asm/vvar.h. */ | 31 | /* Place all vvars at the offsets in asm/vvar.h. */ |
@@ -36,6 +36,7 @@ SECTIONS | |||
36 | #undef EMIT_VVAR | 36 | #undef EMIT_VVAR |
37 | 37 | ||
38 | pvclock_page = vvar_start + PAGE_SIZE; | 38 | pvclock_page = vvar_start + PAGE_SIZE; |
39 | hvclock_page = vvar_start + 2 * PAGE_SIZE; | ||
39 | 40 | ||
40 | . = SIZEOF_HEADERS; | 41 | . = SIZEOF_HEADERS; |
41 | 42 | ||
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c index 491020b2826d..0780a443a53b 100644 --- a/arch/x86/entry/vdso/vdso2c.c +++ b/arch/x86/entry/vdso/vdso2c.c | |||
@@ -74,6 +74,7 @@ enum { | |||
74 | sym_vvar_page, | 74 | sym_vvar_page, |
75 | sym_hpet_page, | 75 | sym_hpet_page, |
76 | sym_pvclock_page, | 76 | sym_pvclock_page, |
77 | sym_hvclock_page, | ||
77 | sym_VDSO_FAKE_SECTION_TABLE_START, | 78 | sym_VDSO_FAKE_SECTION_TABLE_START, |
78 | sym_VDSO_FAKE_SECTION_TABLE_END, | 79 | sym_VDSO_FAKE_SECTION_TABLE_END, |
79 | }; | 80 | }; |
@@ -82,6 +83,7 @@ const int special_pages[] = { | |||
82 | sym_vvar_page, | 83 | sym_vvar_page, |
83 | sym_hpet_page, | 84 | sym_hpet_page, |
84 | sym_pvclock_page, | 85 | sym_pvclock_page, |
86 | sym_hvclock_page, | ||
85 | }; | 87 | }; |
86 | 88 | ||
87 | struct vdso_sym { | 89 | struct vdso_sym { |
@@ -94,6 +96,7 @@ struct vdso_sym required_syms[] = { | |||
94 | [sym_vvar_page] = {"vvar_page", true}, | 96 | [sym_vvar_page] = {"vvar_page", true}, |
95 | [sym_hpet_page] = {"hpet_page", true}, | 97 | [sym_hpet_page] = {"hpet_page", true}, |
96 | [sym_pvclock_page] = {"pvclock_page", true}, | 98 | [sym_pvclock_page] = {"pvclock_page", true}, |
99 | [sym_hvclock_page] = {"hvclock_page", true}, | ||
97 | [sym_VDSO_FAKE_SECTION_TABLE_START] = { | 100 | [sym_VDSO_FAKE_SECTION_TABLE_START] = { |
98 | "VDSO_FAKE_SECTION_TABLE_START", false | 101 | "VDSO_FAKE_SECTION_TABLE_START", false |
99 | }, | 102 | }, |
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c index 226ca70dc6bd..faf80fdeeacc 100644 --- a/arch/x86/entry/vdso/vma.c +++ b/arch/x86/entry/vdso/vma.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/page.h> | 22 | #include <asm/page.h> |
23 | #include <asm/desc.h> | 23 | #include <asm/desc.h> |
24 | #include <asm/cpufeature.h> | 24 | #include <asm/cpufeature.h> |
25 | #include <asm/mshyperv.h> | ||
25 | 26 | ||
26 | #if defined(CONFIG_X86_64) | 27 | #if defined(CONFIG_X86_64) |
27 | unsigned int __read_mostly vdso64_enabled = 1; | 28 | unsigned int __read_mostly vdso64_enabled = 1; |
@@ -121,6 +122,12 @@ static int vvar_fault(const struct vm_special_mapping *sm, | |||
121 | vmf->address, | 122 | vmf->address, |
122 | __pa(pvti) >> PAGE_SHIFT); | 123 | __pa(pvti) >> PAGE_SHIFT); |
123 | } | 124 | } |
125 | } else if (sym_offset == image->sym_hvclock_page) { | ||
126 | struct ms_hyperv_tsc_page *tsc_pg = hv_get_tsc_page(); | ||
127 | |||
128 | if (tsc_pg && vclock_was_used(VCLOCK_HVCLOCK)) | ||
129 | ret = vm_insert_pfn(vma, vmf->address, | ||
130 | vmalloc_to_pfn(tsc_pg)); | ||
124 | } | 131 | } |
125 | 132 | ||
126 | if (ret == 0 || ret == -EBUSY) | 133 | if (ret == 0 || ret == -EBUSY) |
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 7f5152356a59..2b01421f7d0f 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c | |||
@@ -132,6 +132,9 @@ void hyperv_init(void) | |||
132 | tsc_msr.guest_physical_address = vmalloc_to_pfn(tsc_pg); | 132 | tsc_msr.guest_physical_address = vmalloc_to_pfn(tsc_pg); |
133 | 133 | ||
134 | wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); | 134 | wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); |
135 | |||
136 | hyperv_cs_tsc.archdata.vclock_mode = VCLOCK_HVCLOCK; | ||
137 | |||
135 | clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); | 138 | clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); |
136 | return; | 139 | return; |
137 | } | 140 | } |
diff --git a/arch/x86/include/asm/clocksource.h b/arch/x86/include/asm/clocksource.h index eae33c7170c8..47bea8cadbd0 100644 --- a/arch/x86/include/asm/clocksource.h +++ b/arch/x86/include/asm/clocksource.h | |||
@@ -6,7 +6,8 @@ | |||
6 | #define VCLOCK_NONE 0 /* No vDSO clock available. */ | 6 | #define VCLOCK_NONE 0 /* No vDSO clock available. */ |
7 | #define VCLOCK_TSC 1 /* vDSO should use vread_tsc. */ | 7 | #define VCLOCK_TSC 1 /* vDSO should use vread_tsc. */ |
8 | #define VCLOCK_PVCLOCK 2 /* vDSO should use vread_pvclock. */ | 8 | #define VCLOCK_PVCLOCK 2 /* vDSO should use vread_pvclock. */ |
9 | #define VCLOCK_MAX 2 | 9 | #define VCLOCK_HVCLOCK 3 /* vDSO should use vread_hvclock. */ |
10 | #define VCLOCK_MAX 3 | ||
10 | 11 | ||
11 | struct arch_clocksource_data { | 12 | struct arch_clocksource_data { |
12 | int vclock_mode; | 13 | int vclock_mode; |
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h index 2444189cbe28..bccdf4938ddf 100644 --- a/arch/x86/include/asm/vdso.h +++ b/arch/x86/include/asm/vdso.h | |||
@@ -20,6 +20,7 @@ struct vdso_image { | |||
20 | long sym_vvar_page; | 20 | long sym_vvar_page; |
21 | long sym_hpet_page; | 21 | long sym_hpet_page; |
22 | long sym_pvclock_page; | 22 | long sym_pvclock_page; |
23 | long sym_hvclock_page; | ||
23 | long sym_VDSO32_NOTE_MASK; | 24 | long sym_VDSO32_NOTE_MASK; |
24 | long sym___kernel_sigreturn; | 25 | long sym___kernel_sigreturn; |
25 | long sym___kernel_rt_sigreturn; | 26 | long sym___kernel_rt_sigreturn; |