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
