>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
0004-support-hot-remove-assigned-device.patch
Description: 0004-support-hot-remove-assigned-device.patch