[libvirt] [PATCH]execute netdev_del after receive DEVICE_DELETED event

x00221466 xiexiangyou at huawei.com
Thu Mar 27 12:51:24 UTC 2014


Hi,

When live detaching the virtual net device, such as virtio nic、
RTL8139、E1000, there are some problems:

(1)If the Guest OS don't support the hot plugging pci device, detach
the virtual network device by Libvirt, the "net device" in Qemu will
still exist, but "hostnet"(tap) in Qemu will be removed. so the net device
in Guest OS will be of no effect.

(2)If reject the nic in Guest OS, Qemu will remove the "net device",
then Qemu send DEVICE_DELETED to Libvirt, Libvirt receive the event
in event-loop thread and release info of the net device in
qemuDomainRemoveNetDevice func. but "hostnet" in Qemu still exist.
So next live attaching virtual net device will be failed because of
"Duplicate ID".

#virsh attach-device win2008_st_r2_64 net.xml --live
error: Failed to attach device from net.xml
error: internal error: unable to execute QEMU command 'netdev_add':
Duplicate ID 'hostnet0' for netdev

(3)In addition, in qemuDomainDetachNetDevice, detach net device func,
"netdev_del" command will be sent after sending "device_del" command
at once. So it is violent to remove the tap device before the net device
is completely removed.

So I think it's more logical that doing the work of sending Qemu command
"netdev_del" after receive the DEVICE_DELETED event. It can avoid the conflict
of device info between Libvirt side and Qemu side.

I create a thread in qemuDomainRemoveDevice,the handle of DEVICE_DELETED event,
to execute QEMU command "netdev_del".

Regards,
-xie


Signed-off-by: xiexiangyou <xiexiangyou at huawei.com>
---
 src/qemu/qemu_hotplug.c |  154 +++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 137 insertions(+), 17 deletions(-)

diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index bf4f160..81f6a56 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -2699,6 +2699,124 @@ qemuDomainRemoveChrDevice(virQEMUDriverPtr driver,
     virDomainChrDefFree(chr);
 }

