>From 63d8eeeff86e881ab212baed4af51801dbb90460 Mon Sep 17 00:00:00 2001
From: Weidong Han <[EMAIL PROTECTED]>
Date: Fri, 14 Nov 2008 16:45:44 +0800
Subject: [PATCH] support hot remove assigned device

When hot remove assigned device, deassign it from guest, delete it from 
adev_head and remove its ioperm data.

need to assign irq in init_assigned_device(), because assigned_dev_update_irq() 
may not be invoked when hot add a device. Additionally, remove the useless 
parameter of assigned_dev_update_irq()

Signed-off-by: Weidong Han <[EMAIL PROTECTED]>
---
 qemu/hw/device-assignment.c |  160 ++++++++++++++++++++++++++++++++++++-------
 qemu/hw/device-assignment.h |    2 +
 qemu/hw/device-hotplug.c    |   17 +++++
 qemu/hw/pci.c               |    4 +-
 4 files changed, 156 insertions(+), 27 deletions(-)

diff --git a/qemu/hw/device-assignment.c b/qemu/hw/device-assignment.c
index 9aa7708..05db326 100644
--- a/qemu/hw/device-assignment.c
+++ b/qemu/hw/device-assignment.c
@@ -447,7 +447,7 @@ static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t 
devfn)
 /* The pci config space got updated. Check if irq numbers have changed
  * for our devices
  */
-void assigned_dev_update_irq(PCIDevice *d)
+void assigned_dev_update_irq(void)
 {
     int irq, r;
     AssignedDevice *assigned_dev;
@@ -459,7 +459,7 @@ void assigned_dev_update_irq(PCIDevice *d)
         irq = piix_get_irq(irq);
 
 #ifdef TARGET_IA64
-       irq = ipf_map_irq(d, irq);
+       irq = ipf_map_irq(&assigned_dev->dev, irq);
 #endif
 
         if (irq != assigned_dev->girq) {
@@ -485,12 +485,68 @@ void assigned_dev_update_irq(PCIDevice *d)
     }
 }
 
+static int assign_device(AssignedDevice *dev, int disable_iommu)
+{
+    int r;
+    struct kvm_assigned_pci_dev assigned_dev_data;
+
+    memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+    assigned_dev_data.assigned_dev_id  =
+       calc_assigned_dev_id(dev->h_busnr, dev->h_devfn);
+    assigned_dev_data.busnr = dev->h_busnr;
+    assigned_dev_data.devfn = dev->h_devfn;
+
+#ifdef KVM_CAP_IOMMU
+    /* We always enable the IOMMU if present
+     * (or when not disabled on the command line)
+     */
+    r = kvm_check_extension(kvm_context, KVM_CAP_IOMMU);
+    if (r && !disable_iommu)
+       assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU;
+#endif
+      
+    r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
+    if (r < 0)
+       fprintf(stderr, "Could not notify kernel about "
+                "assigned device (%x:%x.%x)\n",
+                dev->h_busnr,
+                PCI_SLOT(dev->h_devfn),
+                PCI_FUNC(dev->h_devfn));
+
+    return r;
+}
+
+static int assign_irq(AssignedDevice *dev)
+{
+    int irq, r = 0;
+    struct kvm_assigned_irq assigned_irq_data;
+
+    irq = pci_map_irq(&dev->dev, dev->intpin);
+    irq = piix_get_irq(irq);
+
+#ifdef TARGET_IA64
+    irq = ipf_map_irq(&dev->dev, irq);
+#endif
+
+    memset(&assigned_irq_data, 0, sizeof(assigned_irq_data));
+    assigned_irq_data.assigned_dev_id  =
+        calc_assigned_dev_id(dev->h_busnr, dev->h_devfn);
+    assigned_irq_data.guest_irq = irq;
+    assigned_irq_data.host_irq = dev->real_device.irq;
+    r = kvm_assign_irq(kvm_context, &assigned_irq_data);
+    if (r < 0)
+        fprintf(stderr, "Are you assigning a device "
+                "that shares IRQ with some other device?\n");
+
+    dev->girq = irq;
+    return r;
+}
+
 struct PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus)
 {
     int r;
     AssignedDevice *dev;
     uint8_t e_device, e_intx;
-    struct kvm_assigned_pci_dev assigned_dev_data;
 
     DEBUG("Registering real physical device %s (bus=%x dev=%x func=%x)\n",
           adev->name, adev->bus, adev->dev, adev->func);
@@ -526,32 +582,22 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo 
*adev, PCIBus *bus)
     dev->h_busnr = adev->bus;
     dev->h_devfn = PCI_DEVFN(adev->dev, adev->func);
 
