[libvirt] [RFC PATCH 2/2] Implement virtio serial address allocation

Ján Tomko jtomko at redhat.com
Wed Jun 25 17:22:09 UTC 2014


Over-engineered to allow non-contiguous indexes.
Free ports of a controller are stored in a virBitmap.
These bitmaps are stored in a hash table, indexed by
the controller index formatted as a string.

Buses are ignored for now, QEMU doesn't seem
to support anything else than .0

For virtconsoles, addresses are reserved silently,
since we don't put those in the XML, but they occupy
virtio-serial ports.

Missing:
  auto-adding controllers when all ports are occupied

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=890606
Paritally fixes https://bugzilla.redhat.com/show_bug.cgi?id=1076708
---
RFC, since it hashes the string of an integer.

 src/conf/domain_addr.c                             | 426 +++++++++++++++++++++
 src/conf/domain_addr.h                             |  49 +++
 src/conf/domain_conf.c                             |  30 --
 src/libvirt_private.syms                           |   9 +
 src/qemu/qemu_command.c                            |  61 +++
 src/qemu/qemu_domain.c                             |   1 +
 src/qemu/qemu_domain.h                             |   1 +
 src/qemu/qemu_hotplug.c                            |  31 +-
 tests/qemuhotplugtest.c                            |   2 +-
 .../qemuxml2argv-channel-virtio-auto.args          |   8 +-
 .../qemuxml2argv-channel-virtio-autoassign.args    |  10 +-
 .../qemuxml2xmlout-channel-virtio-auto.xml         |  10 +-
 12 files changed, 591 insertions(+), 47 deletions(-)

diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index fb4a76f..c1c442a 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -718,3 +718,429 @@ virDomainCCWAddressSetCreate(void)
     virDomainCCWAddressSetFree(addrs);
     return NULL;
 }
