From 1349401ff1aa425e7381ed26feb63e0d6b557fc6 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Tue, 19 Nov 2019 23:12:26 -0800 Subject: clocksource/drivers/hyper-v: Suspend/resume Hyper-V clocksource for hibernation This is needed for hibernation, e.g. when we resume the old kernel, we need to disable the "current" kernel's TSC page and then resume the old kernel's. Signed-off-by: Dexuan Cui Reviewed-by: Michael Kelley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1574233946-48377-1-git-send-email-decui@microsoft.com --- drivers/clocksource/hyperv_timer.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/clocksource/hyperv_timer.c') diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 287d8d58c21a..1aec08e82b7a 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -330,12 +330,37 @@ static u64 read_hv_sched_clock_tsc(void) return read_hv_clock_tsc(NULL) - hv_sched_clock_offset; } +static void suspend_hv_clock_tsc(struct clocksource *arg) +{ + u64 tsc_msr; + + /* Disable the TSC page */ + hv_get_reference_tsc(tsc_msr); + tsc_msr &= ~BIT_ULL(0); + hv_set_reference_tsc(tsc_msr); +} + + +static void resume_hv_clock_tsc(struct clocksource *arg) +{ + phys_addr_t phys_addr = virt_to_phys(&tsc_pg); + u64 tsc_msr; + + /* Re-enable the TSC page */ + hv_get_reference_tsc(tsc_msr); + tsc_msr &= GENMASK_ULL(11, 0); + tsc_msr |= BIT_ULL(0) | (u64)phys_addr; + hv_set_reference_tsc(tsc_msr); +} + static struct clocksource hyperv_cs_tsc = { .name = "hyperv_clocksource_tsc_page", .rating = 400, .read = read_hv_clock_tsc, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .suspend= suspend_hv_clock_tsc, + .resume = resume_hv_clock_tsc, }; static u64 notrace read_hv_clock_msr(struct clocksource *arg) -- cgit v1.2.3 From ddc61bbc45017726a2b450350d476b4dc5ae25ce Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Tue, 26 Nov 2019 10:17:20 +0800 Subject: clocksource/drivers/hyper-v: Reserve PAGE_SIZE space for tsc page Currently, the reserved size for a tsc page is 4K, which is enough for communicating with hypervisor. However, in the case where we want to export the tsc page to userspace (e.g. for vDSO to read the clocksource), the tsc page should be at least PAGE_SIZE, otherwise, when PAGE_SIZE is larger than 4K, extra kernel data will be mapped into userspace, which means leaking kernel information. Therefore reserve PAGE_SIZE space for tsc_pg as a preparation for the vDSO support of ARM64 in the future. Also, while at it, replace all reference to tsc_pg with hv_get_tsc_page() since it should be the only interface to access tsc page. Signed-off-by: Boqun Feng (Microsoft) Cc: linux-hyperv@vger.kernel.org Reviewed-by: Michael Kelley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191126021723.4710-1-boqun.feng@gmail.com --- drivers/clocksource/hyperv_timer.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/clocksource/hyperv_timer.c') diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 1aec08e82b7a..12d75b50a317 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -307,17 +307,20 @@ EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup); struct clocksource *hyperv_cs; EXPORT_SYMBOL_GPL(hyperv_cs); -static struct ms_hyperv_tsc_page tsc_pg __aligned(PAGE_SIZE); +static union { + struct ms_hyperv_tsc_page page; + u8 reserved[PAGE_SIZE]; +} tsc_pg __aligned(PAGE_SIZE); struct ms_hyperv_tsc_page *hv_get_tsc_page(void) { - return &tsc_pg; + return &tsc_pg.page; } EXPORT_SYMBOL_GPL(hv_get_tsc_page); static u64 notrace read_hv_clock_tsc(struct clocksource *arg) { - u64 current_tick = hv_read_tsc_page(&tsc_pg); + u64 current_tick = hv_read_tsc_page(hv_get_tsc_page()); if (current_tick == U64_MAX) hv_get_time_ref_count(current_tick); @@ -397,7 +400,7 @@ static bool __init hv_init_tsc_clocksource(void) return false; hyperv_cs = &hyperv_cs_tsc; - phys_addr = virt_to_phys(&tsc_pg); + phys_addr = virt_to_phys(hv_get_tsc_page()); /* * The Hyper-V TLFS specifies to preserve the value of reserved -- cgit v1.2.3 From 0af3e137c144377fbaf5025ba784ff5ba7ad40c9 Mon Sep 17 00:00:00 2001 From: Andrea Parri Date: Thu, 9 Jan 2020 17:06:49 +0100 Subject: clocksource/drivers/hyper-v: Untangle stimers and timesync from clocksources hyperv_timer.c exports hyperv_cs, which is used by stimers and the timesync mechanism. However, the clocksource dependency is not needed: these mechanisms only depend on the partition reference counter (which can be read via a MSR or via the TSC Reference Page). Introduce the (function) pointer hv_read_reference_counter, as an embodiment of the partition reference counter read, and export it in place of the hyperv_cs pointer. The latter can be removed. This should clarify that there's no relationship between Hyper-V stimers & timesync and the Linux clocksource abstractions. No functional or semantic change. Suggested-by: Michael Kelley Signed-off-by: Andrea Parri Reviewed-by: Michael Kelley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200109160650.16150-2-parri.andrea@gmail.com --- drivers/clocksource/hyperv_timer.c | 36 +++++++++++++++++++++++------------- drivers/hv/hv_util.c | 8 ++++---- include/clocksource/hyperv_timer.h | 2 +- 3 files changed, 28 insertions(+), 18 deletions(-) (limited to 'drivers/clocksource/hyperv_timer.c') diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 12d75b50a317..42748adccc98 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -66,7 +66,7 @@ static int hv_ce_set_next_event(unsigned long delta, { u64 current_tick; - current_tick = hyperv_cs->read(NULL); + current_tick = hv_read_reference_counter(); current_tick += delta; hv_init_timer(0, current_tick); return 0; @@ -304,8 +304,8 @@ EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup); * Hyper-V and 32-bit x86. The TSC reference page version is preferred. */ -struct clocksource *hyperv_cs; -EXPORT_SYMBOL_GPL(hyperv_cs); +u64 (*hv_read_reference_counter)(void); +EXPORT_SYMBOL_GPL(hv_read_reference_counter); static union { struct ms_hyperv_tsc_page page; @@ -318,7 +318,7 @@ struct ms_hyperv_tsc_page *hv_get_tsc_page(void) } EXPORT_SYMBOL_GPL(hv_get_tsc_page); -static u64 notrace read_hv_clock_tsc(struct clocksource *arg) +static u64 notrace read_hv_clock_tsc(void) { u64 current_tick = hv_read_tsc_page(hv_get_tsc_page()); @@ -328,9 +328,14 @@ static u64 notrace read_hv_clock_tsc(struct clocksource *arg) return current_tick; } +static u64 notrace read_hv_clock_tsc_cs(struct clocksource *arg) +{ + return read_hv_clock_tsc(); +} + static u64 read_hv_sched_clock_tsc(void) { - return read_hv_clock_tsc(NULL) - hv_sched_clock_offset; + return read_hv_clock_tsc() - hv_sched_clock_offset; } static void suspend_hv_clock_tsc(struct clocksource *arg) @@ -359,14 +364,14 @@ static void resume_hv_clock_tsc(struct clocksource *arg) static struct clocksource hyperv_cs_tsc = { .name = "hyperv_clocksource_tsc_page", .rating = 400, - .read = read_hv_clock_tsc, + .read = read_hv_clock_tsc_cs, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, .suspend= suspend_hv_clock_tsc, .resume = resume_hv_clock_tsc, }; -static u64 notrace read_hv_clock_msr(struct clocksource *arg) +static u64 notrace read_hv_clock_msr(void) { u64 current_tick; /* @@ -378,15 +383,20 @@ static u64 notrace read_hv_clock_msr(struct clocksource *arg) return current_tick; } +static u64 notrace read_hv_clock_msr_cs(struct clocksource *arg) +{ + return read_hv_clock_msr(); +} + static u64 read_hv_sched_clock_msr(void) { - return read_hv_clock_msr(NULL) - hv_sched_clock_offset; + return read_hv_clock_msr() - hv_sched_clock_offset; } static struct clocksource hyperv_cs_msr = { .name = "hyperv_clocksource_msr", .rating = 400, - .read = read_hv_clock_msr, + .read = read_hv_clock_msr_cs, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -399,7 +409,7 @@ static bool __init hv_init_tsc_clocksource(void) if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE)) return false; - hyperv_cs = &hyperv_cs_tsc; + hv_read_reference_counter = read_hv_clock_tsc; phys_addr = virt_to_phys(hv_get_tsc_page()); /* @@ -417,7 +427,7 @@ static bool __init hv_init_tsc_clocksource(void) hv_set_clocksource_vdso(hyperv_cs_tsc); clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); - hv_sched_clock_offset = hyperv_cs->read(hyperv_cs); + hv_sched_clock_offset = hv_read_reference_counter(); hv_setup_sched_clock(read_hv_sched_clock_tsc); return true; @@ -439,10 +449,10 @@ void __init hv_init_clocksource(void) if (!(ms_hyperv.features & HV_MSR_TIME_REF_COUNT_AVAILABLE)) return; - hyperv_cs = &hyperv_cs_msr; + hv_read_reference_counter = read_hv_clock_msr; clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100); - hv_sched_clock_offset = hyperv_cs->read(hyperv_cs); + hv_sched_clock_offset = hv_read_reference_counter(); hv_setup_sched_clock(read_hv_sched_clock_msr); } EXPORT_SYMBOL_GPL(hv_init_clocksource); diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 766bd8457346..296f9098c9e4 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -211,7 +211,7 @@ static struct timespec64 hv_get_adj_host_time(void) unsigned long flags; spin_lock_irqsave(&host_ts.lock, flags); - reftime = hyperv_cs->read(hyperv_cs); + reftime = hv_read_reference_counter(); newtime = host_ts.host_time + (reftime - host_ts.ref_time); ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); spin_unlock_irqrestore(&host_ts.lock, flags); @@ -250,7 +250,7 @@ static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) */ spin_lock_irqsave(&host_ts.lock, flags); - cur_reftime = hyperv_cs->read(hyperv_cs); + cur_reftime = hv_read_reference_counter(); host_ts.host_time = hosttime; host_ts.ref_time = cur_reftime; @@ -315,7 +315,7 @@ static void timesync_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; adj_guesttime(timedatap->parenttime, - hyperv_cs->read(hyperv_cs), + hv_read_reference_counter(), timedatap->flags); } } @@ -524,7 +524,7 @@ static struct ptp_clock *hv_ptp_clock; static int hv_timesync_init(struct hv_util_service *srv) { /* TimeSync requires Hyper-V clocksource. */ - if (!hyperv_cs) + if (!hv_read_reference_counter) return -ENODEV; spin_lock_init(&host_ts.lock); diff --git a/include/clocksource/hyperv_timer.h b/include/clocksource/hyperv_timer.h index 553e539469f0..34eef083c988 100644 --- a/include/clocksource/hyperv_timer.h +++ b/include/clocksource/hyperv_timer.h @@ -30,7 +30,7 @@ extern void hv_stimer_global_cleanup(void); extern void hv_stimer0_isr(void); #ifdef CONFIG_HYPERV_TIMER -extern struct clocksource *hyperv_cs; +extern u64 (*hv_read_reference_counter)(void); extern void hv_init_clocksource(void); extern struct ms_hyperv_tsc_page *hv_get_tsc_page(void); -- cgit v1.2.3 From 9e0333ae38eeb42249e10f95d209244a6e22ac9f Mon Sep 17 00:00:00 2001 From: Andrea Parri Date: Thu, 9 Jan 2020 17:06:50 +0100 Subject: clocksource/drivers/hyper-v: Set TSC clocksource as default w/ InvariantTSC Change the Hyper-V clocksource ratings to 250, below the TSC clocksource rating of 300. In configurations where Hyper-V offers an InvariantTSC, the TSC is not marked "unstable", so the TSC clocksource is available and preferred. With the higher rating, it will be the default. On older hardware and Hyper-V versions, the TSC is marked "unstable", so no TSC clocksource is created and the selected Hyper-V clocksource will be the default. Signed-off-by: Andrea Parri Reviewed-by: Michael Kelley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200109160650.16150-3-parri.andrea@gmail.com --- drivers/clocksource/hyperv_timer.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/clocksource/hyperv_timer.c') diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 42748adccc98..9d808d595ca8 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -302,6 +302,14 @@ EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup); * the other that uses the TSC reference page feature as defined in the * TLFS. The MSR version is for compatibility with old versions of * Hyper-V and 32-bit x86. The TSC reference page version is preferred. + * + * The Hyper-V clocksource ratings of 250 are chosen to be below the + * TSC clocksource rating of 300. In configurations where Hyper-V offers + * an InvariantTSC, the TSC is not marked "unstable", so the TSC clocksource + * is available and preferred. With the higher rating, it will be the + * default. On older hardware and Hyper-V versions, the TSC is marked + * "unstable", so no TSC clocksource is created and the selected Hyper-V + * clocksource will be the default. */ u64 (*hv_read_reference_counter)(void); @@ -363,7 +371,7 @@ static void resume_hv_clock_tsc(struct clocksource *arg) static struct clocksource hyperv_cs_tsc = { .name = "hyperv_clocksource_tsc_page", - .rating = 400, + .rating = 250, .read = read_hv_clock_tsc_cs, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, @@ -395,7 +403,7 @@ static u64 read_hv_sched_clock_msr(void) static struct clocksource hyperv_cs_msr = { .name = "hyperv_clocksource_msr", - .rating = 400, + .rating = 250, .read = read_hv_clock_msr_cs, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, -- cgit v1.2.3