-    memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
-    assigned_dev_data.assigned_dev_id  =
-       calc_assigned_dev_id(dev->h_busnr, (uint32_t)dev->h_devfn);
-    assigned_dev_data.busnr = dev->h_busnr;
-    assigned_dev_data.devfn = dev->h_devfn;
-
-#ifdef KVM_CAP_IOMMU
-    /* We always enable the IOMMU if present
-     * (or when not disabled on the command line)
-     */
-    r = kvm_check_extension(kvm_context, KVM_CAP_IOMMU);
-    if (r && !adev->disable_iommu)
-       assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU;
-#endif
-      
-    r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
-    if (r < 0) {
-       fprintf(stderr, "Could not notify kernel about "
-                "assigned device \"%s\"\n", adev->name);
-       perror("register_real_device");
+    /* assign device to guest */
+    r = assign_device(dev, adev->disable_iommu);
+    if (r < 0)
        goto out;
-    }
 
+    /* assign IRQ to device */
+    r = assign_irq(dev);
+    if (r < 0)
+       goto out;
+    
     adev->assigned_dev = dev;
-  out:
     return &dev->dev;
+
+out:
+    /*FIXME: release resources */
+    return NULL;
 }
 
 int init_all_assigned_devices(PCIBus *bus)
@@ -619,3 +665,67 @@ bad:
     qemu_free(adev);
     return NULL;
 }
+
+AssignedDevInfo *get_assigned_device(int pcibus, int slot)
+{
+    AssignedDevice *assigned_dev = NULL;
+    AssignedDevInfo *adev = NULL;
+
+    LIST_FOREACH(adev, &adev_head, next) {
+        assigned_dev = adev->assigned_dev;
+        if (pci_bus_num(assigned_dev->dev.bus) == pcibus &&
+            PCI_SLOT(assigned_dev->dev.devfn) == slot)
+            return adev;
+    }
+
+    return NULL;
+}
+
+static void remove_ioperm_data(AssignedDevice *dev)
+{
+    int i;
+    AssignedDevRegion *region;
+
+    for (i = 0; i < MAX_IO_REGIONS; i++) {
+        region = &dev->v_addrs[i];
+        if (region->r_size == 0)
+            continue;
+        kvm_remove_ioperm_data(region->u.r_baseport, region->r_size);
+    }
+}
+
+static void deassign_device(AssignedDevice *dev, int disable_iommu)
+{
+    struct kvm_assigned_pci_dev assigned_dev_data;
+    int r;
+
+    memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+    assigned_dev_data.assigned_dev_id  =
+       calc_assigned_dev_id(dev->h_busnr, dev->h_devfn);
+    assigned_dev_data.busnr = dev->h_busnr;
+    assigned_dev_data.devfn = dev->h_devfn;
+
+#ifdef KVM_CAP_IOMMU
+    /* We always enable the IOMMU if present
+     * (or when not disabled on the command line)
+     */
+    r = kvm_check_extension(kvm_context, KVM_CAP_IOMMU);
+    if (r && !disable_iommu)
+       assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU;
+#endif
+
+    if (kvm_deassign_pci_device(kvm_context, &assigned_dev_data))
+       fprintf(stderr, "Could not notify kernel about "
+                "deassigned device (%x:%x.%x)\n",
+                dev->h_busnr, PCI_SLOT(dev->h_devfn), PCI_FUNC(dev->h_devfn));
+}
+
+void remove_assigned_device(AssignedDevInfo *adev)
+{
+    AssignedDevice *assigned_dev = adev->assigned_dev;
+
+    remove_ioperm_data(assigned_dev);
+    deassign_device(assigned_dev, adev->disable_iommu);
+    LIST_REMOVE(adev, next);
+    qemu_free(adev);
+}
diff --git a/qemu/hw/device-assignment.h b/qemu/hw/device-assignment.h
index d6caa67..6dbfdf5 100644
--- a/qemu/hw/device-assignment.h
+++ b/qemu/hw/device-assignment.h
@@ -96,6 +96,8 @@ struct AssignedDevInfo {
 
 PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus);
 AssignedDevInfo *add_assigned_device(const char *arg);