+
+
+static void
+virDomainVirtioSerialAddrHashValueFree(void *value,
+                                       const void *name ATTRIBUTE_UNUSED)
+{
+    virBitmapPtr map = value;
+
+    virBitmapFree(map);
+}
+
+/* virDomainVirtioSerialAddrSetCreate
+ *
+ * Allocates an address set for virtio serial addresses
+ */
+virDomainVirtioSerialAddrSetPtr
+virDomainVirtioSerialAddrSetCreate(void)
+{
+    virDomainVirtioSerialAddrSetPtr ret = NULL;
+
+    if (VIR_ALLOC(ret) < 0)
+        goto error;
+
+    if (!(ret->used = virHashCreate(9, virDomainVirtioSerialAddrHashValueFree)))
+        goto error;
+
+    return ret;
+
+ error:
+    virDomainVirtioSerialAddrSetFree(ret);
+    return NULL;
+}
+
+/* virDomainVirtioSerialAddrSetAddController
+ *
+ * Adds virtio serial ports of the existing controllers
+ * to the address set.
+ */
+int
+virDomainVirtioSerialAddrSetAddController(virDomainVirtioSerialAddrSetPtr addrs,
+                                          virDomainControllerDefPtr cont)
+{
+    virBitmapPtr map = NULL;
+    char *str = NULL;
+    int ret = -1;
+    int ports;
+
+    if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL)
+        return 0;
+
+    ports = cont->opts.vioserial.ports;
+    if (ports == -1)
+        ports = 31;
+
+    VIR_DEBUG("Adding virtio serial controller index %u with %d"
+              " ports to the address set", cont->idx, ports);
+
+    if (!(map = virBitmapNew(ports)))
+        goto cleanup;
+
+    /* port 0 is reserved for virtconsoles only */
+    if (virBitmapSetBit(map, 0) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed to reserve virtio serial port 0"));
+        goto cleanup;
+    }
+
+    if (virAsprintf(&str, "%u", cont->idx) < 0)
+        goto cleanup;
+
+    if (virHashLookup(addrs->used, str)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("virtio serial controller with index %u "
+                         " is already in the address set"), cont->idx);
+        goto cleanup;
+    }
+    if (virHashAddEntry(addrs->used, str, map) < 0)
+        goto cleanup;
+    map = NULL;
+
+    if (!addrs->nextInit) {
+        addrs->next.controller = cont->idx;
+        addrs->nextInit = true;
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(str);
+    virBitmapFree(map);
+    return ret;
+}
+
+/* virDomainVirtioSerialAddrSetAddControllers
+ *
+ * Adds virtio serial ports of the existing controllers
+ * to the address set.
+ */
+int
+virDomainVirtioSerialAddrSetAddControllers(virDomainVirtioSerialAddrSetPtr addrs,
+                                           virDomainDefPtr def)
+{
+    size_t i;
+
+    for (i = 0; i < def->ncontrollers; i++) {
+        if (virDomainVirtioSerialAddrSetAddController(addrs,
+                                                      def->controllers[i]) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+/* virDomainVirtioSerialAddrSetRemoveController
+ *
+ * Removes a virtio serial controller from the address set.
+ * Useful for cleaning up after a failed hotunplug.
+ */
+int
+virDomainVirtioSerialAddrSetRemoveController(virDomainVirtioSerialAddrSetPtr addrs,
+                                             virDomainControllerDefPtr cont)
+{
+    char *str = NULL;
+    int ret = -1;
+
+    if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL)
+        return 0;
+
+    VIR_DEBUG("Removing virtio serial controller index %u"
+              " from the address set", cont->idx);
+
+    if (virAsprintf(&str, "%u", cont->idx) < 0)
+        goto cleanup;
+
+    if (virHashRemoveEntry(addrs->used, str) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("virtio serial controller with index %u "
+                         " is not in the address set"), cont->idx);
+        goto cleanup;
+    }
+
+    /* XXX: Handle addrs->next if we ever support hotunplugging
+     * virtio-serial controllers */
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(str);
+    return ret;
+}
+
+void
+virDomainVirtioSerialAddrSetFree(virDomainVirtioSerialAddrSetPtr addrs)
+{
+    if (addrs) {
+        virHashFree(addrs->used);
+        VIR_FREE(addrs);
+    }
+}
+
+/*
+ * Eww, this function compares two unsigned integers stored as a string
+ */
+static int
+virDomainVirtioSerialAddrCompare(const virHashKeyValuePair *a,
+                                 const virHashKeyValuePair *b)
+{
+    const char *key_a = a->key;
+    const char *key_b = b->key;
+
+    size_t len_a = strlen(key_a);
+    size_t len_b = strlen(key_b);
+
+    /* with no padding/negative numbers allowed, the longer string
+     * contains a larger number */
+    if (len_a < len_b)
+        return -1;
+    else if (len_a > len_b)
+        return 1;
+    else
+        return strncmp(key_a, key_b, len_a);
+}
+
+static int
+virDomainVirtioSerialAddrNext(virDomainVirtioSerialAddrSetPtr addrs,
+                              virDomainDeviceVirtioSerialAddress *addr,
+                              bool allowZero)
+{
+    virBitmapPtr cur = NULL;
+    char *str = NULL;
+    int ret = -1;
+    virHashKeyValuePairPtr arr = NULL;
+    size_t i, ncontrollers;
+    size_t curidx;
+    ssize_t port, start = 0;
+    unsigned int controller;
+
+    /* port number 0 is reserved for virtconsoles */
+    if (allowZero)
+        start = -1;
+
+    /* What controller was the last assigned address on? */
+    if (virAsprintf(&str, "%u", addrs->next.controller) < 0)
+        goto cleanup;
+
+    if (!(cur = virHashLookup(addrs->used, str))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("The last used virtio serial controller is missing "
+                         "from the address set hash table"));
+        goto cleanup;
+    }
+
+    /* Look for a free port on the current controller */
+    if ((port = virBitmapNextClearBit(cur, start + addrs->next.port)) >= 0) {
+        controller = addrs->next.controller;
+        goto success;
+    }
+
+    ncontrollers = virHashSize(addrs->used);
+    arr = virHashGetItems(addrs->used, virDomainVirtioSerialAddrCompare);
+    if (!arr) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get the hash table as an array"));
+        goto cleanup;
+    }
+
+    /* Find its position in the hash "array" */
+    for (i = 0; i < ncontrollers; i++) {
+        if (arr[i].value == cur) {
+            curidx = i;
+            break;
+        }
+    }
+    if (i == ncontrollers) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("The last used virtio serial controller is missing from the set"));
+        goto cleanup;
+    }
+
+    /* Search for a free port after the current controller */
+    for (i = curidx; i < ncontrollers; i++) {
+        cur = (virBitmapPtr) arr[i].value;
+        if ((port = virBitmapNextClearBit(cur, start)) >= 0) {
+            if (virStrToLong_ui(arr[i].key, NULL, 10, &controller) < 0)
+                goto cleanup;
+            goto success;
+        }
+    }
+
+    for (i = 0; i < curidx; i++) {
+        cur = (virBitmapPtr) arr[i].value;
+        if ((port = virBitmapNextClearBit(cur, start)) >= 0) {
+            if (virStrToLong_ui(arr[i].key, NULL, 10, &controller) < 0)
+                goto cleanup;
+            goto success;
+        }
+    }
+
+    virReportError(VIR_ERR_XML_ERROR, "%s",
+                   _("Unable to find a free virtio-serial port"));
+
+ cleanup:
+    VIR_FREE(arr);
+    return ret;
+
+ success:
+    addr->bus = 0;
+    addr->port = port;
+    addr->controller = controller;
+    VIR_DEBUG("Found free virtio serial controller %u port %u", addr->controller,
+              addr->port);
+    ret = 0;
+    goto cleanup;
+}
+
+/* virDomainVirtioSerialAddrAutoAssign
+ *
+ * reserve a virtio serial address of the device (if it has one)
+ * or assign a virtio serial address to the device
+ */
+int
+virDomainVirtioSerialAddrAutoAssign(virDomainVirtioSerialAddrSetPtr addrs,
+                                    virDomainDeviceInfoPtr info,
+                                    bool allowZero)
+{
+    if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
+        info->addr.vioserial.port)
+        return virDomainVirtioSerialAddrReserve(NULL, NULL, info, addrs);
+    else
+        return virDomainVirtioSerialAddrAssign(addrs, info, allowZero);
+}
+
+
+int
+virDomainVirtioSerialAddrAssign(virDomainVirtioSerialAddrSetPtr addrs,
+                                virDomainDeviceInfoPtr info,
+                                bool allowZero)
+{
+    char *str = NULL;
+    int ret = -1;
+    virDomainDeviceInfo nfo = { NULL };
+    virDomainDeviceInfoPtr ptr = allowZero ? &nfo : info;
+
+    ptr->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL;
+
+    if (virDomainVirtioSerialAddrNext(addrs, &ptr->addr.vioserial,
+                                      allowZero) < 0)
+        goto cleanup;
+
+    if (ptr->addr.vioserial.port)
+        addrs->next = info->addr.vioserial;
+
+    if (virDomainVirtioSerialAddrReserve(NULL, NULL, ptr, addrs) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(str);
+    return ret;
+}
+
+/* virDomainVirtioSerialAddrReserve
+ *
+ * Reserve the virtio serial address of the device
+ *
+ * For use with virDomainDeviceInfoIterate,
+ * opaque should be the address set
+ */
+int
+virDomainVirtioSerialAddrReserve(virDomainDefPtr def ATTRIBUTE_UNUSED,
+                                 virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
+                                 virDomainDeviceInfoPtr info,
+                                 void *data)
+{
+    virDomainVirtioSerialAddrSetPtr addrs = data;
+    virBitmapPtr map;
+    char *str = NULL;
+    int ret = -1;
+    bool b;
+
+    if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL ||
+        info->addr.vioserial.port == 0)
+        return 0;
+
+    VIR_DEBUG("Reserving virtio serial %u %u", info->addr.vioserial.controller,
+              info->addr.vioserial.port);
+
+    if (virAsprintf(&str, "%u", info->addr.vioserial.controller) < 0)
+        goto cleanup;
+
+    if ((map = virHashLookup(addrs->used, str)) == NULL) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("virtio serial controller %u is missing"),
+                       info->addr.vioserial.controller);
+        goto cleanup;
+    }
+
+    if (virBitmapGetBit(map, info->addr.vioserial.port, &b) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("virtio serial controller %u port %u out of bounds"),
+                       info->addr.vioserial.controller,
+                       info->addr.vioserial.port);
+        goto cleanup;
+    }
+
+    if (b) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("virtio serial controller %u port %u already occupied"),
+                       info->addr.vioserial.controller,
+                       info->addr.vioserial.port);
+        goto cleanup;
+    }
+
+    ignore_value(virBitmapSetBit(map, info->addr.vioserial.port));
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(str);
+    return ret;
+}
+
+/* virDomainVirtioSerialAddrRelease
+ *
+ * Release the virtio serial address of the device
+ */
+int
+virDomainVirtioSerialAddrRelease(virDomainVirtioSerialAddrSetPtr addrs,
+                                 virDomainDeviceInfoPtr info)
+{
+    virBitmapPtr map;
+    char *str = NULL;
+    int ret = -1;
+
+    if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL ||
+        info->addr.vioserial.port == 0)
+        return 0;
+
+    VIR_DEBUG("Releasing virtio serial %u %u", info->addr.vioserial.controller,
+              info->addr.vioserial.port);
+
+    if (virAsprintf(&str, "%u", info->addr.vioserial.controller) < 0)
+        goto cleanup;
+
+    if ((map = virHashLookup(addrs->used, str)) == NULL) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("virtio serial controller %u is missing"),
+                       info->addr.vioserial.controller);
+        goto cleanup;
+    }
+
+    if (virBitmapClearBit(map, info->addr.vioserial.port) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("virtio serial controller %u port %u out of bounds"),
+                       info->addr.vioserial.controller,
+                       info->addr.vioserial.port);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(str);
+    return ret;
+}
diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h
index 2c3468e..81b1ba2 100644
--- a/src/conf/domain_addr.h
+++ b/src/conf/domain_addr.h
@@ -173,4 +173,53 @@ int virDomainCCWAddressReleaseAddr(virDomainCCWAddressSetPtr addrs,
                                    virDomainDeviceInfoPtr dev)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
 virDomainCCWAddressSetPtr virDomainCCWAddressSetCreate(void);
