diff options
author | Avi Kivity <avi@redhat.com> | 2011-04-27 12:42:18 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2011-05-22 08:47:45 -0400 |
commit | 2fb92db1ec08f3235c500e7f460eeb78092d844e (patch) | |
tree | 9cc23153b938a29969baa86e8075c507a7a05570 /arch | |
parent | 1aa366163b8b69f660cf94fd5062fa44859e4318 (diff) |
KVM: VMX: Cache vmcs segment fields
Since the emulator now checks segment limits and access rights, it
generates a lot more accesses to the vmcs segment fields. Undo some
of the performance hit by cacheing those fields in a read-only cache
(the entire cache is invalidated on any write, or on guest exit).
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/kvm/vmx.c | 102 |
2 files changed, 93 insertions, 10 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index afb0e69bd160..d2ac8e2ee897 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -136,6 +136,7 @@ enum kvm_reg_ex { | |||
136 | VCPU_EXREG_CR3, | 136 | VCPU_EXREG_CR3, |
137 | VCPU_EXREG_RFLAGS, | 137 | VCPU_EXREG_RFLAGS, |
138 | VCPU_EXREG_CPL, | 138 | VCPU_EXREG_CPL, |
139 | VCPU_EXREG_SEGMENTS, | ||
139 | }; | 140 | }; |
140 | 141 | ||
141 | enum { | 142 | enum { |
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 139a5cb1f5e1..4c3fa0f67469 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -162,6 +162,10 @@ struct vcpu_vmx { | |||
162 | u32 ar; | 162 | u32 ar; |
163 | } tr, es, ds, fs, gs; | 163 | } tr, es, ds, fs, gs; |
164 | } rmode; | 164 | } rmode; |
165 | struct { | ||
166 | u32 bitmask; /* 4 bits per segment (1 bit per field) */ | ||
167 | struct kvm_save_segment seg[8]; | ||
168 | } segment_cache; | ||
165 | int vpid; | 169 | int vpid; |
166 | bool emulation_required; | 170 | bool emulation_required; |
167 | 171 | ||
@@ -174,6 +178,15 @@ struct vcpu_vmx { | |||
174 | bool rdtscp_enabled; | 178 | bool rdtscp_enabled; |
175 | }; | 179 | }; |
176 | 180 | ||
181 | enum segment_cache_field { | ||
182 | SEG_FIELD_SEL = 0, | ||
183 | SEG_FIELD_BASE = 1, | ||
184 | SEG_FIELD_LIMIT = 2, | ||
185 | SEG_FIELD_AR = 3, | ||
186 | |||
187 | SEG_FIELD_NR = 4 | ||
188 | }; | ||
189 | |||
177 | static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) | 190 | static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) |
178 | { | 191 | { |
179 | return container_of(vcpu, struct vcpu_vmx, vcpu); | 192 | return container_of(vcpu, struct vcpu_vmx, vcpu); |
@@ -646,6 +659,62 @@ static void vmcs_set_bits(unsigned long field, u32 mask) | |||
646 | vmcs_writel(field, vmcs_readl(field) | mask); | 659 | vmcs_writel(field, vmcs_readl(field) | mask); |
647 | } | 660 | } |
648 | 661 | ||
662 | static void vmx_segment_cache_clear(struct vcpu_vmx *vmx) | ||
663 | { | ||
664 | vmx->segment_cache.bitmask = 0; | ||
665 | } | ||
666 | |||
667 | static bool vmx_segment_cache_test_set(struct vcpu_vmx *vmx, unsigned seg, | ||
668 | unsigned field) | ||
669 | { | ||
670 | bool ret; | ||
671 | u32 mask = 1 << (seg * SEG_FIELD_NR + field); | ||
672 | |||
673 | if (!(vmx->vcpu.arch.regs_avail & (1 << VCPU_EXREG_SEGMENTS))) { | ||
674 | vmx->vcpu.arch.regs_avail |= (1 << VCPU_EXREG_SEGMENTS); | ||
675 | vmx->segment_cache.bitmask = 0; | ||
676 | } | ||
677 | ret = vmx->segment_cache.bitmask & mask; | ||
678 | vmx->segment_cache.bitmask |= mask; | ||
679 | return ret; | ||
680 | } | ||
681 | |||
682 | static u16 vmx_read_guest_seg_selector(struct vcpu_vmx *vmx, unsigned seg) | ||
683 | { | ||
684 | u16 *p = &vmx->segment_cache.seg[seg].selector; | ||
685 | |||
686 | if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_SEL)) | ||
687 | *p = vmcs_read16(kvm_vmx_segment_fields[seg].selector); | ||
688 | return *p; | ||
689 | } | ||
690 | |||
691 | static ulong vmx_read_guest_seg_base(struct vcpu_vmx *vmx, unsigned seg) | ||
692 | { | ||
693 | ulong *p = &vmx->segment_cache.seg[seg].base; | ||
694 | |||
695 | if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_BASE)) | ||
696 | *p = vmcs_readl(kvm_vmx_segment_fields[seg].base); | ||
697 | return *p; | ||
698 | } | ||
699 | |||
700 | static u32 vmx_read_guest_seg_limit(struct vcpu_vmx *vmx, unsigned seg) | ||
701 | { | ||
702 | u32 *p = &vmx->segment_cache.seg[seg].limit; | ||
703 | |||
704 | if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_LIMIT)) | ||
705 | *p = vmcs_read32(kvm_vmx_segment_fields[seg].limit); | ||
706 | return *p; | ||
707 | } | ||
708 | |||
709 | static u32 vmx_read_guest_seg_ar(struct vcpu_vmx *vmx, unsigned seg) | ||
710 | { | ||
711 | u32 *p = &vmx->segment_cache.seg[seg].ar; | ||
712 | |||
713 | if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_AR)) | ||
714 | *p = vmcs_read32(kvm_vmx_segment_fields[seg].ar_bytes); | ||
715 | return *p; | ||
716 | } | ||
717 | |||
649 | static void update_exception_bitmap(struct kvm_vcpu *vcpu) | 718 | static void update_exception_bitmap(struct kvm_vcpu *vcpu) |
650 | { | 719 | { |
651 | u32 eb; | 720 | u32 eb; |
@@ -1271,9 +1340,11 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) | |||
1271 | break; | 1340 | break; |
1272 | #ifdef CONFIG_X86_64 | 1341 | #ifdef CONFIG_X86_64 |
1273 | case MSR_FS_BASE: | 1342 | case MSR_FS_BASE: |
1343 | vmx_segment_cache_clear(vmx); | ||
1274 | vmcs_writel(GUEST_FS_BASE, data); | 1344 | vmcs_writel(GUEST_FS_BASE, data); |
1275 | break; | 1345 | break; |
1276 | case MSR_GS_BASE: | 1346 | case MSR_GS_BASE: |
1347 | vmx_segment_cache_clear(vmx); | ||
1277 | vmcs_writel(GUEST_GS_BASE, data); | 1348 | vmcs_writel(GUEST_GS_BASE, data); |
1278 | break; | 1349 | break; |
1279 | case MSR_KERNEL_GS_BASE: | 1350 | case MSR_KERNEL_GS_BASE: |
@@ -1717,6 +1788,8 @@ static void enter_pmode(struct kvm_vcpu *vcpu) | |||
1717 | vmx->emulation_required = 1; | 1788 | vmx->emulation_required = 1; |
1718 | vmx->rmode.vm86_active = 0; | 1789 | vmx->rmode.vm86_active = 0; |
1719 | 1790 | ||
1791 | vmx_segment_cache_clear(vmx); | ||
1792 | |||
1720 | vmcs_write16(GUEST_TR_SELECTOR, vmx->rmode.tr.selector); | 1793 | vmcs_write16(GUEST_TR_SELECTOR, vmx->rmode.tr.selector); |
1721 | vmcs_writel(GUEST_TR_BASE, vmx->rmode.tr.base); | 1794 | vmcs_writel(GUEST_TR_BASE, vmx->rmode.tr.base); |
1722 | vmcs_write32(GUEST_TR_LIMIT, vmx->rmode.tr.limit); | 1795 | vmcs_write32(GUEST_TR_LIMIT, vmx->rmode.tr.limit); |
@@ -1740,6 +1813,8 @@ static void enter_pmode(struct kvm_vcpu *vcpu) | |||
1740 | fix_pmode_dataseg(VCPU_SREG_GS, &vmx->rmode.gs); | 1813 | fix_pmode_dataseg(VCPU_SREG_GS, &vmx->rmode.gs); |
1741 | fix_pmode_dataseg(VCPU_SREG_FS, &vmx->rmode.fs); | 1814 | fix_pmode_dataseg(VCPU_SREG_FS, &vmx->rmode.fs); |
1742 | 1815 | ||
1816 | vmx_segment_cache_clear(vmx); | ||
1817 | |||
1743 | vmcs_write16(GUEST_SS_SELECTOR, 0); | 1818 | vmcs_write16(GUEST_SS_SELECTOR, 0); |
1744 | vmcs_write32(GUEST_SS_AR_BYTES, 0x93); | 1819 | vmcs_write32(GUEST_SS_AR_BYTES, 0x93); |
1745 | 1820 | ||
@@ -1803,6 +1878,8 @@ static void enter_rmode(struct kvm_vcpu *vcpu) | |||
1803 | vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); | 1878 | vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); |
1804 | } | 1879 | } |
1805 | 1880 | ||
1881 | vmx_segment_cache_clear(vmx); | ||
1882 | |||
1806 | vmx->rmode.tr.selector = vmcs_read16(GUEST_TR_SELECTOR); | 1883 | vmx->rmode.tr.selector = vmcs_read16(GUEST_TR_SELECTOR); |
1807 | vmx->rmode.tr.base = vmcs_readl(GUEST_TR_BASE); | 1884 | vmx->rmode.tr.base = vmcs_readl(GUEST_TR_BASE); |
1808 | vmcs_writel(GUEST_TR_BASE, rmode_tss_base(vcpu->kvm)); | 1885 | vmcs_writel(GUEST_TR_BASE, rmode_tss_base(vcpu->kvm)); |
@@ -1879,6 +1956,8 @@ static void enter_lmode(struct kvm_vcpu *vcpu) | |||
1879 | { | 1956 | { |
1880 | u32 guest_tr_ar; | 1957 | u32 guest_tr_ar; |
1881 | 1958 | ||
1959 | vmx_segment_cache_clear(to_vmx(vcpu)); | ||
1960 | |||
1882 | guest_tr_ar = vmcs_read32(GUEST_TR_AR_BYTES); | 1961 | guest_tr_ar = vmcs_read32(GUEST_TR_AR_BYTES); |
1883 | if ((guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) { | 1962 | if ((guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) { |
1884 | printk(KERN_DEBUG "%s: tss fixup for long mode. \n", | 1963 | printk(KERN_DEBUG "%s: tss fixup for long mode. \n", |
@@ -2082,7 +2161,6 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu, | |||
2082 | struct kvm_segment *var, int seg) | 2161 | struct kvm_segment *var, int seg) |
2083 | { | 2162 | { |
2084 | struct vcpu_vmx *vmx = to_vmx(vcpu); | 2163 | struct vcpu_vmx *vmx = to_vmx(vcpu); |
2085 | struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | ||
2086 | struct kvm_save_segment *save; | 2164 | struct kvm_save_segment *save; |
2087 | u32 ar; | 2165 | u32 ar; |
2088 | 2166 | ||
@@ -2104,13 +2182,13 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu, | |||
2104 | var->limit = save->limit; | 2182 | var->limit = save->limit; |
2105 | ar = save->ar; | 2183 | ar = save->ar; |
2106 | if (seg == VCPU_SREG_TR | 2184 | if (seg == VCPU_SREG_TR |
2107 | || var->selector == vmcs_read16(sf->selector)) | 2185 | || var->selector == vmx_read_guest_seg_selector(vmx, seg)) |
2108 | goto use_saved_rmode_seg; | 2186 | goto use_saved_rmode_seg; |
2109 | } | 2187 | } |
2110 | var->base = vmcs_readl(sf->base); | 2188 | var->base = vmx_read_guest_seg_base(vmx, seg); |
2111 | var->limit = vmcs_read32(sf->limit); | 2189 | var->limit = vmx_read_guest_seg_limit(vmx, seg); |
2112 | var->selector = vmcs_read16(sf->selector); | 2190 | var->selector = vmx_read_guest_seg_selector(vmx, seg); |
2113 | ar = vmcs_read32(sf->ar_bytes); | 2191 | ar = vmx_read_guest_seg_ar(vmx, seg); |
2114 | use_saved_rmode_seg: | 2192 | use_saved_rmode_seg: |
2115 | if ((ar & AR_UNUSABLE_MASK) && !emulate_invalid_guest_state) | 2193 | if ((ar & AR_UNUSABLE_MASK) && !emulate_invalid_guest_state) |
2116 | ar = 0; | 2194 | ar = 0; |
@@ -2127,14 +2205,13 @@ use_saved_rmode_seg: | |||
2127 | 2205 | ||
2128 | static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg) | 2206 | static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg) |
2129 | { | 2207 | { |
2130 | struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | ||
2131 | struct kvm_segment s; | 2208 | struct kvm_segment s; |
2132 | 2209 | ||
2133 | if (to_vmx(vcpu)->rmode.vm86_active) { | 2210 | if (to_vmx(vcpu)->rmode.vm86_active) { |
2134 | vmx_get_segment(vcpu, &s, seg); | 2211 | vmx_get_segment(vcpu, &s, seg); |
2135 | return s.base; | 2212 | return s.base; |
2136 | } | 2213 | } |
2137 | return vmcs_readl(sf->base); | 2214 | return vmx_read_guest_seg_base(to_vmx(vcpu), seg); |
2138 | } | 2215 | } |
2139 | 2216 | ||
2140 | static int __vmx_get_cpl(struct kvm_vcpu *vcpu) | 2217 | static int __vmx_get_cpl(struct kvm_vcpu *vcpu) |
@@ -2146,7 +2223,7 @@ static int __vmx_get_cpl(struct kvm_vcpu *vcpu) | |||
2146 | && (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */ | 2223 | && (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */ |
2147 | return 3; | 2224 | return 3; |
2148 | 2225 | ||
2149 | return vmcs_read16(GUEST_CS_SELECTOR) & 3; | 2226 | return vmx_read_guest_seg_selector(to_vmx(vcpu), VCPU_SREG_CS) & 3; |
2150 | } | 2227 | } |
2151 | 2228 | ||
2152 | static int vmx_get_cpl(struct kvm_vcpu *vcpu) | 2229 | static int vmx_get_cpl(struct kvm_vcpu *vcpu) |
@@ -2188,6 +2265,8 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, | |||
2188 | struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | 2265 | struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; |
2189 | u32 ar; | 2266 | u32 ar; |
2190 | 2267 | ||
2268 | vmx_segment_cache_clear(vmx); | ||
2269 | |||
2191 | if (vmx->rmode.vm86_active && seg == VCPU_SREG_TR) { | 2270 | if (vmx->rmode.vm86_active && seg == VCPU_SREG_TR) { |
2192 | vmcs_write16(sf->selector, var->selector); | 2271 | vmcs_write16(sf->selector, var->selector); |
2193 | vmx->rmode.tr.selector = var->selector; | 2272 | vmx->rmode.tr.selector = var->selector; |
@@ -2229,7 +2308,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, | |||
2229 | 2308 | ||
2230 | static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) | 2309 | static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) |
2231 | { | 2310 | { |
2232 | u32 ar = vmcs_read32(GUEST_CS_AR_BYTES); | 2311 | u32 ar = vmx_read_guest_seg_ar(to_vmx(vcpu), VCPU_SREG_CS); |
2233 | 2312 | ||
2234 | *db = (ar >> 14) & 1; | 2313 | *db = (ar >> 14) & 1; |
2235 | *l = (ar >> 13) & 1; | 2314 | *l = (ar >> 13) & 1; |
@@ -2816,6 +2895,8 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu) | |||
2816 | if (ret != 0) | 2895 | if (ret != 0) |
2817 | goto out; | 2896 | goto out; |
2818 | 2897 | ||
2898 | vmx_segment_cache_clear(vmx); | ||
2899 | |||
2819 | seg_setup(VCPU_SREG_CS); | 2900 | seg_setup(VCPU_SREG_CS); |
2820 | /* | 2901 | /* |
2821 | * GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode | 2902 | * GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode |
@@ -4188,6 +4269,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) | |||
4188 | | (1 << VCPU_EXREG_RFLAGS) | 4269 | | (1 << VCPU_EXREG_RFLAGS) |
4189 | | (1 << VCPU_EXREG_CPL) | 4270 | | (1 << VCPU_EXREG_CPL) |
4190 | | (1 << VCPU_EXREG_PDPTR) | 4271 | | (1 << VCPU_EXREG_PDPTR) |
4272 | | (1 << VCPU_EXREG_SEGMENTS) | ||
4191 | | (1 << VCPU_EXREG_CR3)); | 4273 | | (1 << VCPU_EXREG_CR3)); |
4192 | vcpu->arch.regs_dirty = 0; | 4274 | vcpu->arch.regs_dirty = 0; |
4193 | 4275 | ||