Просмотр исходного кода

Merge git://git.kernel.org/pub/scm/virt/kvm/kvm

Pull KVM fix from Marcelo Tosatti:
 "Memory leak and oops on the x86 mmu code, and sanitization of the
  KVM_IRQFD ioctl."

* git://git.kernel.org/pub/scm/virt/kvm/kvm:
  KVM: MMU: fix shrinking page from the empty mmu
  KVM: fix fault page leak
  KVM: Sanitize KVM_IRQFD flags
  KVM: Add missing KVM_IRQFD API documentation
  KVM: Pass kvm_irqfd to functions
Linus Torvalds 13 лет назад
Родитель
Сommit
6bc51545da
5 измененных файлов с 37 добавлено и 13 удалено
  1. 17 0
      Documentation/virtual/kvm/api.txt
  2. 3 0
      arch/x86/kvm/mmu.c
  3. 2 2
      include/linux/kvm_host.h
  4. 13 10
      virt/kvm/eventfd.c
  5. 2 1
      virt/kvm/kvm_main.c

+ 17 - 0
Documentation/virtual/kvm/api.txt

@@ -1930,6 +1930,23 @@ The "pte_enc" field provides a value that can OR'ed into the hash
 PTE's RPN field (ie, it needs to be shifted left by 12 to OR it
 PTE's RPN field (ie, it needs to be shifted left by 12 to OR it
 into the hash PTE second double word).
 into the hash PTE second double word).
 
 
+4.75 KVM_IRQFD
+
+Capability: KVM_CAP_IRQFD
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_irqfd (in)
+Returns: 0 on success, -1 on error
+
+Allows setting an eventfd to directly trigger a guest interrupt.
+kvm_irqfd.fd specifies the file descriptor to use as the eventfd and
+kvm_irqfd.gsi specifies the irqchip pin toggled by this event.  When
+an event is tiggered on the eventfd, an interrupt is injected into
+the guest using the specified gsi pin.  The irqfd is removed using
+the KVM_IRQFD_FLAG_DEASSIGN flag, specifying both kvm_irqfd.fd
+and kvm_irqfd.gsi.
+
+
 5. The kvm_run structure
 5. The kvm_run structure
 ------------------------
 ------------------------
 
 

+ 3 - 0
arch/x86/kvm/mmu.c

@@ -3934,6 +3934,9 @@ static void kvm_mmu_remove_some_alloc_mmu_pages(struct kvm *kvm,
 {
 {
 	struct kvm_mmu_page *page;
 	struct kvm_mmu_page *page;
 
 
+	if (list_empty(&kvm->arch.active_mmu_pages))
+		return;
+
 	page = container_of(kvm->arch.active_mmu_pages.prev,
 	page = container_of(kvm->arch.active_mmu_pages.prev,
 			    struct kvm_mmu_page, link);
 			    struct kvm_mmu_page, link);
 	kvm_mmu_prepare_zap_page(kvm, page, invalid_list);
 	kvm_mmu_prepare_zap_page(kvm, page, invalid_list);

+ 2 - 2
include/linux/kvm_host.h

@@ -815,7 +815,7 @@ static inline void kvm_free_irq_routing(struct kvm *kvm) {}
 #ifdef CONFIG_HAVE_KVM_EVENTFD
 #ifdef CONFIG_HAVE_KVM_EVENTFD
 
 
 void kvm_eventfd_init(struct kvm *kvm);
 void kvm_eventfd_init(struct kvm *kvm);
-int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags);
+int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args);
 void kvm_irqfd_release(struct kvm *kvm);
 void kvm_irqfd_release(struct kvm *kvm);
 void kvm_irq_routing_update(struct kvm *, struct kvm_irq_routing_table *);
 void kvm_irq_routing_update(struct kvm *, struct kvm_irq_routing_table *);
 int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);
 int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);
@@ -824,7 +824,7 @@ int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);
 
 
 static inline void kvm_eventfd_init(struct kvm *kvm) {}
 static inline void kvm_eventfd_init(struct kvm *kvm) {}
 
 
-static inline int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags)
+static inline int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args)
 {
 {
 	return -EINVAL;
 	return -EINVAL;
 }
 }

+ 13 - 10
virt/kvm/eventfd.c

@@ -198,7 +198,7 @@ static void irqfd_update(struct kvm *kvm, struct _irqfd *irqfd,
 }
 }
 
 
 static int
 static int