+
+struct _virDomainVirtioSerialAddrSet {
+    virHashTablePtr used;
+    virDomainDeviceVirtioSerialAddress next;
+    bool nextInit;
+};
+typedef struct _virDomainVirtioSerialAddrSet virDomainVirtioSerialAddrSet;
+typedef virDomainVirtioSerialAddrSet *virDomainVirtioSerialAddrSetPtr;
+
+virDomainVirtioSerialAddrSetPtr
+virDomainVirtioSerialAddrSetCreate(void);
+int
+virDomainVirtioSerialAddrSetAddController(virDomainVirtioSerialAddrSetPtr addrs,
+                                          virDomainControllerDefPtr cont)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int
+virDomainVirtioSerialAddrSetAddControllers(virDomainVirtioSerialAddrSetPtr addrs,
+                                           virDomainDefPtr def)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int
+virDomainVirtioSerialAddrSetRemoveController(virDomainVirtioSerialAddrSetPtr addrs,
+                                             virDomainControllerDefPtr cont)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+void
+virDomainVirtioSerialAddrSetFree(virDomainVirtioSerialAddrSetPtr addrs);
+int
+virDomainVirtioSerialAddrAutoAssign(virDomainVirtioSerialAddrSetPtr addrs,
+                                    virDomainDeviceInfoPtr info,
+                                    bool allowZero)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int
+virDomainVirtioSerialAddrAssign(virDomainVirtioSerialAddrSetPtr addrs,
+                                virDomainDeviceInfoPtr info,
+                                bool allowZero)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int
+virDomainVirtioSerialAddrReserve(virDomainDefPtr def,
+                                 virDomainDeviceDefPtr dev,
+                                 virDomainDeviceInfoPtr info,
+                                 void *data)
+    ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+
+int
+virDomainVirtioSerialAddrRelease(virDomainVirtioSerialAddrSetPtr addrs,
+                                 virDomainDeviceInfoPtr info)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
 #endif /* __DOMAIN_ADDR_H__ */
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 02c394f..bdca496 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -3027,21 +3027,6 @@ virDomainDeviceDefPostParseInternal(virDomainDeviceDefPtr dev,
 
             chr->target.port = maxport + 1;
         }