+AssignedDevInfo *get_assigned_device(int pcibus, int slot);
+void remove_assigned_device(AssignedDevInfo *adev);
 int init_all_assigned_devices(PCIBus *bus);
 
 #define MAX_DEV_ASSIGN_CMDLINE 8
diff --git a/qemu/hw/device-hotplug.c b/qemu/hw/device-hotplug.c
index ba1b161..f7cf517 100644
--- a/qemu/hw/device-hotplug.c
+++ b/qemu/hw/device-hotplug.c
@@ -58,6 +58,14 @@ static PCIDevice *qemu_system_hot_assign_device(const char 
*opts, int bus_nr)
     return ret;
 }
 
+static void qemu_system_hot_deassign_device(AssignedDevInfo *adev)
+{
+    remove_assigned_device(adev);
+
+    term_printf("Unregistered host PCI device %02x:%02x.%1x "
+               "(\"%s\") from guest\n", 
+               adev->bus, adev->dev, adev->func, adev->name);
+}
 #endif /* USE_KVM_DEVICE_ASSIGNMENT */
 
 static int add_init_drive(const char *opts)
@@ -236,6 +244,7 @@ void device_hot_remove_success(int pcibus, int slot)
 {
     PCIDevice *d = pci_find_device(pcibus, slot);
     int class_code;
+    AssignedDevInfo *adev;
 
     if (!d) {
         term_printf("invalid slot %d\n", slot);
@@ -246,6 +255,14 @@ void device_hot_remove_success(int pcibus, int slot)
 
     pci_unregister_device(d);
 
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+    adev = get_assigned_device(pcibus, slot);
+    if (adev) {
+        qemu_system_hot_deassign_device(adev);
+        return;
+    }
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+
     switch(class_code) {
     case PCI_BASE_CLASS_STORAGE:
         destroy_bdrvs(slot);
diff --git a/qemu/hw/pci.c b/qemu/hw/pci.c
index 75bc9a9..c200ba6 100644
--- a/qemu/hw/pci.c
+++ b/qemu/hw/pci.c
@@ -50,7 +50,7 @@ struct PCIBus {
 
 static void pci_update_mappings(PCIDevice *d);
 static void pci_set_irq(void *opaque, int irq_num, int level);
-void assigned_dev_update_irq(PCIDevice *d);
+void assigned_dev_update_irq(void);
 
 target_phys_addr_t pci_mem_base;
 static int pci_irq_index;
@@ -458,7 +458,7 @@ void pci_default_write_config(PCIDevice *d,
     if (kvm_enabled() && qemu_kvm_irqchip_in_kernel() &&
         address >= PIIX_CONFIG_IRQ_ROUTE &&
        address < PIIX_CONFIG_IRQ_ROUTE + 4)
-        assigned_dev_update_irq(d);
+        assigned_dev_update_irq();
 #endif /* USE_KVM_DEVICE_ASSIGNMENT */
 
     end = address + len;
-- 
1.5.1

Attachment: 0004-support-hot-remove-assigned-device.patch
Description: 0004-support-hot-remove-assigned-device.patch

Reply via email to