Commit a983fb238728e1123177e8058d4f644b949a7d05

Authored by Marcelo Tosatti
1 parent b050b015ab

KVM: x86: switch kvm_set_memory_alias to SRCU update

Using a similar two-step procedure as for memslots.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>

Showing 4 changed files with 63 additions and 11 deletions Side-by-side Diff

arch/x86/include/asm/kvm_host.h
... ... @@ -368,7 +368,11 @@
368 368 gfn_t base_gfn;
369 369 unsigned long npages;
370 370 gfn_t target_gfn;
  371 +#define KVM_ALIAS_INVALID 1UL
  372 + unsigned long flags;
371 373 };
  374 +
  375 +#define KVM_ARCH_HAS_UNALIAS_INSTANTIATION
372 376  
373 377 struct kvm_mem_aliases {
374 378 struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS];
... ... @@ -38,6 +38,7 @@
38 38 #include <linux/intel-iommu.h>
39 39 #include <linux/cpufreq.h>
40 40 #include <linux/user-return-notifier.h>
  41 +#include <linux/srcu.h>
41 42 #include <trace/events/kvm.h>
42 43 #undef TRACE_INCLUDE_FILE
43 44 #define CREATE_TRACE_POINTS
44 45  
45 46  
... ... @@ -2223,12 +2224,33 @@
2223 2224 return kvm->arch.n_alloc_mmu_pages;
2224 2225 }
2225 2226  
  2227 +gfn_t unalias_gfn_instantiation(struct kvm *kvm, gfn_t gfn)
  2228 +{
  2229 + int i;
  2230 + struct kvm_mem_alias *alias;
  2231 + struct kvm_mem_aliases *aliases;
  2232 +
  2233 + aliases = rcu_dereference(kvm->arch.aliases);
  2234 +
  2235 + for (i = 0; i < aliases->naliases; ++i) {
  2236 + alias = &aliases->aliases[i];
  2237 + if (alias->flags & KVM_ALIAS_INVALID)
  2238 + continue;
  2239 + if (gfn >= alias->base_gfn
  2240 + && gfn < alias->base_gfn + alias->npages)
  2241 + return alias->target_gfn + gfn - alias->base_gfn;
  2242 + }
  2243 + return gfn;
  2244 +}
  2245 +
2226 2246 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
2227 2247 {
2228 2248 int i;
2229 2249 struct kvm_mem_alias *alias;
2230   - struct kvm_mem_aliases *aliases = kvm->arch.aliases;
  2250 + struct kvm_mem_aliases *aliases;
2231 2251  
  2252 + aliases = rcu_dereference(kvm->arch.aliases);
  2253 +
2232 2254 for (i = 0; i < aliases->naliases; ++i) {
2233 2255 alias = &aliases->aliases[i];
2234 2256 if (gfn >= alias->base_gfn
... ... @@ -2248,7 +2270,7 @@
2248 2270 {
2249 2271 int r, n;
2250 2272 struct kvm_mem_alias *p;
2251   - struct kvm_mem_aliases *aliases;
  2273 + struct kvm_mem_aliases *aliases, *old_aliases;
2252 2274  
2253 2275 r = -EINVAL;
2254 2276 /* General sanity checks */
2255 2277  
2256 2278  
2257 2279  
2258 2280  
2259 2281  
2260 2282  
2261 2283  
... ... @@ -2265,28 +2287,48 @@
2265 2287 < alias->target_phys_addr)
2266 2288 goto out;
2267 2289  
  2290 + r = -ENOMEM;
  2291 + aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
  2292 + if (!aliases)
  2293 + goto out;
  2294 +
2268 2295 down_write(&kvm->slots_lock);
2269   - spin_lock(&kvm->mmu_lock);
2270 2296  
2271   - aliases = kvm->arch.aliases;
  2297 + /* invalidate any gfn reference in case of deletion/shrinking */
  2298 + memcpy(aliases, kvm->arch.aliases, sizeof(struct kvm_mem_aliases));
  2299 + aliases->aliases[alias->slot].flags |= KVM_ALIAS_INVALID;
  2300 + old_aliases = kvm->arch.aliases;
  2301 + rcu_assign_pointer(kvm->arch.aliases, aliases);
  2302 + synchronize_srcu_expedited(&kvm->srcu);
  2303 + kvm_mmu_zap_all(kvm);
  2304 + kfree(old_aliases);
2272 2305  
  2306 + r = -ENOMEM;
  2307 + aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
  2308 + if (!aliases)
  2309 + goto out_unlock;
  2310 +
  2311 + memcpy(aliases, kvm->arch.aliases, sizeof(struct kvm_mem_aliases));
  2312 +
2273 2313 p = &aliases->aliases[alias->slot];
2274 2314 p->base_gfn = alias->guest_phys_addr >> PAGE_SHIFT;
2275 2315 p->npages = alias->memory_size >> PAGE_SHIFT;
2276 2316 p->target_gfn = alias->target_phys_addr >> PAGE_SHIFT;
  2317 + p->flags &= ~(KVM_ALIAS_INVALID);
2277 2318  
2278 2319 for (n = KVM_ALIAS_SLOTS; n > 0; --n)
2279 2320 if (aliases->aliases[n - 1].npages)
2280 2321 break;
2281 2322 aliases->naliases = n;
2282 2323  
2283   - spin_unlock(&kvm->mmu_lock);
2284   - kvm_mmu_zap_all(kvm);
  2324 + old_aliases = kvm->arch.aliases;
  2325 + rcu_assign_pointer(kvm->arch.aliases, aliases);
  2326 + synchronize_srcu_expedited(&kvm->srcu);
  2327 + kfree(old_aliases);
  2328 + r = 0;
2285 2329  
  2330 +out_unlock:
2286 2331 up_write(&kvm->slots_lock);
2287   -
2288   - return 0;
2289   -
2290 2332 out:
2291 2333 return r;
2292 2334 }
include/linux/kvm_host.h
... ... @@ -266,6 +266,8 @@
266 266 void kvm_disable_largepages(void);
267 267 void kvm_arch_flush_shadow(struct kvm *kvm);
268 268 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn);
  269 +gfn_t unalias_gfn_instantiation(struct kvm *kvm, gfn_t gfn);
  270 +
269 271 struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn);
270 272 unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn);
271 273 void kvm_release_page_clean(struct page *page);
... ... @@ -537,6 +539,10 @@
537 539 return 1;
538 540 return 0;
539 541 }
  542 +#endif
  543 +
  544 +#ifndef KVM_ARCH_HAS_UNALIAS_INSTANTIATION
  545 +#define unalias_gfn_instantiation unalias_gfn
540 546 #endif
541 547  
542 548 #ifdef CONFIG_HAVE_KVM_IRQCHIP
... ... @@ -859,7 +859,7 @@
859 859 int i;
860 860 struct kvm_memslots *slots = rcu_dereference(kvm->memslots);
861 861  
862   - gfn = unalias_gfn(kvm, gfn);
  862 + gfn = unalias_gfn_instantiation(kvm, gfn);
863 863 for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
864 864 struct kvm_memory_slot *memslot = &slots->memslots[i];
865 865  
... ... @@ -896,7 +896,7 @@
896 896 {
897 897 struct kvm_memory_slot *slot;
898 898  
899   - gfn = unalias_gfn(kvm, gfn);
  899 + gfn = unalias_gfn_instantiation(kvm, gfn);
900 900 slot = gfn_to_memslot_unaliased(kvm, gfn);
901 901 if (!slot || slot->flags & KVM_MEMSLOT_INVALID)
902 902 return bad_hva();