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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list