>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