-kvm_irqfd_assign(struct kvm *kvm, int fd, int gsi)
+kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
 {
 {
 	struct kvm_irq_routing_table *irq_rt;
 	struct kvm_irq_routing_table *irq_rt;
 	struct _irqfd *irqfd, *tmp;
 	struct _irqfd *irqfd, *tmp;
@@ -212,12 +212,12 @@ kvm_irqfd_assign(struct kvm *kvm, int fd, int gsi)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	irqfd->kvm = kvm;
 	irqfd->kvm = kvm;
-	irqfd->gsi = gsi;
+	irqfd->gsi = args->gsi;
 	INIT_LIST_HEAD(&irqfd->list);
 	INIT_LIST_HEAD(&irqfd->list);
 	INIT_WORK(&irqfd->inject, irqfd_inject);
 	INIT_WORK(&irqfd->inject, irqfd_inject);
 	INIT_WORK(&irqfd->shutdown, irqfd_shutdown);
 	INIT_WORK(&irqfd->shutdown, irqfd_shutdown);
 
 
-	file = eventfd_fget(fd);
+	file = eventfd_fget(args->fd);
 	if (IS_ERR(file)) {
 	if (IS_ERR(file)) {
 		ret = PTR_ERR(file);
 		ret = PTR_ERR(file);
 		goto fail;
 		goto fail;
@@ -298,19 +298,19 @@ kvm_eventfd_init(struct kvm *kvm)
  * shutdown any irqfd's that match fd+gsi
  * shutdown any irqfd's that match fd+gsi
  */
  */
 static int
 static int
-kvm_irqfd_deassign(struct kvm *kvm, int fd, int gsi)
+kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args)
 {
 {
 	struct _irqfd *irqfd, *tmp;
 	struct _irqfd *irqfd, *tmp;
 	struct eventfd_ctx *eventfd;
 	struct eventfd_ctx *eventfd;
 
 
-	eventfd = eventfd_ctx_fdget(fd);
+	eventfd = eventfd_ctx_fdget(args->fd);
 	if (IS_ERR(eventfd))
 	if (IS_ERR(eventfd))
 		return PTR_ERR(eventfd);
 		return PTR_ERR(eventfd);
 
 
 	spin_lock_irq(&kvm->irqfds.lock);
 	spin_lock_irq(&kvm->irqfds.lock);
 
 
 	list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) {
 	list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) {
-		if (irqfd->eventfd == eventfd && irqfd->gsi == gsi) {
+		if (irqfd->eventfd == eventfd && irqfd->gsi == args->gsi) {
 			/*
 			/*
 			 * This rcu_assign_pointer is needed for when
 			 * This rcu_assign_pointer is needed for when
 			 * another thread calls kvm_irq_routing_update before
 			 * another thread calls kvm_irq_routing_update before
@@ -338,12 +338,15 @@ kvm_irqfd_deassign(struct kvm *kvm, int fd, int gsi)
 }
 }
 
 
 int
 int
-kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags)
+kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args)
 {
 {
-	if (flags & KVM_IRQFD_FLAG_DEASSIGN)
-		return kvm_irqfd_deassign(kvm, fd, gsi);
+	if (args->flags & ~KVM_IRQFD_FLAG_DEASSIGN)
+		return -EINVAL;
+
+	if (args->flags & KVM_IRQFD_FLAG_DEASSIGN)
+		return kvm_irqfd_deassign(kvm, args);
 
 
-	return kvm_irqfd_assign(kvm, fd, gsi);
+	return kvm_irqfd_assign(kvm, args);
 }
 }
 
 
 /*
 /*

+ 2 - 1
virt/kvm/kvm_main.c

@@ -2047,7 +2047,7 @@ static long kvm_vm_ioctl(struct file *filp,
 		r = -EFAULT;
 		r = -EFAULT;
 		if (copy_from_user(&data, argp, sizeof data))
 		if (copy_from_user(&data, argp, sizeof data))
 			goto out;
 			goto out;
-		r = kvm_irqfd(kvm, data.fd, data.gsi, data.flags);
+		r = kvm_irqfd(kvm, &data);
 		break;
 		break;
 	}
 	}
 	case KVM_IOEVENTFD: {
 	case KVM_IOEVENTFD: {
@@ -2845,6 +2845,7 @@ void kvm_exit(void)
 	kvm_arch_hardware_unsetup();
 	kvm_arch_hardware_unsetup();
 	kvm_arch_exit();
 	kvm_arch_exit();
 	free_cpumask_var(cpus_hardware_enabled);
 	free_cpumask_var(cpus_hardware_enabled);
+	__free_page(fault_page);
 	__free_page(hwpoison_page);
 	__free_page(hwpoison_page);
 	__free_page(bad_page);
 	__free_page(bad_page);
 }
 }