162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Kernel-based Virtual Machine driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This module enables kernel and guest-mode vCPU access to guest physical 662306a36Sopenharmony_ci * memory with suitable invalidation mechanisms. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright © 2021 Amazon.com, Inc. or its affiliates. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Authors: 1162306a36Sopenharmony_ci * David Woodhouse <dwmw2@infradead.org> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kvm_host.h> 1562306a36Sopenharmony_ci#include <linux/kvm.h> 1662306a36Sopenharmony_ci#include <linux/highmem.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "kvm_mm.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * MMU notifier 'invalidate_range_start' hook. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_civoid gfn_to_pfn_cache_invalidate_start(struct kvm *kvm, unsigned long start, 2662306a36Sopenharmony_ci unsigned long end, bool may_block) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_VCPUS); 2962306a36Sopenharmony_ci struct gfn_to_pfn_cache *gpc; 3062306a36Sopenharmony_ci bool evict_vcpus = false; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci spin_lock(&kvm->gpc_lock); 3362306a36Sopenharmony_ci list_for_each_entry(gpc, &kvm->gpc_list, list) { 3462306a36Sopenharmony_ci write_lock_irq(&gpc->lock); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* Only a single page so no need to care about length */ 3762306a36Sopenharmony_ci if (gpc->valid && !is_error_noslot_pfn(gpc->pfn) && 3862306a36Sopenharmony_ci gpc->uhva >= start && gpc->uhva < end) { 3962306a36Sopenharmony_ci gpc->valid = false; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* 4262306a36Sopenharmony_ci * If a guest vCPU could be using the physical address, 4362306a36Sopenharmony_ci * it needs to be forced out of guest mode. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci if (gpc->usage & KVM_GUEST_USES_PFN) { 4662306a36Sopenharmony_ci if (!evict_vcpus) { 4762306a36Sopenharmony_ci evict_vcpus = true; 4862306a36Sopenharmony_ci bitmap_zero(vcpu_bitmap, KVM_MAX_VCPUS); 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci __set_bit(gpc->vcpu->vcpu_idx, vcpu_bitmap); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci write_unlock_irq(&gpc->lock); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci spin_unlock(&kvm->gpc_lock); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (evict_vcpus) { 5862306a36Sopenharmony_ci /* 5962306a36Sopenharmony_ci * KVM needs to ensure the vCPU is fully out of guest context 6062306a36Sopenharmony_ci * before allowing the invalidation to continue. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci unsigned int req = KVM_REQ_OUTSIDE_GUEST_MODE; 6362306a36Sopenharmony_ci bool called; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * If the OOM reaper is active, then all vCPUs should have 6762306a36Sopenharmony_ci * been stopped already, so perform the request without 6862306a36Sopenharmony_ci * KVM_REQUEST_WAIT and be sad if any needed to be IPI'd. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci if (!may_block) 7162306a36Sopenharmony_ci req &= ~KVM_REQUEST_WAIT; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci called = kvm_make_vcpus_request_mask(kvm, req, vcpu_bitmap); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci WARN_ON_ONCE(called && !may_block); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cibool kvm_gpc_check(struct gfn_to_pfn_cache *gpc, unsigned long len) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct kvm_memslots *slots = kvm_memslots(gpc->kvm); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!gpc->active) 8462306a36Sopenharmony_ci return false; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if ((gpc->gpa & ~PAGE_MASK) + len > PAGE_SIZE) 8762306a36Sopenharmony_ci return false; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (gpc->generation != slots->generation || kvm_is_error_hva(gpc->uhva)) 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!gpc->valid) 9362306a36Sopenharmony_ci return false; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return true; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_gpc_check); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void gpc_unmap_khva(kvm_pfn_t pfn, void *khva) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci /* Unmap the old pfn/page if it was mapped before. */ 10262306a36Sopenharmony_ci if (!is_error_noslot_pfn(pfn) && khva) { 10362306a36Sopenharmony_ci if (pfn_valid(pfn)) 10462306a36Sopenharmony_ci kunmap(pfn_to_page(pfn)); 10562306a36Sopenharmony_ci#ifdef CONFIG_HAS_IOMEM 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci memunmap(khva); 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_seq) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * mn_active_invalidate_count acts for all intents and purposes 11662306a36Sopenharmony_ci * like mmu_invalidate_in_progress here; but the latter cannot 11762306a36Sopenharmony_ci * be used here because the invalidation of caches in the 11862306a36Sopenharmony_ci * mmu_notifier event occurs _before_ mmu_invalidate_in_progress 11962306a36Sopenharmony_ci * is elevated. 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Note, it does not matter that mn_active_invalidate_count 12262306a36Sopenharmony_ci * is not protected by gpc->lock. It is guaranteed to 12362306a36Sopenharmony_ci * be elevated before the mmu_notifier acquires gpc->lock, and 12462306a36Sopenharmony_ci * isn't dropped until after mmu_invalidate_seq is updated. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci if (kvm->mn_active_invalidate_count) 12762306a36Sopenharmony_ci return true; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Ensure mn_active_invalidate_count is read before 13162306a36Sopenharmony_ci * mmu_invalidate_seq. This pairs with the smp_wmb() in 13262306a36Sopenharmony_ci * mmu_notifier_invalidate_range_end() to guarantee either the 13362306a36Sopenharmony_ci * old (non-zero) value of mn_active_invalidate_count or the 13462306a36Sopenharmony_ci * new (incremented) value of mmu_invalidate_seq is observed. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci smp_rmb(); 13762306a36Sopenharmony_ci return kvm->mmu_invalidate_seq != mmu_seq; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci /* Note, the new page offset may be different than the old! */ 14362306a36Sopenharmony_ci void *old_khva = gpc->khva - offset_in_page(gpc->khva); 14462306a36Sopenharmony_ci kvm_pfn_t new_pfn = KVM_PFN_ERR_FAULT; 14562306a36Sopenharmony_ci void *new_khva = NULL; 14662306a36Sopenharmony_ci unsigned long mmu_seq; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci lockdep_assert_held(&gpc->refresh_lock); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci lockdep_assert_held_write(&gpc->lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Invalidate the cache prior to dropping gpc->lock, the gpa=>uhva 15462306a36Sopenharmony_ci * assets have already been updated and so a concurrent check() from a 15562306a36Sopenharmony_ci * different task may not fail the gpa/uhva/generation checks. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci gpc->valid = false; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci do { 16062306a36Sopenharmony_ci mmu_seq = gpc->kvm->mmu_invalidate_seq; 16162306a36Sopenharmony_ci smp_rmb(); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci write_unlock_irq(&gpc->lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * If the previous iteration "failed" due to an mmu_notifier 16762306a36Sopenharmony_ci * event, release the pfn and unmap the kernel virtual address 16862306a36Sopenharmony_ci * from the previous attempt. Unmapping might sleep, so this 16962306a36Sopenharmony_ci * needs to be done after dropping the lock. Opportunistically 17062306a36Sopenharmony_ci * check for resched while the lock isn't held. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci if (new_pfn != KVM_PFN_ERR_FAULT) { 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Keep the mapping if the previous iteration reused 17562306a36Sopenharmony_ci * the existing mapping and didn't create a new one. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci if (new_khva != old_khva) 17862306a36Sopenharmony_ci gpc_unmap_khva(new_pfn, new_khva); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci kvm_release_pfn_clean(new_pfn); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci cond_resched(); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* We always request a writeable mapping */ 18662306a36Sopenharmony_ci new_pfn = hva_to_pfn(gpc->uhva, false, false, NULL, true, NULL); 18762306a36Sopenharmony_ci if (is_error_noslot_pfn(new_pfn)) 18862306a36Sopenharmony_ci goto out_error; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Obtain a new kernel mapping if KVM itself will access the 19262306a36Sopenharmony_ci * pfn. Note, kmap() and memremap() can both sleep, so this 19362306a36Sopenharmony_ci * too must be done outside of gpc->lock! 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci if (gpc->usage & KVM_HOST_USES_PFN) { 19662306a36Sopenharmony_ci if (new_pfn == gpc->pfn) { 19762306a36Sopenharmony_ci new_khva = old_khva; 19862306a36Sopenharmony_ci } else if (pfn_valid(new_pfn)) { 19962306a36Sopenharmony_ci new_khva = kmap(pfn_to_page(new_pfn)); 20062306a36Sopenharmony_ci#ifdef CONFIG_HAS_IOMEM 20162306a36Sopenharmony_ci } else { 20262306a36Sopenharmony_ci new_khva = memremap(pfn_to_hpa(new_pfn), PAGE_SIZE, MEMREMAP_WB); 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci if (!new_khva) { 20662306a36Sopenharmony_ci kvm_release_pfn_clean(new_pfn); 20762306a36Sopenharmony_ci goto out_error; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci write_lock_irq(&gpc->lock); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Other tasks must wait for _this_ refresh to complete before 21562306a36Sopenharmony_ci * attempting to refresh. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci WARN_ON_ONCE(gpc->valid); 21862306a36Sopenharmony_ci } while (mmu_notifier_retry_cache(gpc->kvm, mmu_seq)); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci gpc->valid = true; 22162306a36Sopenharmony_ci gpc->pfn = new_pfn; 22262306a36Sopenharmony_ci gpc->khva = new_khva + (gpc->gpa & ~PAGE_MASK); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Put the reference to the _new_ pfn. The pfn is now tracked by the 22662306a36Sopenharmony_ci * cache and can be safely migrated, swapped, etc... as the cache will 22762306a36Sopenharmony_ci * invalidate any mappings in response to relevant mmu_notifier events. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci kvm_release_pfn_clean(new_pfn); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciout_error: 23462306a36Sopenharmony_ci write_lock_irq(&gpc->lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return -EFAULT; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, 24062306a36Sopenharmony_ci unsigned long len) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct kvm_memslots *slots = kvm_memslots(gpc->kvm); 24362306a36Sopenharmony_ci unsigned long page_offset = gpa & ~PAGE_MASK; 24462306a36Sopenharmony_ci bool unmap_old = false; 24562306a36Sopenharmony_ci unsigned long old_uhva; 24662306a36Sopenharmony_ci kvm_pfn_t old_pfn; 24762306a36Sopenharmony_ci void *old_khva; 24862306a36Sopenharmony_ci int ret; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * If must fit within a single page. The 'len' argument is 25262306a36Sopenharmony_ci * only to enforce that. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci if (page_offset + len > PAGE_SIZE) 25562306a36Sopenharmony_ci return -EINVAL; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * If another task is refreshing the cache, wait for it to complete. 25962306a36Sopenharmony_ci * There is no guarantee that concurrent refreshes will see the same 26062306a36Sopenharmony_ci * gpa, memslots generation, etc..., so they must be fully serialized. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci mutex_lock(&gpc->refresh_lock); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci write_lock_irq(&gpc->lock); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (!gpc->active) { 26762306a36Sopenharmony_ci ret = -EINVAL; 26862306a36Sopenharmony_ci goto out_unlock; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci old_pfn = gpc->pfn; 27262306a36Sopenharmony_ci old_khva = gpc->khva - offset_in_page(gpc->khva); 27362306a36Sopenharmony_ci old_uhva = gpc->uhva; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* If the userspace HVA is invalid, refresh that first */ 27662306a36Sopenharmony_ci if (gpc->gpa != gpa || gpc->generation != slots->generation || 27762306a36Sopenharmony_ci kvm_is_error_hva(gpc->uhva)) { 27862306a36Sopenharmony_ci gfn_t gfn = gpa_to_gfn(gpa); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci gpc->gpa = gpa; 28162306a36Sopenharmony_ci gpc->generation = slots->generation; 28262306a36Sopenharmony_ci gpc->memslot = __gfn_to_memslot(slots, gfn); 28362306a36Sopenharmony_ci gpc->uhva = gfn_to_hva_memslot(gpc->memslot, gfn); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (kvm_is_error_hva(gpc->uhva)) { 28662306a36Sopenharmony_ci ret = -EFAULT; 28762306a36Sopenharmony_ci goto out; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * If the userspace HVA changed or the PFN was already invalid, 29362306a36Sopenharmony_ci * drop the lock and do the HVA to PFN lookup again. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (!gpc->valid || old_uhva != gpc->uhva) { 29662306a36Sopenharmony_ci ret = hva_to_pfn_retry(gpc); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * If the HVA→PFN mapping was already valid, don't unmap it. 30062306a36Sopenharmony_ci * But do update gpc->khva because the offset within the page 30162306a36Sopenharmony_ci * may have changed. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci gpc->khva = old_khva + page_offset; 30462306a36Sopenharmony_ci ret = 0; 30562306a36Sopenharmony_ci goto out_unlock; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci out: 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Invalidate the cache and purge the pfn/khva if the refresh failed. 31162306a36Sopenharmony_ci * Some/all of the uhva, gpa, and memslot generation info may still be 31262306a36Sopenharmony_ci * valid, leave it as is. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (ret) { 31562306a36Sopenharmony_ci gpc->valid = false; 31662306a36Sopenharmony_ci gpc->pfn = KVM_PFN_ERR_FAULT; 31762306a36Sopenharmony_ci gpc->khva = NULL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Detect a pfn change before dropping the lock! */ 32162306a36Sopenharmony_ci unmap_old = (old_pfn != gpc->pfn); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciout_unlock: 32462306a36Sopenharmony_ci write_unlock_irq(&gpc->lock); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci mutex_unlock(&gpc->refresh_lock); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (unmap_old) 32962306a36Sopenharmony_ci gpc_unmap_khva(old_pfn, old_khva); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ciint kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci return __kvm_gpc_refresh(gpc, gpc->gpa, len); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_gpc_refresh); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_civoid kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm, 34162306a36Sopenharmony_ci struct kvm_vcpu *vcpu, enum pfn_cache_usage usage) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci WARN_ON_ONCE(!usage || (usage & KVM_GUEST_AND_HOST_USE_PFN) != usage); 34462306a36Sopenharmony_ci WARN_ON_ONCE((usage & KVM_GUEST_USES_PFN) && !vcpu); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci rwlock_init(&gpc->lock); 34762306a36Sopenharmony_ci mutex_init(&gpc->refresh_lock); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci gpc->kvm = kvm; 35062306a36Sopenharmony_ci gpc->vcpu = vcpu; 35162306a36Sopenharmony_ci gpc->usage = usage; 35262306a36Sopenharmony_ci gpc->pfn = KVM_PFN_ERR_FAULT; 35362306a36Sopenharmony_ci gpc->uhva = KVM_HVA_ERR_BAD; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_gpc_init); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciint kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct kvm *kvm = gpc->kvm; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!gpc->active) { 36262306a36Sopenharmony_ci if (KVM_BUG_ON(gpc->valid, kvm)) 36362306a36Sopenharmony_ci return -EIO; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci spin_lock(&kvm->gpc_lock); 36662306a36Sopenharmony_ci list_add(&gpc->list, &kvm->gpc_list); 36762306a36Sopenharmony_ci spin_unlock(&kvm->gpc_lock); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * Activate the cache after adding it to the list, a concurrent 37162306a36Sopenharmony_ci * refresh must not establish a mapping until the cache is 37262306a36Sopenharmony_ci * reachable by mmu_notifier events. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci write_lock_irq(&gpc->lock); 37562306a36Sopenharmony_ci gpc->active = true; 37662306a36Sopenharmony_ci write_unlock_irq(&gpc->lock); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci return __kvm_gpc_refresh(gpc, gpa, len); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_gpc_activate); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_civoid kvm_gpc_deactivate(struct gfn_to_pfn_cache *gpc) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct kvm *kvm = gpc->kvm; 38562306a36Sopenharmony_ci kvm_pfn_t old_pfn; 38662306a36Sopenharmony_ci void *old_khva; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (gpc->active) { 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * Deactivate the cache before removing it from the list, KVM 39162306a36Sopenharmony_ci * must stall mmu_notifier events until all users go away, i.e. 39262306a36Sopenharmony_ci * until gpc->lock is dropped and refresh is guaranteed to fail. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci write_lock_irq(&gpc->lock); 39562306a36Sopenharmony_ci gpc->active = false; 39662306a36Sopenharmony_ci gpc->valid = false; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * Leave the GPA => uHVA cache intact, it's protected by the 40062306a36Sopenharmony_ci * memslot generation. The PFN lookup needs to be redone every 40162306a36Sopenharmony_ci * time as mmu_notifier protection is lost when the cache is 40262306a36Sopenharmony_ci * removed from the VM's gpc_list. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci old_khva = gpc->khva - offset_in_page(gpc->khva); 40562306a36Sopenharmony_ci gpc->khva = NULL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci old_pfn = gpc->pfn; 40862306a36Sopenharmony_ci gpc->pfn = KVM_PFN_ERR_FAULT; 40962306a36Sopenharmony_ci write_unlock_irq(&gpc->lock); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci spin_lock(&kvm->gpc_lock); 41262306a36Sopenharmony_ci list_del(&gpc->list); 41362306a36Sopenharmony_ci spin_unlock(&kvm->gpc_lock); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci gpc_unmap_khva(old_pfn, old_khva); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_gpc_deactivate); 419