Store the available ports of a virtio-serial controller in a virBitmap.
The bitmaps are stored in a hash table - the controller index
formatted as a string.
Buses are not tracked, because they aren't supported by QEMU.
---
src/conf/domain_addr.c | 382 +++++++++++++++++++++++++++++++++++++++++++++++
src/conf/domain_addr.h | 45 ++++++
src/libvirt_private.syms | 8 +
3 files changed, 435 insertions(+)
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index fb4a76f..654c95a 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -718,3 +718,385 @@ virDomainCCWAddressSetCreate(void)
virDomainCCWAddressSetFree(addrs);
return NULL;
}
+
+
+#define VIR_DOMAIN_DEFAULT_VIRTIO_SERIAL_PORTS 31
+
+
+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 controller
+ * 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 = VIR_DOMAIN_DEFAULT_VIRTIO_SERIAL_PORTS;
+
+ VIR_DEBUG("Adding virtio serial controller index %u with %d"
+ " ports to the address set", cont->idx, ports);
+
+ if (!(map = virBitmapNew(ports)))
+ 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;
+}
+
+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);
+ VIR_FREE(str);
+ 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)
+{
+ 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;
+
+ addrs->next = info->addr.vioserial;
+
+ if (virDomainVirtioSerialAddrReserve(NULL, NULL, ptr, addrs) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ 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 does not have port %u"),
+ info->addr.vioserial.controller,
+ info->addr.vioserial.port);
+ goto cleanup;
+ }
+
+ if (b) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("virtio serial port %u on controller %u is already
occupied"),
+ info->addr.vioserial.port,
+ info->addr.vioserial.controller);
+ 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 does not have port %u"),
+ 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..5c65a98 100644
--- a/src/conf/domain_addr.h
+++ b/src/conf/domain_addr.h
@@ -173,4 +173,49 @@ 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);
+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/libvirt_private.syms b/src/libvirt_private.syms
index 6028002..9f2996a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -108,6 +108,14 @@ virDomainPCIAddressSetFree;
virDomainPCIAddressSetGrow;
virDomainPCIAddressSlotInUse;
virDomainPCIAddressValidate;
+virDomainVirtioSerialAddrAssign;
+virDomainVirtioSerialAddrAutoAssign;
+virDomainVirtioSerialAddrRelease;
+virDomainVirtioSerialAddrReserve;
+virDomainVirtioSerialAddrSetAddController;
+virDomainVirtioSerialAddrSetAddControllers;
+virDomainVirtioSerialAddrSetCreate;
+virDomainVirtioSerialAddrSetFree;
# conf/domain_audit.h
--
2.0.5