-
-        if (chr->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
-            chr->info.addr.vioserial.port == 0) {
-            int maxport = 0;
-
-            for (i = 0; i < cnt; i++) {
-                const virDomainChrDef *thischr = arrPtr[i];
-                if (thischr->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
-                    thischr->info.addr.vioserial.controller == chr->info.addr.vioserial.controller &&
-                    thischr->info.addr.vioserial.bus == chr->info.addr.vioserial.bus &&
-                    (int)thischr->info.addr.vioserial.port > maxport)
-                    maxport = thischr->info.addr.vioserial.port;
-            }
-            chr->info.addr.vioserial.port = maxport + 1;
-        }
     }
 
     return 0;
@@ -12495,21 +12480,6 @@ virDomainDefParseXML(xmlDocPtr xml,
             chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO &&
             chr->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
             chr->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL;
-
-        if (chr->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
-            chr->info.addr.vioserial.port == 0) {
-            int maxport = 0;
-            size_t j;
-            for (j = 0; j < i; j++) {
-                virDomainChrDefPtr thischr = def->channels[j];
-                if (thischr->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
-                    thischr->info.addr.vioserial.controller == chr->info.addr.vioserial.controller &&
-                    thischr->info.addr.vioserial.bus == chr->info.addr.vioserial.bus &&
-                    (int)thischr->info.addr.vioserial.port > maxport)
-                    maxport = thischr->info.addr.vioserial.port;
-            }
-            chr->info.addr.vioserial.port = maxport + 1;
-        }
     }
     VIR_FREE(nodes);
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index a793b4c..61e452d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -110,6 +110,15 @@ virDomainPCIAddressSetFree;
 virDomainPCIAddressSetGrow;
 virDomainPCIAddressSlotInUse;
 virDomainPCIAddressValidate;
