Message ID | 1501687459-10540-1-git-send-email-peng.hao2@zte.com.cn (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 02/08/2017 17:24, Peng Hao wrote: > some versions of windows guest access rtc frequently because of > rtc as system tick.guest access rtc like this: write register index > to 0x70, then write or read data from 0x71. writing 0x70 port is > just as index and do nothing else. So writing 0x70 is not necessory > to exit to userspace every time and caching rtc register index in kvm > can reduce VM-EXIT time. > without my patch, get the vm-exit time of accessing rtc 0x70 using > perf tools: (guest OS : windows 7 64bit) > IO Port Access Samples Samples% Time% Min Time Max Time Avg time > 0x70:POUT 86 30.99% 74.59% 9us 29us 10.75us (+- 3.41%) > > with my patch > IO Port Access Samples Samples% Time% Min Time Max Time Avg time > 0x70:POUT 106 32.02% 29.47% 0us 10us 1.57us (+- 7.38%) > > the patch is a part of optimizing rtc 0x70 port access.another is in > qemu. > > Signed-off-by: Peng Hao <peng.hao2@zte.com.cn> > Reviewed-by: Liu Yi <liu.yi24@zte.com.cn> Technically, bit 7 of port 0x70 disables NMI, but QEMU doesn't implement that part so I guess that's fine. How would userspace read the index? Could you instead extend the existing coalesced MMIO support to I/O ports? Paolo > --- > arch/x86/include/asm/kvm_host.h | 1 + > arch/x86/kvm/Makefile | 2 +- > arch/x86/kvm/vrtc.c | 101 ++++++++++++++++++++++++++++++++++++++++ > arch/x86/kvm/vrtc.h | 19 ++++++++ > arch/x86/kvm/x86.c | 15 ++++++ > include/uapi/linux/kvm.h | 2 + > 6 files changed, 139 insertions(+), 1 deletion(-) > create mode 100644 arch/x86/kvm/vrtc.c > create mode 100644 arch/x86/kvm/vrtc.h > > diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h > index 1588e9e..41fde27 100644 > --- a/arch/x86/include/asm/kvm_host.h > +++ b/arch/x86/include/asm/kvm_host.h > @@ -761,6 +761,7 @@ struct kvm_arch { > struct kvm_pic *vpic; > struct kvm_ioapic *vioapic; > struct kvm_pit *vpit; > + struct kvm_vrtc *vrtc; > atomic_t vapics_in_nmi_mode; > struct mutex apic_map_lock; > struct kvm_apic_map *apic_map; > diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile > index 09d4b17..dfd4364 100644 > --- a/arch/x86/kvm/Makefile > +++ b/arch/x86/kvm/Makefile > @@ -13,7 +13,7 @@ kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o > > kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \ > i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ > - hyperv.o page_track.o debugfs.o > + hyperv.o page_track.o debugfs.o vrtc.o > > kvm-intel-y += vmx.o pmu_intel.o > kvm-amd-y += svm.o pmu_amd.o > diff --git a/arch/x86/kvm/vrtc.c b/arch/x86/kvm/vrtc.c > new file mode 100644 > index 0000000..c8b45c0 > --- /dev/null > +++ b/arch/x86/kvm/vrtc.c > @@ -0,0 +1,101 @@ > +#include <linux/kvm_host.h> > +#include <linux/slab.h> > + > +#include "x86.h" > +#include "vrtc.h" > + > +static inline struct kvm_vrtc *dev_to_vrtc(struct kvm_io_device *dev) > +{ > + return container_of(dev, struct kvm_vrtc, dev); > +} > + > +static int vrtc_ioport_read(struct kvm_vcpu *vcpu, > + struct kvm_io_device *dev, > + gpa_t addr, int len, void *data) > +{ > + return 0xff; > +} > + > +static int vrtc_ioport_write(struct kvm_vcpu *vcpu, > + struct kvm_io_device *dev, > + gpa_t addr, int len, const void *val) > +{ > + struct kvm_vrtc *vrtc = dev_to_vrtc(dev); > + unsigned char data = *(unsigned char *)val; > + int ret = -EOPNOTSUPP; > + > + if (addr != KVM_VRTC_BASE_ADDRESS) > + return -EOPNOTSUPP; > + > + spin_lock(&vrtc->lock); > + if (vrtc->prev_reg_index == 0xff) { > + vrtc->prev_reg_index = data; > + goto out; > + } > + > + if (data == vrtc->prev_reg_index) { > + ret = 0; > + goto out; > + } else { > + vrtc->prev_reg_index = data; > + goto out; > + } > + > +out: > + spin_unlock(&vrtc->lock); > + return ret; > +} > + > +static const struct kvm_io_device_ops vrtc_dev_ops = { > + .read = vrtc_ioport_read, > + .write = vrtc_ioport_write, > +}; > + > +struct kvm_vrtc *kvm_create_vrtc(struct kvm *kvm) > +{ > + struct kvm_vrtc *vrtc; > + int ret; > + > + vrtc = kzalloc(sizeof(struct kvm_vrtc), GFP_KERNEL); > + if (!vrtc) > + return NULL; > + > + spin_lock_init(&vrtc->lock); > + vrtc->kvm = kvm; > + kvm_vrtc_reset(vrtc); > + mutex_lock(&kvm->slots_lock); > + > + kvm_iodevice_init(&vrtc->dev, &vrtc_dev_ops); > + ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, KVM_VRTC_BASE_ADDRESS, > + 1, &vrtc->dev); > + if (ret < 0) > + goto fail_register_vrtc; > + > + mutex_unlock(&kvm->slots_lock); > + return vrtc; > + > +fail_register_vrtc: > + mutex_unlock(&kvm->slots_lock); > + kfree(vrtc); > + return NULL; > +} > + > +void kvm_vrtc_reset(struct kvm_vrtc *vrtc) > +{ > + vrtc->prev_reg_index = 0xff; > +} > + > +void kvm_vrtc_destroy(struct kvm *kvm) > +{ > + struct kvm_vrtc *vrtc = kvm->arch.vrtc; > + > + if (!vrtc) > + return; > + mutex_lock(&kvm->slots_lock); > + kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vrtc->dev); > + mutex_unlock(&kvm->slots_lock); > + > + kvm->arch.vrtc = NULL; > + kfree(vrtc); > +} > + > diff --git a/arch/x86/kvm/vrtc.h b/arch/x86/kvm/vrtc.h > new file mode 100644 > index 0000000..ea434ce > --- /dev/null > +++ b/arch/x86/kvm/vrtc.h > @@ -0,0 +1,19 @@ > +#ifndef __VRTC_H > +#define __VRTC_H > + > +#include <kvm/iodev.h> > +#include <linux/spinlock.h> > + > +struct kvm_vrtc { > + spinlock_t lock; > + struct kvm_io_device dev; > + struct kvm *kvm; > + unsigned char prev_reg_index; > +}; > + > + > +#define KVM_VRTC_BASE_ADDRESS 0x70 > +struct kvm_vrtc *kvm_create_vrtc(struct kvm *kvm); > +void kvm_vrtc_destroy(struct kvm *kvm); > +void kvm_vrtc_reset(struct kvm_vrtc *vrtc); > +#endif > diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c > index 6c7266f..426cf82 100644 > --- a/arch/x86/kvm/x86.c > +++ b/arch/x86/kvm/x86.c > @@ -29,6 +29,7 @@ > #include "cpuid.h" > #include "pmu.h" > #include "hyperv.h" > +#include "vrtc.h" > > #include <linux/clocksource.h> > #include <linux/interrupt.h> > @@ -4033,6 +4034,19 @@ long kvm_arch_vm_ioctl(struct file *filp, > mutex_unlock(&kvm->lock); > break; > } > + case KVM_CREATE_VRTC: > + mutex_lock(&kvm->lock); > + r = -EEXIST; > + if (kvm->arch.vrtc) > + goto create_vrtc_unlock; > + > + r = -ENOMEM; > + kvm->arch.vrtc = kvm_create_vrtc(kvm); > + if (kvm->arch.vrtc) > + r = 0; > +create_vrtc_unlock: > + mutex_unlock(&kvm->lock); > + break; > case KVM_CREATE_PIT: > u.pit_config.flags = KVM_PIT_SPEAKER_DUMMY; > goto create_pit; > @@ -8168,6 +8182,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) > if (kvm_x86_ops->vm_destroy) > kvm_x86_ops->vm_destroy(kvm); > kvm_pic_destroy(kvm); > + kvm_vrtc_destroy(kvm); > kvm_ioapic_destroy(kvm); > kvm_free_vcpus(kvm); > kvfree(rcu_dereference_check(kvm->arch.apic_map, 1)); > diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h > index c0b6dfe..ce503f35 100644 > --- a/include/uapi/linux/kvm.h > +++ b/include/uapi/linux/kvm.h > @@ -927,6 +927,7 @@ struct kvm_ppc_resize_hpt { > #define KVM_CAP_S390_CMMA_MIGRATION 145 > #define KVM_CAP_PPC_FWNMI 146 > #define KVM_CAP_PPC_SMT_POSSIBLE 147 > +#define KVM_CAP_VRTC 150 > > #ifdef KVM_CAP_IRQ_ROUTING > > @@ -1256,6 +1257,7 @@ struct kvm_s390_ucas_mapping { > #define KVM_PPC_CONFIGURE_V3_MMU _IOW(KVMIO, 0xaf, struct kvm_ppc_mmuv3_cfg) > /* Available with KVM_CAP_PPC_RADIX_MMU */ > #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) > +#define KVM_CREATE_VRTC _IO(KVMIO, 0xba) > > /* ioctl for vm fd */ > #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) >
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 1588e9e..41fde27 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -761,6 +761,7 @@ struct kvm_arch { struct kvm_pic *vpic; struct kvm_ioapic *vioapic; struct kvm_pit *vpit; + struct kvm_vrtc *vrtc; atomic_t vapics_in_nmi_mode; struct mutex apic_map_lock; struct kvm_apic_map *apic_map; diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 09d4b17..dfd4364 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -13,7 +13,7 @@ kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \ i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ - hyperv.o page_track.o debugfs.o + hyperv.o page_track.o debugfs.o vrtc.o kvm-intel-y += vmx.o pmu_intel.o kvm-amd-y += svm.o pmu_amd.o diff --git a/arch/x86/kvm/vrtc.c b/arch/x86/kvm/vrtc.c new file mode 100644 index 0000000..c8b45c0 --- /dev/null +++ b/arch/x86/kvm/vrtc.c @@ -0,0 +1,101 @@ +#include <linux/kvm_host.h> +#include <linux/slab.h> + +#include "x86.h" +#include "vrtc.h" + +static inline struct kvm_vrtc *dev_to_vrtc(struct kvm_io_device *dev) +{ + return container_of(dev, struct kvm_vrtc, dev); +} + +static int vrtc_ioport_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, void *data) +{ + return 0xff; +} + +static int vrtc_ioport_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, const void *val) +{ + struct kvm_vrtc *vrtc = dev_to_vrtc(dev); + unsigned char data = *(unsigned char *)val; + int ret = -EOPNOTSUPP; + + if (addr != KVM_VRTC_BASE_ADDRESS) + return -EOPNOTSUPP; + + spin_lock(&vrtc->lock); + if (vrtc->prev_reg_index == 0xff) { + vrtc->prev_reg_index = data; + goto out; + } + + if (data == vrtc->prev_reg_index) { + ret = 0; + goto out; + } else { + vrtc->prev_reg_index = data; + goto out; + } + +out: + spin_unlock(&vrtc->lock); + return ret; +} + +static const struct kvm_io_device_ops vrtc_dev_ops = { + .read = vrtc_ioport_read, + .write = vrtc_ioport_write, +}; + +struct kvm_vrtc *kvm_create_vrtc(struct kvm *kvm) +{ + struct kvm_vrtc *vrtc; + int ret; + + vrtc = kzalloc(sizeof(struct kvm_vrtc), GFP_KERNEL); + if (!vrtc) + return NULL; + + spin_lock_init(&vrtc->lock); + vrtc->kvm = kvm; + kvm_vrtc_reset(vrtc); + mutex_lock(&kvm->slots_lock); + + kvm_iodevice_init(&vrtc->dev, &vrtc_dev_ops); + ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, KVM_VRTC_BASE_ADDRESS, + 1, &vrtc->dev); + if (ret < 0) + goto fail_register_vrtc; + + mutex_unlock(&kvm->slots_lock); + return vrtc; + +fail_register_vrtc: + mutex_unlock(&kvm->slots_lock); + kfree(vrtc); + return NULL; +} + +void kvm_vrtc_reset(struct kvm_vrtc *vrtc) +{ + vrtc->prev_reg_index = 0xff; +} + +void kvm_vrtc_destroy(struct kvm *kvm) +{ + struct kvm_vrtc *vrtc = kvm->arch.vrtc; + + if (!vrtc) + return; + mutex_lock(&kvm->slots_lock); + kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vrtc->dev); + mutex_unlock(&kvm->slots_lock); + + kvm->arch.vrtc = NULL; + kfree(vrtc); +} + diff --git a/arch/x86/kvm/vrtc.h b/arch/x86/kvm/vrtc.h new file mode 100644 index 0000000..ea434ce --- /dev/null +++ b/arch/x86/kvm/vrtc.h @@ -0,0 +1,19 @@ +#ifndef __VRTC_H +#define __VRTC_H + +#include <kvm/iodev.h> +#include <linux/spinlock.h> + +struct kvm_vrtc { + spinlock_t lock; + struct kvm_io_device dev; + struct kvm *kvm; + unsigned char prev_reg_index; +}; + + +#define KVM_VRTC_BASE_ADDRESS 0x70 +struct kvm_vrtc *kvm_create_vrtc(struct kvm *kvm); +void kvm_vrtc_destroy(struct kvm *kvm); +void kvm_vrtc_reset(struct kvm_vrtc *vrtc); +#endif diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6c7266f..426cf82 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -29,6 +29,7 @@ #include "cpuid.h" #include "pmu.h" #include "hyperv.h" +#include "vrtc.h" #include <linux/clocksource.h> #include <linux/interrupt.h> @@ -4033,6 +4034,19 @@ long kvm_arch_vm_ioctl(struct file *filp, mutex_unlock(&kvm->lock); break; } + case KVM_CREATE_VRTC: + mutex_lock(&kvm->lock); + r = -EEXIST; + if (kvm->arch.vrtc) + goto create_vrtc_unlock; + + r = -ENOMEM; + kvm->arch.vrtc = kvm_create_vrtc(kvm); + if (kvm->arch.vrtc) + r = 0; +create_vrtc_unlock: + mutex_unlock(&kvm->lock); + break; case KVM_CREATE_PIT: u.pit_config.flags = KVM_PIT_SPEAKER_DUMMY; goto create_pit; @@ -8168,6 +8182,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) if (kvm_x86_ops->vm_destroy) kvm_x86_ops->vm_destroy(kvm); kvm_pic_destroy(kvm); + kvm_vrtc_destroy(kvm); kvm_ioapic_destroy(kvm); kvm_free_vcpus(kvm); kvfree(rcu_dereference_check(kvm->arch.apic_map, 1)); diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index c0b6dfe..ce503f35 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -927,6 +927,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_CMMA_MIGRATION 145 #define KVM_CAP_PPC_FWNMI 146 #define KVM_CAP_PPC_SMT_POSSIBLE 147 +#define KVM_CAP_VRTC 150 #ifdef KVM_CAP_IRQ_ROUTING @@ -1256,6 +1257,7 @@ struct kvm_s390_ucas_mapping { #define KVM_PPC_CONFIGURE_V3_MMU _IOW(KVMIO, 0xaf, struct kvm_ppc_mmuv3_cfg) /* Available with KVM_CAP_PPC_RADIX_MMU */ #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) +#define KVM_CREATE_VRTC _IO(KVMIO, 0xba) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)