History

KVM: Allow cross page reads and writes from cached translations.

This patch adds support for kvm_gfn_to_hva_cache_init functions for reads and writes that will cross a page. If the range falls within the same memslot, then this will be a fast operation. If the range is split between two memslots, then the slower kvm_read_guest and kvm_write_guest are used. Tested: Test against kvm_clock unit tests.

Andrew Honig committed 13 years ago

Commit 8f964521 parent 09a6e1f5 files · +46 −19Browse files

Unified · Split

5 files
  1. arch/x86/kvm/lapic.c +1 −1
  2. arch/x86/kvm/x86.c +6 −7
  3. include/linux/kvm_host.h +1 −1
  4. include/linux/kvm_types.h +1 −0
  5. virt/kvm/kvm_main.c +37 −10
arch/x86/kvm/lapic.c+1−1
@@ -1857,7 +1857,7 @@ int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data) · 1856 unmodified lines
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
1863void kvm_lapic_init(void)1863void kvm_lapic_init(void)
arch/x86/kvm/x86.c+6−7
@@ -1823,7 +1823,8 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) · 1822 unmodified lines
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) · 122 unmodified lines
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 else1960 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) · 13 unmodified lines
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;
include/linux/kvm_host.h+1−1
@@ -518,7 +518,7 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, · 517 unmodified lines
518int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,518int 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);
520int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,520int 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);
522int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);522int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
523int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);523int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
524struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);524struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
include/linux/kvm_types.h+1−0
@@ -71,6 +71,7 @@ struct gfn_to_hva_cache { · 70 unmodified lines
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
virt/kvm/kvm_main.c+37−10
@@ -1541,21 +1541,38 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, · 1540 unmodified lines
1541}1541}
1542 1542
1543int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,1543int 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 else1560 } 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}
1561EXPORT_SYMBOL_GPL(kvm_gfn_to_hva_cache_init);1578EXPORT_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, · 4 unmodified lines
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, · 13 unmodified lines
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;