aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2011-04-27 12:42:18 -0400
committerAvi Kivity <avi@redhat.com>2011-05-22 08:47:45 -0400
commit2fb92db1ec08f3235c500e7f460eeb78092d844e (patch)
tree9cc23153b938a29969baa86e8075c507a7a05570 /arch
parent1aa366163b8b69f660cf94fd5062fa44859e4318 (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.h1
-rw-r--r--arch/x86/kvm/vmx.c102
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
141enum { 142enum {
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
181enum 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
177static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) 190static 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
662static void vmx_segment_cache_clear(struct vcpu_vmx *vmx)
663{
664 vmx->segment_cache.bitmask = 0;
665}
666
667static 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
682static 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
691static 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
700static 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
709static 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
649static void update_exception_bitmap(struct kvm_vcpu *vcpu) 718static 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);
2114use_saved_rmode_seg: 2192use_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
2128static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg) 2206static 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
2140static int __vmx_get_cpl(struct kvm_vcpu *vcpu) 2217static 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
2152static int vmx_get_cpl(struct kvm_vcpu *vcpu) 2229static 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
2230static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) 2309static 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