From patchwork Fri May 20 17:12:40 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kiszka X-Patchwork-Id: 804272 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p4KHCskX019605 for ; Fri, 20 May 2011 17:12:55 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933435Ab1ETRMs (ORCPT ); Fri, 20 May 2011 13:12:48 -0400 Received: from thoth.sbs.de ([192.35.17.2]:34741 "EHLO thoth.sbs.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933366Ab1ETRMr (ORCPT ); Fri, 20 May 2011 13:12:47 -0400 Received: from mail1.siemens.de (localhost [127.0.0.1]) by thoth.sbs.de (8.13.6/8.13.6) with ESMTP id p4KHChwB031375; Fri, 20 May 2011 19:12:43 +0200 Received: from mchn199C.mchp.siemens.de ([139.25.109.49]) by mail1.siemens.de (8.13.6/8.13.6) with ESMTP id p4KHCeFE023636; Fri, 20 May 2011 19:12:43 +0200 From: Jan Kiszka To: Avi Kivity , Marcelo Tosatti Cc: kvm@vger.kernel.org Subject: [PATCH 10/10] qemu-kvm: Rework ioport access management Date: Fri, 20 May 2011 19:12:40 +0200 Message-Id: <4a239896884d57ea7e43231908aad9d737c45e70.1305911554.git.jan.kiszka@siemens.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: References: In-Reply-To: References: Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Fri, 20 May 2011 17:12:55 +0000 (UTC) Clean up the interface for enabling/disabling direct ioport access for assigned devices. There is now only a register and a deregister service. Both are automatically updating the access on all vcpus. Besides that, there is an update service for newly created VCPUs that applies all currently registered regions. Signed-off-by: Jan Kiszka --- hw/device-assignment.c | 20 +++------ qemu-kvm-x86.c | 12 ++++-- qemu-kvm.c | 107 ++++++++++++++++++++++++++++++++++-------------- qemu-kvm.h | 21 ++------- 4 files changed, 96 insertions(+), 64 deletions(-) diff --git a/hw/device-assignment.c b/hw/device-assignment.c index abc38f8..57d8dc0 100644 --- a/hw/device-assignment.c +++ b/hw/device-assignment.c @@ -298,7 +298,7 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num, AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); AssignedDevRegion *region = &r_dev->v_addrs[region_num]; int first_map = (region->e_size == 0); - CPUState *env; + int r; region->e_physbase = addr; region->e_size = size; @@ -307,17 +307,11 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num, addr, region->u.r_baseport, type, size, region_num); if (first_map && region->region->resource_fd < 0) { - struct ioperm_data *data; - - data = qemu_mallocz(sizeof(struct ioperm_data)); - data->start_port = region->u.r_baseport; - data->num = region->r_size; - data->turn_on = 1; - - kvm_add_ioperm_data(data); - - for (env = first_cpu; env; env = env->next_cpu) - kvm_ioperm(env, data); + r = kvm_add_ioport_region(region->u.r_baseport, region->r_size); + if (r < 0) { + fprintf(stderr, "%s: failed to enable ioport access (%m)\n", + __func__); + } } register_ioport_read(addr, size, 1, assigned_dev_ioport_readb, @@ -832,7 +826,7 @@ static void free_assigned_device(AssignedDevice *dev) } if (pci_region->type & IORESOURCE_IO) { if (pci_region->resource_fd < 0) { - kvm_remove_ioperm_data(region->u.r_baseport, region->r_size); + kvm_remove_ioport_region(region->u.r_baseport, region->r_size); } } else if (pci_region->type & IORESOURCE_MEM) { if (region->u.r_virtbase) { diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c index 89bb692..2a01ccc 100644 --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -164,14 +164,18 @@ static int _kvm_arch_init_vcpu(CPUState *env) #ifdef KVM_EXIT_TPR_ACCESS kvm_enable_tpr_access_reporting(env); #endif - return 0; + + return kvm_update_ioport_access(env); } #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -void kvm_arch_do_ioperm(void *_data) +int kvm_arch_set_ioport_access(unsigned long start, unsigned long size, + bool enable) { - struct ioperm_data *data = _data; - ioperm(data->start_port, data->num, data->turn_on); + if (ioperm(start, size, enable) < 0) { + return -errno; + } + return 0; } #endif diff --git a/qemu-kvm.c b/qemu-kvm.c index b2387df..94e12f3 100644 --- a/qemu-kvm.c +++ b/qemu-kvm.c @@ -66,11 +66,6 @@ static int qemu_system_ready; CPUState *kvm_debug_cpu_requested; -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -/* The list of ioperm_data */ -static QLIST_HEAD(, ioperm_data) ioperm_head; -#endif - #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) static inline void set_gsi(KVMState *s, unsigned int gsi) @@ -856,19 +851,10 @@ static int kvm_main_loop_cpu(CPUState *env) static void *ap_main_loop(void *_env) { CPUState *env = _env; -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - struct ioperm_data *data = NULL; -#endif current_env = env; env->thread_id = kvm_get_thread_id(); -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - /* do ioperm for io ports of assigned devices */ - QLIST_FOREACH(data, &ioperm_head, entries) - on_vcpu(env, kvm_arch_do_ioperm, data); -#endif - pthread_mutex_lock(&qemu_mutex); cpu_single_env = env; @@ -1069,36 +1055,95 @@ void qemu_mutex_lock_iothread(void) } #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -void kvm_add_ioperm_data(struct ioperm_data *data) +typedef struct KVMIOPortRegion { + unsigned long start; + unsigned long size; + int status; + QLIST_ENTRY(KVMIOPortRegion) entry; +} KVMIOPortRegion; + +static QLIST_HEAD(, KVMIOPortRegion) ioport_regions; + +static void do_set_ioport_access(void *data) { - QLIST_INSERT_HEAD(&ioperm_head, data, entries); + KVMIOPortRegion *region = data; + bool enable = region->status > 0; + int r; + + r = kvm_arch_set_ioport_access(region->start, region->size, enable); + if (r < 0) { + region->status = r; + } else { + region->status = 1; + } } -void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num) +int kvm_add_ioport_region(unsigned long start, unsigned long size) { - struct ioperm_data *data; - - data = QLIST_FIRST(&ioperm_head); - while (data) { - struct ioperm_data *next = QLIST_NEXT(data, entries); + KVMIOPortRegion *region = qemu_mallocz(sizeof(KVMIOPortRegion)); + CPUState *env; + int r = 0; - if (data->start_port == start_port && data->num == num) { - QLIST_REMOVE(data, entries); - qemu_free(data); + region->start = start; + region->size = size; + region->status = 1; + QLIST_INSERT_HEAD(&ioport_regions, region, entry); + + if (qemu_system_ready) { + for (env = first_cpu; env != NULL; env = env->next_cpu) { + on_vcpu(env, do_set_ioport_access, region); + if (region->status < 0) { + r = region->status; + kvm_remove_ioport_region(start, size); + break; + } } - - data = next; } + return r; } -void kvm_ioperm(CPUState *env, void *data) +int kvm_remove_ioport_region(unsigned long start, unsigned long size) { - if (kvm_enabled() && qemu_system_ready) { - on_vcpu(env, kvm_arch_do_ioperm, data); + KVMIOPortRegion *region, *tmp; + CPUState *env; + int r = -ENOENT; + + QLIST_FOREACH_SAFE(region, &ioport_regions, entry, tmp) { + if (region->start == start && region->size == size) { + region->status = 0; + } + if (qemu_system_ready) { + for (env = first_cpu; env != NULL; env = env->next_cpu) { + on_vcpu(env, do_set_ioport_access, region); + } + } + QLIST_REMOVE(region, entry); + qemu_free(region); + r = 0; } + return r; } +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ -#endif +int kvm_update_ioport_access(CPUState *env) +{ +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + KVMIOPortRegion *region; + int r; + + assert(qemu_cpu_is_self(env)); + + QLIST_FOREACH(region, &ioport_regions, entry) { + bool enable = region->status > 0; + + r = kvm_arch_set_ioport_access(region->start, region->size, enable); + if (r < 0) { + return r; + } + } +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ + return 0; +} int kvm_set_boot_cpu_id(KVMState *s, uint32_t id) { diff --git a/qemu-kvm.h b/qemu-kvm.h index 57dc6aa..094aef2 100644 --- a/qemu-kvm.h +++ b/qemu-kvm.h @@ -282,31 +282,20 @@ void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write); int kvm_arch_init_irq_routing(void); -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -struct ioperm_data; +int kvm_add_ioport_region(unsigned long start, unsigned long size); +int kvm_remove_ioport_region(unsigned long start, unsigned long size); -void kvm_ioperm(CPUState *env, void *data); -void kvm_add_ioperm_data(struct ioperm_data *data); -void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num); -void kvm_arch_do_ioperm(void *_data); -#endif +int kvm_update_ioport_access(CPUState *env); +int kvm_arch_set_ioport_access(unsigned long start, unsigned long size, + bool enable); #ifdef CONFIG_KVM -#include "qemu-queue.h" - extern int kvm_irqchip; extern int kvm_pit; extern int kvm_pit_reinject; extern int kvm_nested; extern unsigned int kvm_shadow_memory; -struct ioperm_data { - unsigned long start_port; - unsigned long num; - int turn_on; - QLIST_ENTRY(ioperm_data) entries; -}; - int kvm_handle_tpr_access(CPUState *env); #else