]> Git Repo - J-linux.git/blobdiff - drivers/clocksource/hyperv_timer.c
scsi: zfcp: Trace when request remove fails after qdio send fails
[J-linux.git] / drivers / clocksource / hyperv_timer.c
index 18de1f439ffd5013ed7e1c09f7f4a70bdc8ad5a6..c0cef92b12b82fdef9e178a7ecb6a79f44f3ab42 100644 (file)
@@ -367,9 +367,18 @@ static union {
        u8 reserved[PAGE_SIZE];
 } tsc_pg __aligned(PAGE_SIZE);
 
+static struct ms_hyperv_tsc_page *tsc_page = &tsc_pg.page;
+static unsigned long tsc_pfn;
+
+unsigned long hv_get_tsc_pfn(void)
+{
+       return tsc_pfn;
+}
+EXPORT_SYMBOL_GPL(hv_get_tsc_pfn);
+
 struct ms_hyperv_tsc_page *hv_get_tsc_page(void)
 {
-       return &tsc_pg.page;
+       return tsc_page;
 }
 EXPORT_SYMBOL_GPL(hv_get_tsc_page);
 
@@ -407,13 +416,12 @@ static void suspend_hv_clock_tsc(struct clocksource *arg)
 
 static void resume_hv_clock_tsc(struct clocksource *arg)
 {
-       phys_addr_t phys_addr = virt_to_phys(&tsc_pg);
        union hv_reference_tsc_msr tsc_msr;
 
        /* Re-enable the TSC page */
        tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC);
        tsc_msr.enable = 1;
-       tsc_msr.pfn = HVPFN_DOWN(phys_addr);
+       tsc_msr.pfn = tsc_pfn;
        hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64);
 }
 
@@ -497,14 +505,10 @@ static __always_inline void hv_setup_sched_clock(void *sched_clock) {}
 static bool __init hv_init_tsc_clocksource(void)
 {
        union hv_reference_tsc_msr tsc_msr;
-       phys_addr_t     phys_addr;
 
        if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
                return false;
 
-       if (hv_root_partition)
-               return false;
-
        /*
         * If Hyper-V offers TSC_INVARIANT, then the virtualized TSC correctly
         * handles frequency and offset changes due to live migration,
@@ -522,18 +526,30 @@ static bool __init hv_init_tsc_clocksource(void)
        }
 
        hv_read_reference_counter = read_hv_clock_tsc;
-       phys_addr = virt_to_phys(hv_get_tsc_page());
 
        /*
-        * The Hyper-V TLFS specifies to preserve the value of reserved
-        * bits in registers. So read the existing value, preserve the
-        * low order 12 bits, and add in the guest physical address
-        * (which already has at least the low 12 bits set to zero since
-        * it is page aligned). Also set the "enable" bit, which is bit 0.
+        * TSC page mapping works differently in root compared to guest.
+        * - In guest partition the guest PFN has to be passed to the
+        *   hypervisor.
+        * - In root partition it's other way around: it has to map the PFN
+        *   provided by the hypervisor.
+        *   But it can't be mapped right here as it's too early and MMU isn't
+        *   ready yet. So, we only set the enable bit here and will remap the
+        *   page later in hv_remap_tsc_clocksource().
+        *
+        * It worth mentioning, that TSC clocksource read function
+        * (read_hv_clock_tsc) has a MSR-based fallback mechanism, used when
+        * TSC page is zeroed (which is the case until the PFN is remapped) and
+        * thus TSC clocksource will work even without the real TSC page
+        * mapped.
         */
        tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC);
+       if (hv_root_partition)
+               tsc_pfn = tsc_msr.pfn;
+       else
+               tsc_pfn = HVPFN_DOWN(virt_to_phys(tsc_page));
        tsc_msr.enable = 1;
-       tsc_msr.pfn = HVPFN_DOWN(phys_addr);
+       tsc_msr.pfn = tsc_pfn;
        hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64);
 
        clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);
@@ -566,3 +582,20 @@ void __init hv_init_clocksource(void)
        hv_sched_clock_offset = hv_read_reference_counter();
        hv_setup_sched_clock(read_hv_sched_clock_msr);
 }
+
+void __init hv_remap_tsc_clocksource(void)
+{
+       if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
+               return;
+
+       if (!hv_root_partition) {
+               WARN(1, "%s: attempt to remap TSC page in guest partition\n",
+                    __func__);
+               return;
+       }
+
+       tsc_page = memremap(tsc_pfn << HV_HYP_PAGE_SHIFT, sizeof(tsc_pg),
+                           MEMREMAP_WB);
+       if (!tsc_page)
+               pr_err("Failed to remap Hyper-V TSC page.\n");
+}
This page took 0.030881 seconds and 4 git commands to generate.