diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-07 16:01:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-07 16:01:25 -0400 |
commit | 875b7679abbb232b584f2eec59fa6e45690dd6c4 (patch) | |
tree | 09335bd46be2c41367f24b129b685c97e5a5842a | |
parent | 39ab967e1d090e36ed49adce9ad82f57c91adbb6 (diff) | |
parent | 8f964525a121f2ff2df948dac908dcc65be21b5b (diff) |
Merge git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM fix from Gleb Natapov:
"Bugfix for the regression introduced by commit c300aa64ddf5"
* git://git.kernel.org/pub/scm/virt/kvm/kvm:
KVM: Allow cross page reads and writes from cached translations.
-rw-r--r-- | arch/x86/kvm/lapic.c | 2 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 13 | ||||
-rw-r--r-- | include/linux/kvm_host.h | 2 | ||||
-rw-r--r-- | include/linux/kvm_types.h | 1 | ||||
-rw-r--r-- | virt/kvm/kvm_main.c | 47 |
5 files changed, 46 insertions, 19 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 02b51dd4e4ad..f77df1c5de6e 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -1857,7 +1857,7 @@ int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data) | |||
1857 | if (!pv_eoi_enabled(vcpu)) | 1857 | if (!pv_eoi_enabled(vcpu)) |
1858 | return 0; | 1858 | return 0; |
1859 | return kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.pv_eoi.data, | 1859 | return kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.pv_eoi.data, |
1860 | addr); | 1860 | addr, sizeof(u8)); |
1861 | } | 1861 | } |
1862 | 1862 | ||
1863 | void kvm_lapic_init(void) | 1863 | void kvm_lapic_init(void) |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f19ac0aca60d..e1721324c271 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -1823,7 +1823,8 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) | |||
1823 | return 0; | 1823 | return 0; |
1824 | } | 1824 | } |
1825 | 1825 | ||
1826 | if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa)) | 1826 | if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa, |
1827 | sizeof(u32))) | ||
1827 | return 1; | 1828 | return 1; |
1828 | 1829 | ||
1829 | vcpu->arch.apf.send_user_only = !(data & KVM_ASYNC_PF_SEND_ALWAYS); | 1830 | vcpu->arch.apf.send_user_only = !(data & KVM_ASYNC_PF_SEND_ALWAYS); |
@@ -1952,12 +1953,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) | |||
1952 | 1953 | ||
1953 | gpa_offset = data & ~(PAGE_MASK | 1); | 1954 | gpa_offset = data & ~(PAGE_MASK | 1); |
1954 | 1955 | ||
1955 | /* Check that the address is 32-byte aligned. */ | ||
1956 | if (gpa_offset & (sizeof(struct pvclock_vcpu_time_info) - 1)) | ||
1957 | break; | ||
1958 | |||
1959 | if (kvm_gfn_to_hva_cache_init(vcpu->kvm, | 1956 | if (kvm_gfn_to_hva_cache_init(vcpu->kvm, |
1960 | &vcpu->arch.pv_time, data & ~1ULL)) | 1957 | &vcpu->arch.pv_time, data & ~1ULL, |
1958 | sizeof(struct pvclock_vcpu_time_info))) | ||
1961 | vcpu->arch.pv_time_enabled = false; | 1959 | vcpu->arch.pv_time_enabled = false; |
1962 | else | 1960 | else |
1963 | vcpu->arch.pv_time_enabled = true; | 1961 | vcpu->arch.pv_time_enabled = true; |
@@ -1977,7 +1975,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) | |||
1977 | return 1; | 1975 | return 1; |
1978 | 1976 | ||
1979 | if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.st.stime, | 1977 | if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.st.stime, |
1980 | data & KVM_STEAL_VALID_BITS)) | 1978 | data & KVM_STEAL_VALID_BITS, |
1979 | sizeof(struct kvm_steal_time))) | ||
1981 | return 1; | 1980 | return 1; |
1982 | 1981 | ||
1983 | vcpu->arch.st.msr_val = data; | 1982 | vcpu->arch.st.msr_val = data; |
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index cad77fe09d77..c13958251927 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h | |||
@@ -518,7 +518,7 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, | |||
518 | int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, | 518 | int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, |
519 | void *data, unsigned long len); | 519 | void *data, unsigned long len); |
520 | int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, | 520 | int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, |
521 | gpa_t gpa); | 521 | gpa_t gpa, unsigned long len); |
522 | int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); | 522 | int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); |
523 | int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); | 523 | int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); |
524 | struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); | 524 | struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); |
diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h index fa7cc7244cbd..b0bcce0ddc95 100644 --- a/include/linux/kvm_types.h +++ b/include/linux/kvm_types.h | |||
@@ -71,6 +71,7 @@ struct gfn_to_hva_cache { | |||
71 | u64 generation; | 71 | u64 generation; |
72 | gpa_t gpa; | 72 | gpa_t gpa; |
73 | unsigned long hva; | 73 | unsigned long hva; |
74 | unsigned long len; | ||
74 | struct kvm_memory_slot *memslot; | 75 | struct kvm_memory_slot *memslot; |
75 | }; | 76 | }; |
76 | 77 | ||
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index adc68feb5c5a..f18013f09e68 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -1541,21 +1541,38 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, | |||
1541 | } | 1541 | } |
1542 | 1542 | ||
1543 | int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, | 1543 | int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, |
1544 | gpa_t gpa) | 1544 | gpa_t gpa, unsigned long len) |
1545 | { | 1545 | { |
1546 | struct kvm_memslots *slots = kvm_memslots(kvm); | 1546 | struct kvm_memslots *slots = kvm_memslots(kvm); |
1547 | int offset = offset_in_page(gpa); | 1547 | int offset = offset_in_page(gpa); |
1548 | gfn_t gfn = gpa >> PAGE_SHIFT; | 1548 | gfn_t start_gfn = gpa >> PAGE_SHIFT; |
1549 | gfn_t end_gfn = (gpa + len - 1) >> PAGE_SHIFT; | ||
1550 | gfn_t nr_pages_needed = end_gfn - start_gfn + 1; | ||
1551 | gfn_t nr_pages_avail; | ||
1549 | 1552 | ||
1550 | ghc->gpa = gpa; | 1553 | ghc->gpa = gpa; |
1551 | ghc->generation = slots->generation; | 1554 | ghc->generation = slots->generation; |
1552 | ghc->memslot = gfn_to_memslot(kvm, gfn); | 1555 | ghc->len = len; |
1553 | ghc->hva = gfn_to_hva_many(ghc->memslot, gfn, NULL); | 1556 | ghc->memslot = gfn_to_memslot(kvm, start_gfn); |
1554 | if (!kvm_is_error_hva(ghc->hva)) | 1557 | ghc->hva = gfn_to_hva_many(ghc->memslot, start_gfn, &nr_pages_avail); |
1558 | if (!kvm_is_error_hva(ghc->hva) && nr_pages_avail >= nr_pages_needed) { | ||
1555 | ghc->hva += offset; | 1559 | ghc->hva += offset; |
1556 | else | 1560 | } else { |
1557 | return -EFAULT; | 1561 | /* |
1558 | 1562 | * If the requested region crosses two memslots, we still | |
1563 | * verify that the entire region is valid here. | ||
1564 | */ | ||
1565 | while (start_gfn <= end_gfn) { | ||
1566 | ghc->memslot = gfn_to_memslot(kvm, start_gfn); | ||
1567 | ghc->hva = gfn_to_hva_many(ghc->memslot, start_gfn, | ||
1568 | &nr_pages_avail); | ||
1569 | if (kvm_is_error_hva(ghc->hva)) | ||
1570 | return -EFAULT; | ||
1571 | start_gfn += nr_pages_avail; | ||
1572 | } | ||
1573 | /* Use the slow path for cross page reads and writes. */ | ||
1574 | ghc->memslot = NULL; | ||
1575 | } | ||
1559 | return 0; | 1576 | return 0; |
1560 | } | 1577 | } |
1561 | EXPORT_SYMBOL_GPL(kvm_gfn_to_hva_cache_init); | 1578 | EXPORT_SYMBOL_GPL(kvm_gfn_to_hva_cache_init); |
@@ -1566,8 +1583,13 @@ int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, | |||
1566 | struct kvm_memslots *slots = kvm_memslots(kvm); | 1583 | struct kvm_memslots *slots = kvm_memslots(kvm); |
1567 | int r; | 1584 | int r; |
1568 | 1585 | ||
1586 | BUG_ON(len > ghc->len); | ||
1587 | |||
1569 | if (slots->generation != ghc->generation) | 1588 | if (slots->generation != ghc->generation) |
1570 | kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa); | 1589 | kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa, ghc->len); |
1590 | |||
1591 | if (unlikely(!ghc->memslot)) | ||
1592 | return kvm_write_guest(kvm, ghc->gpa, data, len); | ||
1571 | 1593 | ||
1572 | if (kvm_is_error_hva(ghc->hva)) | 1594 | if (kvm_is_error_hva(ghc->hva)) |
1573 | return -EFAULT; | 1595 | return -EFAULT; |
@@ -1587,8 +1609,13 @@ int kvm_read_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, | |||
1587 | struct kvm_memslots *slots = kvm_memslots(kvm); | 1609 | struct kvm_memslots *slots = kvm_memslots(kvm); |
1588 | int r; | 1610 | int r; |
1589 | 1611 | ||
1612 | BUG_ON(len > ghc->len); | ||
1613 | |||
1590 | if (slots->generation != ghc->generation) | 1614 | if (slots->generation != ghc->generation) |
1591 | kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa); | 1615 | kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa, ghc->len); |
1616 | |||
1617 | if (unlikely(!ghc->memslot)) | ||
1618 | return kvm_read_guest(kvm, ghc->gpa, data, len); | ||
1592 | 1619 | ||
1593 | if (kvm_is_error_hva(ghc->hva)) | 1620 | if (kvm_is_error_hva(ghc->hva)) |
1594 | return -EFAULT; | 1621 | return -EFAULT; |