if (!gpc->active)
                return false;
 
-       if (offset_in_page(gpc->gpa) + len > PAGE_SIZE)
+       if (gpc->generation != slots->generation || kvm_is_error_hva(gpc->uhva))
                return false;
 
-       if (gpc->generation != slots->generation || kvm_is_error_hva(gpc->uhva))
+       if (offset_in_page(gpc->uhva) + len > PAGE_SIZE)
                return false;
 
        if (!gpc->valid)
 static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 {
        /* Note, the new page offset may be different than the old! */
-       void *old_khva = gpc->khva - offset_in_page(gpc->khva);
+       void *old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc->khva);
        kvm_pfn_t new_pfn = KVM_PFN_ERR_FAULT;
        void *new_khva = NULL;
        unsigned long mmu_seq;
 
        gpc->valid = true;
        gpc->pfn = new_pfn;
-       gpc->khva = new_khva + offset_in_page(gpc->gpa);
+       gpc->khva = new_khva + offset_in_page(gpc->uhva);
 
        /*
         * Put the reference to the _new_ pfn.  The pfn is now tracked by the
        bool unmap_old = false;
        unsigned long old_uhva;
        kvm_pfn_t old_pfn;
+       bool hva_change = false;
        void *old_khva;
        int ret;
 
        }
 
        old_pfn = gpc->pfn;
-       old_khva = gpc->khva - offset_in_page(gpc->khva);
-       old_uhva = gpc->uhva;
+       old_khva = (void *)PAGE_ALIGN_DOWN((uintptr_t)gpc->khva);
+       old_uhva = PAGE_ALIGN_DOWN(gpc->uhva);
 
-       /* If the userspace HVA is invalid, refresh that first */
+       /* Refresh the userspace HVA if necessary */
        if (gpc->gpa != gpa || gpc->generation != slots->generation ||
            kvm_is_error_hva(gpc->uhva)) {
                gfn_t gfn = gpa_to_gfn(gpa);
                        ret = -EFAULT;
                        goto out;
                }
+
+               /*
+                * Even if the GPA and/or the memslot generation changed, the
+                * HVA may still be the same.
+                */
+               if (gpc->uhva != old_uhva)
+                       hva_change = true;
+       } else {
+               gpc->uhva = old_uhva;
        }
 
+       /* Note: the offset must be correct before calling hva_to_pfn_retry() */
+       gpc->uhva += page_offset;
+
        /*
         * If the userspace HVA changed or the PFN was already invalid,
         * drop the lock and do the HVA to PFN lookup again.
         */
-       if (!gpc->valid || old_uhva != gpc->uhva) {
+       if (!gpc->valid || hva_change) {
                ret = hva_to_pfn_retry(gpc);
        } else {
                /*