+typedef struct privVmDevDriver {
+    virQEMUDriverPtr driver;
+    virDomainObjPtr vm;
+    virDomainDeviceDefPtr dev;
+}privVmDevDriver, *privVmDevDriverPtr;
+
+static void
+qemuProcessRemoveNetDevice(void *opaque)
+{
+    privVmDevDriverPtr privData = opaque;
+    virQEMUDriverPtr driver = privData->driver;
+    virDomainObjPtr vm = privData->vm;
+    virDomainDeviceDefPtr dev = privData->dev;
+
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    char mac[VIR_MAC_STRING_BUFLEN];
+    int detachidx;
+    virDomainNetDefPtr detach = NULL;
+    char *hostnet_name = NULL;
+    int vlan;
+
+    virObjectLock(vm);
+
+    if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+        goto cleanup;
+
+    detachidx = virDomainNetFindIdx(vm->def, dev->data.net);
+    if (detachidx == -2) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                        _("multiple devices matching mac address %s found"),
+                         virMacAddrFormat(&dev->data.net->mac, mac));
+        goto endjob;
+    }
+    else if (detachidx < 0) {
+           virReportError(VIR_ERR_OPERATION_FAILED,
+                           _("network device %s not found"),
+                           virMacAddrFormat(&dev->data.net->mac, mac));
+            goto endjob;
+    }
+    detach = vm->def->nets[detachidx];
+    if (virAsprintf(&hostnet_name, "host%s", detach->info.alias) < 0) {
+        virReportOOMError();
+        goto endjob;
+    }
+    if ((vlan = qemuDomainNetVLAN(detach)) < 0) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       "%s", _("unable to determine original VLAN"));
+        goto endjob;
+    }
+
+    qemuDomainObjEnterMonitor(driver, vm);
+    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV) &&
+        virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+        if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
+            qemuDomainObjExitMonitor(driver, vm);
+            virDomainAuditNet(vm, detach, NULL, "detach", false);
+            goto endjob;
+        }
+    } else {
+        if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
+            qemuDomainObjExitMonitor(driver, vm);
+            virDomainAuditNet(vm, detach, NULL, "detach", false);
+            goto endjob;
+        }
+    }
+    qemuDomainObjExitMonitor(driver, vm);
+
+    qemuDomainRemoveNetDevice(driver, vm, dev->data.net);
+
+endjob:
+    if (!qemuDomainObjEndJob(driver, vm))
+        vm = NULL;
+
+cleanup:
+    VIR_FREE(hostnet_name);
+    VIR_FREE(privData->dev);
+    VIR_FREE(privData);
+
+    if (vm && virObjectUnref(vm))
+        virObjectUnlock(vm);
+}
+
+static void
+qemuRemoveNetDeviceJob(virQEMUDriverPtr driver,
+                       virDomainObjPtr vm,
+                       virDomainDeviceDefPtr dev)
+{
+    virThread th;
+    privVmDevDriverPtr data = NULL;
+    virDomainDeviceDefPtr device = NULL;
+
+    if (VIR_ALLOC(data) < 0) {
+        VIR_ERROR(_("Failed to alloc VmDevDriver"));
+        return;
+    }
+
+    if (VIR_ALLOC(device) < 0) {
+        VIR_ERROR(_("Failed to alloc virDomainDeviceDefPtr"));
+        VIR_FREE(data);
+        return;
+    }
+    memcpy(device, dev, sizeof(*device));
+
+    data->dev = device;
+    data->driver = driver;
+    data->vm = vm;
+    virObjectRef(vm);
+    if (virThreadCreate(&th,
+                        false,
+                        qemuProcessRemoveNetDevice,
+                        data) < 0) {
+        VIR_ERROR(_("Failed to create remove net device thread"));
+        VIR_FREE(device);
+        VIR_FREE(data);
+        virObjectUnref(vm);
+    }
+
+}

 void
 qemuDomainRemoveDevice(virQEMUDriverPtr driver,
@@ -2713,7 +2831,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
         qemuDomainRemoveControllerDevice(driver, vm, dev->data.controller);
         break;
     case VIR_DOMAIN_DEVICE_NET:
-        qemuDomainRemoveNetDevice(driver, vm, dev->data.net);
+        qemuRemoveNetDeviceJob(driver, vm, dev);
         break;
     case VIR_DOMAIN_DEVICE_HOSTDEV:
         qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev);
@@ -3419,26 +3537,28 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver,
             goto cleanup;
         }
     }
+    qemuDomainObjExitMonitor(driver, vm);

-    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV) &&
-        virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
-        if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
-            qemuDomainObjExitMonitor(driver, vm);
-            virDomainAuditNet(vm, detach, NULL, "detach", false);
-            goto cleanup;
-        }
-    } else {
-        if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
-            qemuDomainObjExitMonitor(driver, vm);
-            virDomainAuditNet(vm, detach, NULL, "detach", false);
-            goto cleanup;
+    if (!qemuDomainWaitForDeviceRemoval(vm)) {
+        qemuDomainObjEnterMonitor(driver, vm);
+        if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV) &&
+            virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+            if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
+                qemuDomainObjExitMonitor(driver, vm);
+                virDomainAuditNet(vm, detach, NULL, "detach", false);
+                goto cleanup;
+            }
+        } else {
+            if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
+                qemuDomainObjExitMonitor(driver, vm);
+                virDomainAuditNet(vm, detach, NULL, "detach", false);
+                goto cleanup;
+            }
         }
-    }
-    qemuDomainObjExitMonitor(driver, vm);
+        qemuDomainObjExitMonitor(driver, vm);

-    if (!qemuDomainWaitForDeviceRemoval(vm))
         qemuDomainRemoveNetDevice(driver, vm, detach);
-
+    }
     ret = 0;

 cleanup:
-- 
1.6.0.2





More information about the libvir-list mailing list