+virDomainVirtioSerialAddrAssign;
+virDomainVirtioSerialAddrAutoAssign;
+virDomainVirtioSerialAddrRelease;
+virDomainVirtioSerialAddrReserve;
+virDomainVirtioSerialAddrSetAddController;
+virDomainVirtioSerialAddrSetAddControllers;
+virDomainVirtioSerialAddrSetCreate;
+virDomainVirtioSerialAddrSetFree;
+virDomainVirtioSerialAddrSetRemoveController;
 
 
 # conf/domain_audit.h
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 93d303e..fd1aa7c 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1229,6 +1229,63 @@ qemuAssignSpaprVIOAddress(virDomainDefPtr def, virDomainDeviceInfoPtr info,
     return 0;
 }
 
+
+static int
+qemuDomainAssignVirtioSerialAddresses(virDomainDefPtr def,
+                                      virDomainObjPtr obj)
+{
+    int ret = -1;
+    size_t i;
+    virDomainVirtioSerialAddrSetPtr addrs = NULL;
+    qemuDomainObjPrivatePtr priv = NULL;
+
+    if (!(addrs = virDomainVirtioSerialAddrSetCreate()))
+        goto cleanup;
+
+    if (virDomainVirtioSerialAddrSetAddControllers(addrs, def) < 0)
+        goto cleanup;
+
+    if (virDomainDeviceInfoIterate(def, virDomainVirtioSerialAddrReserve,
+                                   addrs) < 0)
+        goto cleanup;
+
+    for (i = 0; i < def->nconsoles; i++) {
+        virDomainChrDefPtr chr = def->consoles[i];
+        if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
+            chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO) {
+            if (virDomainVirtioSerialAddrAssign(addrs, &chr->info, true) < 0)
+                goto cleanup;
+        }
+    }
+
+    for (i = 0; i < def->nchannels; i++) {
+        virDomainChrDefPtr chr = def->channels[i];
+        if (chr->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
+            chr->info.addr.vioserial.port == 0 &&
+            virDomainVirtioSerialAddrAssign(addrs, &chr->info, false) < 0)
+            goto cleanup;
+    }
+
+    if (obj && obj->privateData) {
+        priv = obj->privateData;
+        if (addrs) {
+            /* if this is the live domain object, we persist the CCW addresses*/
+            virDomainVirtioSerialAddrSetFree(priv->vioserialaddrs);
+            priv->persistentAddrs = 1;
+            priv->vioserialaddrs = addrs;
+            addrs = NULL;
+        } else {
+            priv->persistentAddrs = 0;
+        }
+    }
+    ret = 0;
+
+ cleanup:
+    virDomainVirtioSerialAddrSetFree(addrs);
+    return ret;
+}
+
+
 int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def,
                                       virQEMUCapsPtr qemuCaps)
 {
@@ -1553,6 +1610,10 @@ int qemuDomainAssignAddresses(virDomainDefPtr def,
 {
     int rc;
 
+    rc = qemuDomainAssignVirtioSerialAddresses(def, obj);
+    if (rc)
+        return rc;
+
     rc = qemuDomainAssignSpaprVIOAddresses(def, qemuCaps);
     if (rc)
         return rc;
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 2e55c99..03f2915 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -250,6 +250,7 @@ qemuDomainObjPrivateFree(void *data)
     virCgroupFree(&priv->cgroup);
     virDomainPCIAddressSetFree(priv->pciaddrs);
     virDomainCCWAddressSetFree(priv->ccwaddrs);
+    virDomainVirtioSerialAddrSetFree(priv->vioserialaddrs);
     virDomainChrSourceDefFree(priv->monConfig);
     qemuDomainObjFreeJob(priv);
     VIR_FREE(priv->vcpupids);
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 3bda446..bd27660 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -144,6 +144,7 @@ struct _qemuDomainObjPrivate {
 
     virDomainPCIAddressSetPtr pciaddrs;
     virDomainCCWAddressSetPtr ccwaddrs;
+    virDomainVirtioSerialAddrSetPtr vioserialaddrs;
     int persistentAddrs;
 
     virQEMUCapsPtr qemuCaps;
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 9cd6a3e..6e4c456 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -367,6 +367,7 @@ int qemuDomainAttachControllerDevice(virQEMUDriverPtr driver,
     char *devstr = NULL;
     qemuDomainObjPrivatePtr priv = vm->privateData;
     bool releaseaddr = false;
+    bool needs_remove = false;;
 
     if (virDomainControllerFind(vm->def, controller->type, controller->idx) >= 0) {
         virReportError(VIR_ERR_OPERATION_FAILED,
@@ -405,6 +406,12 @@ int qemuDomainAttachControllerDevice(virQEMUDriverPtr driver,
             goto cleanup;
         }
 
+        if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL &&
+            virDomainVirtioSerialAddrSetAddController(priv->vioserialaddrs,
+                                                      controller) < 0)
+            goto cleanup;
+        needs_remove = true;
+
         if (!(devstr = qemuBuildControllerDevStr(vm->def, controller, priv->qemuCaps, NULL))) {
             goto cleanup;
         }
@@ -430,6 +437,9 @@ int qemuDomainAttachControllerDevice(virQEMUDriverPtr driver,
     }
 
  cleanup:
+    if (ret != 0 && needs_remove)
+        virDomainVirtioSerialAddrSetRemoveController(priv->vioserialaddrs,
+                                                    controller);
     if (ret != 0 && releaseaddr)
         qemuDomainReleaseDeviceAddress(vm, &controller->info, NULL);
 
@@ -1437,6 +1447,8 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver,
     char *devstr = NULL;
     char *charAlias = NULL;
     bool need_remove = false;
+    bool need_release = false;
+    bool allowZero = false;
 
     if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
@@ -1447,6 +1459,16 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver,
     if (qemuAssignDeviceChrAlias(vmdef, chr, -1) < 0)
         return ret;
 
+    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
+        chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO)
+        allowZero = true;
+
+    if (virDomainVirtioSerialAddrAutoAssign(priv->vioserialaddrs,
+                                            &chr->info,
+                                            allowZero) < 0)
+        goto cleanup;
+    need_release = true;
+
     if (qemuBuildChrDeviceStr(&devstr, vm->def, chr, priv->qemuCaps) < 0)
         return ret;
 
@@ -1475,6 +1497,8 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver,
 
     ret = 0;
  cleanup:
+    if (ret < 0 && need_release)
+        virDomainVirtioSerialAddrRelease(priv->vioserialaddrs, &chr->info);
     if (ret < 0 && need_remove)
         qemuDomainChrRemove(vmdef, chr);
     VIR_FREE(charAlias);
@@ -3638,10 +3662,13 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver,
     qemuDomainObjExitMonitor(driver, vm);
 
     rc = qemuDomainWaitForDeviceRemoval(vm);
-    if (rc == 0 || rc == 1)
+    if (rc == 0 || rc == 1) {
+        virDomainVirtioSerialAddrRelease(priv->vioserialaddrs, &tmpChr->info);
         ret = qemuDomainRemoveChrDevice(driver, vm, tmpChr);
-    else
+    } else {
         ret = 0;
+    }
+
 
  cleanup:
     qemuDomainResetDeviceRemoval(vm);
diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c
index 9d39968..f971ae2 100644
--- a/tests/qemuhotplugtest.c
+++ b/tests/qemuhotplugtest.c
@@ -84,7 +84,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
     if (event)
         virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT);
 
-    if (qemuDomainAssignPCIAddresses((*vm)->def, priv->qemuCaps, *vm) < 0)
+    if (qemuDomainAssignAddresses((*vm)->def, priv->qemuCaps, *vm) < 0)
         goto cleanup;
 
     if (qemuAssignDeviceAliases((*vm)->def, priv->qemuCaps) < 0)
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args
index f7d7409..71edfae 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args
@@ -9,14 +9,14 @@ virtio-serial-pci,id=virtio-serial2,bus=pci.0,addr=0x4 -usb -hda \
 /dev/HostVG/QEMUGuest1 -chardev pty,id=charchannel0 -device virtserialport,\
 bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,\
 name=org.linux-kvm.port.0 -chardev pty,id=charchannel1 -device virtserialport,\
-bus=virtio-serial1.0,nr=1,chardev=charchannel1,id=channel1,\
+bus=virtio-serial0.0,nr=2,chardev=charchannel1,id=channel1,\
 name=org.linux-kvm.port.foo -chardev pty,id=charchannel2 -device \
 virtserialport,bus=virtio-serial1.0,nr=3,chardev=charchannel2,id=channel2,\
 name=org.linux-kvm.port.bar -chardev pty,id=charchannel3 -device \
-virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel3,id=channel3,\
+virtserialport,bus=virtio-serial0.0,nr=3,chardev=charchannel3,id=channel3,\
 name=org.linux-kvm.port.wizz -chardev pty,id=charchannel4 -device \
-virtserialport,bus=virtio-serial1.0,nr=4,chardev=charchannel4,id=channel4,\
+virtserialport,bus=virtio-serial0.0,nr=4,chardev=charchannel4,id=channel4,\
 name=org.linux-kvm.port.ooh -chardev pty,id=charchannel5 -device \
-virtserialport,bus=virtio-serial2.0,nr=1,chardev=charchannel5,id=channel5,\
+virtserialport,bus=virtio-serial0.0,nr=5,chardev=charchannel5,id=channel5,\
 name=org.linux-kvm.port.lla -device virtio-balloon-pci,id=balloon0,\
 bus=pci.0,addr=0x5
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-autoassign.args b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-autoassign.args
index d64a228..f11039d 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-autoassign.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-autoassign.args
@@ -5,16 +5,16 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
 -device virtio-serial-pci,id=virtio-serial0,max_ports=4,vectors=4,bus=pci.0\
 ,addr=0x3 -device virtio-serial-pci,id=virtio-serial1,bus=pci.0,addr=0xa \
 -usb -hda /dev/HostVG/QEMUGuest1 \
--chardev pty,id=charchannel0 -device virtserialport,bus=virtio-serial0.0,nr=1,\
+-chardev pty,id=charchannel0 -device virtserialport,bus=virtio-serial0.0,nr=2,\
 chardev=charchannel0,id=channel0,name=org.linux-kvm.port.0 \
--chardev pty,id=charchannel1 -device virtserialport,bus=virtio-serial0.0,nr=2,\
+-chardev pty,id=charchannel1 -device virtserialport,bus=virtio-serial0.0,nr=3,\
 chardev=charchannel1,id=channel1,name=org.linux-kvm.port.foo \
 -chardev pty,id=charchannel2 -device virtserialport,bus=virtio-serial0.0,nr=1,\
 chardev=charchannel2,id=channel2,name=org.linux-kvm.port.bar \
--chardev pty,id=charchannel3 -device virtserialport,bus=virtio-serial0.2,nr=1,\
+-chardev pty,id=charchannel3 -device virtserialport,bus=virtio-serial1.0,nr=1,\
 chardev=charchannel3,id=channel3,name=org.linux-kvm.port.wizz \
--chardev pty,id=charchannel4 -device virtserialport,bus=virtio-serial0.0,nr=3,\
+-chardev pty,id=charchannel4 -device virtserialport,bus=virtio-serial1.0,nr=2,\
 chardev=charchannel4,id=channel4,name=org.linux-kvm.port.ooh \
--chardev pty,id=charchannel5 -device virtserialport,bus=virtio-serial0.0,nr=4,\
+-chardev pty,id=charchannel5 -device virtserialport,bus=virtio-serial1.0,nr=3,\
 chardev=charchannel5,id=channel5,name=org.linux-kvm.port.lla \
 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-auto.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-auto.xml
index fd6b852..afe41cf 100644
--- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-auto.xml
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-channel-virtio-auto.xml
@@ -29,11 +29,11 @@
     <controller type='virtio-serial' index='2'/>
     <channel type='pty'>
       <target type='virtio' name='org.linux-kvm.port.0'/>
-      <address type='virtio-serial' controller='0' bus='0' port='1'/>
+      <address type='virtio-serial' controller='0' bus='0' port='0'/>
     </channel>
     <channel type='pty'>
       <target type='virtio' name='org.linux-kvm.port.foo'/>
-      <address type='virtio-serial' controller='1' bus='0' port='1'/>
+      <address type='virtio-serial' controller='1' bus='0' port='0'/>
     </channel>
     <channel type='pty'>
       <target type='virtio' name='org.linux-kvm.port.bar'/>
@@ -41,15 +41,15 @@
     </channel>
     <channel type='pty'>
       <target type='virtio' name='org.linux-kvm.port.wizz'/>
-      <address type='virtio-serial' controller='0' bus='0' port='2'/>
+      <address type='virtio-serial' controller='0' bus='0' port='0'/>
     </channel>
     <channel type='pty'>
       <target type='virtio' name='org.linux-kvm.port.ooh'/>
-      <address type='virtio-serial' controller='1' bus='0' port='4'/>
+      <address type='virtio-serial' controller='1' bus='0' port='0'/>
     </channel>
     <channel type='pty'>
       <target type='virtio' name='org.linux-kvm.port.lla'/>
-      <address type='virtio-serial' controller='2' bus='0' port='1'/>
+      <address type='virtio-serial' controller='2' bus='0' port='0'/>
     </channel>
     <memballoon model='virtio'/>
   </devices>
-- 
1.8.5.5




More information about the libvir-list mailing list