diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2009-12-23 11:35:23 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2010-03-01 10:35:45 -0500 |
commit | a983fb238728e1123177e8058d4f644b949a7d05 (patch) | |
tree | 55f9b26d8ace10a1233b293dd423eae2d0ff9046 | |
parent | b050b015abbef8225826eecb6f6b4d4a6dea7b79 (diff) |
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>
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 4 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 60 | ||||
-rw-r--r-- | include/linux/kvm_host.h | 6 | ||||
-rw-r--r-- | virt/kvm/kvm_main.c | 4 |
4 files changed, 63 insertions, 11 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 7cdcb3d0f770..6c8c7c578c46 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -368,8 +368,12 @@ struct kvm_mem_alias { | |||
368 | gfn_t base_gfn; | 368 | gfn_t base_gfn; |
369 | unsigned long npages; | 369 | unsigned long npages; |
370 | gfn_t target_gfn; | 370 | gfn_t target_gfn; |
371 | #define KVM_ALIAS_INVALID 1UL | ||
372 | unsigned long flags; | ||
371 | }; | 373 | }; |
372 | 374 | ||
375 | #define KVM_ARCH_HAS_UNALIAS_INSTANTIATION | ||
376 | |||
373 | struct kvm_mem_aliases { | 377 | struct kvm_mem_aliases { |
374 | struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS]; | 378 | struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS]; |
375 | int naliases; | 379 | int naliases; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index e7488350ca16..28127c936c3b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/intel-iommu.h> | 38 | #include <linux/intel-iommu.h> |
39 | #include <linux/cpufreq.h> | 39 | #include <linux/cpufreq.h> |
40 | #include <linux/user-return-notifier.h> | 40 | #include <linux/user-return-notifier.h> |
41 | #include <linux/srcu.h> | ||
41 | #include <trace/events/kvm.h> | 42 | #include <trace/events/kvm.h> |
42 | #undef TRACE_INCLUDE_FILE | 43 | #undef TRACE_INCLUDE_FILE |
43 | #define CREATE_TRACE_POINTS | 44 | #define CREATE_TRACE_POINTS |
@@ -2223,11 +2224,32 @@ static int kvm_vm_ioctl_get_nr_mmu_pages(struct kvm *kvm) | |||
2223 | return kvm->arch.n_alloc_mmu_pages; | 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 | gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn) | 2246 | gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn) |
2227 | { | 2247 | { |
2228 | int i; | 2248 | int i; |
2229 | struct kvm_mem_alias *alias; | 2249 | struct kvm_mem_alias *alias; |
2230 | struct kvm_mem_aliases *aliases = kvm->arch.aliases; | 2250 | struct kvm_mem_aliases *aliases; |
2251 | |||
2252 | aliases = rcu_dereference(kvm->arch.aliases); | ||
2231 | 2253 | ||
2232 | for (i = 0; i < aliases->naliases; ++i) { | 2254 | for (i = 0; i < aliases->naliases; ++i) { |
2233 | alias = &aliases->aliases[i]; | 2255 | alias = &aliases->aliases[i]; |
@@ -2248,7 +2270,7 @@ static int kvm_vm_ioctl_set_memory_alias(struct kvm *kvm, | |||
2248 | { | 2270 | { |
2249 | int r, n; | 2271 | int r, n; |
2250 | struct kvm_mem_alias *p; | 2272 | struct kvm_mem_alias *p; |
2251 | struct kvm_mem_aliases *aliases; | 2273 | struct kvm_mem_aliases *aliases, *old_aliases; |
2252 | 2274 | ||
2253 | r = -EINVAL; | 2275 | r = -EINVAL; |
2254 | /* General sanity checks */ | 2276 | /* General sanity checks */ |
@@ -2265,28 +2287,48 @@ static int kvm_vm_ioctl_set_memory_alias(struct kvm *kvm, | |||
2265 | < alias->target_phys_addr) | 2287 | < alias->target_phys_addr) |
2266 | goto out; | 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 | down_write(&kvm->slots_lock); | 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); | ||
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)); | ||
2272 | 2312 | ||
2273 | p = &aliases->aliases[alias->slot]; | 2313 | p = &aliases->aliases[alias->slot]; |
2274 | p->base_gfn = alias->guest_phys_addr >> PAGE_SHIFT; | 2314 | p->base_gfn = alias->guest_phys_addr >> PAGE_SHIFT; |
2275 | p->npages = alias->memory_size >> PAGE_SHIFT; | 2315 | p->npages = alias->memory_size >> PAGE_SHIFT; |
2276 | p->target_gfn = alias->target_phys_addr >> PAGE_SHIFT; | 2316 | p->target_gfn = alias->target_phys_addr >> PAGE_SHIFT; |
2317 | p->flags &= ~(KVM_ALIAS_INVALID); | ||
2277 | 2318 | ||
2278 | for (n = KVM_ALIAS_SLOTS; n > 0; --n) | 2319 | for (n = KVM_ALIAS_SLOTS; n > 0; --n) |
2279 | if (aliases->aliases[n - 1].npages) | 2320 | if (aliases->aliases[n - 1].npages) |
2280 | break; | 2321 | break; |
2281 | aliases->naliases = n; | 2322 | aliases->naliases = n; |
2282 | 2323 | ||
2283 | spin_unlock(&kvm->mmu_lock); | 2324 | old_aliases = kvm->arch.aliases; |
2284 | kvm_mmu_zap_all(kvm); | 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 | up_write(&kvm->slots_lock); | 2331 | up_write(&kvm->slots_lock); |
2287 | |||
2288 | return 0; | ||
2289 | |||
2290 | out: | 2332 | out: |
2291 | return r; | 2333 | return r; |
2292 | } | 2334 | } |
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 93bd30701ca7..20941c0f4045 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h | |||
@@ -266,6 +266,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, | |||
266 | void kvm_disable_largepages(void); | 266 | void kvm_disable_largepages(void); |
267 | void kvm_arch_flush_shadow(struct kvm *kvm); | 267 | void kvm_arch_flush_shadow(struct kvm *kvm); |
268 | gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn); | 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 | struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); | 271 | struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); |
270 | unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); | 272 | unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); |
271 | void kvm_release_page_clean(struct page *page); | 273 | void kvm_release_page_clean(struct page *page); |
@@ -539,6 +541,10 @@ static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_se | |||
539 | } | 541 | } |
540 | #endif | 542 | #endif |
541 | 543 | ||
544 | #ifndef KVM_ARCH_HAS_UNALIAS_INSTANTIATION | ||
545 | #define unalias_gfn_instantiation unalias_gfn | ||
546 | #endif | ||
547 | |||
542 | #ifdef CONFIG_HAVE_KVM_IRQCHIP | 548 | #ifdef CONFIG_HAVE_KVM_IRQCHIP |
543 | 549 | ||
544 | #define KVM_MAX_IRQ_ROUTES 1024 | 550 | #define KVM_MAX_IRQ_ROUTES 1024 |
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 2bb24a814fdf..c680f7b64c6f 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -859,7 +859,7 @@ int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn) | |||
859 | int i; | 859 | int i; |
860 | struct kvm_memslots *slots = rcu_dereference(kvm->memslots); | 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 | for (i = 0; i < KVM_MEMORY_SLOTS; ++i) { | 863 | for (i = 0; i < KVM_MEMORY_SLOTS; ++i) { |
864 | struct kvm_memory_slot *memslot = &slots->memslots[i]; | 864 | struct kvm_memory_slot *memslot = &slots->memslots[i]; |
865 | 865 | ||
@@ -896,7 +896,7 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) | |||
896 | { | 896 | { |
897 | struct kvm_memory_slot *slot; | 897 | struct kvm_memory_slot *slot; |
898 | 898 | ||
899 | gfn = unalias_gfn(kvm, gfn); | 899 | gfn = unalias_gfn_instantiation(kvm, gfn); |
900 | slot = gfn_to_memslot_unaliased(kvm, gfn); | 900 | slot = gfn_to_memslot_unaliased(kvm, gfn); |
901 | if (!slot || slot->flags & KVM_MEMSLOT_INVALID) | 901 | if (!slot || slot->flags & KVM_MEMSLOT_INVALID) |
902 | return bad_hva(); | 902 | return bad_hva(); |