libvirt/src/conf/domain_addr.c
Ján Tomko 22f9754f1b mark virDomainVirtioSerialAddrSetAddController as static.
This function is no longer used outside domain_addr.c
2015-12-17 16:57:25 +01:00

1176 lines
36 KiB
C

/*
* domain_addr.c: helper APIs for managing domain device addresses
*
* Copyright (C) 2006-2015 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include "viralloc.h"
#include "virlog.h"
#include "virstring.h"
#include "domain_addr.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN
VIR_LOG_INIT("conf.domain_addr");
bool
virDomainPCIAddressFlagsCompatible(virDevicePCIAddressPtr addr,
const char *addrStr,
virDomainPCIConnectFlags busFlags,
virDomainPCIConnectFlags devFlags,
bool reportError,
bool fromConfig)
{
virErrorNumber errType = (fromConfig
? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR);
if (fromConfig) {
/* If the requested connection was manually specified in
* config, allow a PCI device to connect to a PCIe slot, or
* vice versa.
*/
if (busFlags & VIR_PCI_CONNECT_TYPES_ENDPOINT)
busFlags |= VIR_PCI_CONNECT_TYPES_ENDPOINT;
/* Also allow manual specification of bus to override
* libvirt's assumptions about whether or not hotplug
* capability will be required.
*/
if (devFlags & VIR_PCI_CONNECT_HOTPLUGGABLE)
busFlags |= VIR_PCI_CONNECT_HOTPLUGGABLE;
}
/* If this bus doesn't allow the type of connection (PCI
* vs. PCIe) required by the device, or if the device requires
* hot-plug and this bus doesn't have it, return false.
*/
if (!(devFlags & busFlags & VIR_PCI_CONNECT_TYPES_MASK)) {
if (reportError) {
if (devFlags & VIR_PCI_CONNECT_TYPE_PCI) {
virReportError(errType,
_("PCI bus is not compatible with the device "
"at %s. Device requires a standard PCI slot, "
"which is not provided by bus %.4x:%.2x"),
addrStr, addr->domain, addr->bus);
} else if (devFlags & VIR_PCI_CONNECT_TYPE_PCIE) {
virReportError(errType,
_("PCI bus is not compatible with the device "
"at %s. Device requires a PCI Express slot, "
"which is not provided by bus %.4x:%.2x"),
addrStr, addr->domain, addr->bus);
} else {
/* this should never happen. If it does, there is a
* bug in the code that sets the flag bits for devices.
*/
virReportError(errType,
_("The device information for %s has no PCI "
"connection types listed"), addrStr);
}
}
return false;
}
if ((devFlags & VIR_PCI_CONNECT_HOTPLUGGABLE) &&
!(busFlags & VIR_PCI_CONNECT_HOTPLUGGABLE)) {
if (reportError) {
virReportError(errType,
_("PCI bus is not compatible with the device "
"at %s. Device requires hot-plug capability, "
"which is not provided by bus %.4x:%.2x"),
addrStr, addr->domain, addr->bus);
}
return false;
}
return true;
}
/* Verify that the address is in bounds for the chosen bus, and
* that the bus is of the correct type for the device (via
* comparing the flags).
*/
bool
virDomainPCIAddressValidate(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr,
const char *addrStr,
virDomainPCIConnectFlags flags,
bool fromConfig)
{
virDomainPCIAddressBusPtr bus;
virErrorNumber errType = (fromConfig
? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR);
if (addrs->nbuses == 0) {
virReportError(errType, "%s", _("No PCI buses available"));
return false;
}
if (addr->domain != 0) {
virReportError(errType,
_("Invalid PCI address %s. "
"Only PCI domain 0 is available"),
addrStr);
return false;
}
if (addr->bus >= addrs->nbuses) {
virReportError(errType,
_("Invalid PCI address %s. "
"Only PCI buses up to %zu are available"),
addrStr, addrs->nbuses - 1);
return false;
}
bus = &addrs->buses[addr->bus];
/* assure that at least one of the requested connection types is
* provided by this bus
*/
if (!virDomainPCIAddressFlagsCompatible(addr, addrStr, bus->flags,
flags, true, fromConfig))
return false;
/* some "buses" are really just a single port */
if (bus->minSlot && addr->slot < bus->minSlot) {
virReportError(errType,
_("Invalid PCI address %s. slot must be >= %zu"),
addrStr, bus->minSlot);
return false;
}
if (addr->slot > bus->maxSlot) {
virReportError(errType,
_("Invalid PCI address %s. slot must be <= %zu"),
addrStr, bus->maxSlot);
return false;
}
if (addr->function > VIR_PCI_ADDRESS_FUNCTION_LAST) {
virReportError(errType,
_("Invalid PCI address %s. function must be <= %u"),
addrStr, VIR_PCI_ADDRESS_FUNCTION_LAST);
return false;
}
return true;
}
int
virDomainPCIAddressBusSetModel(virDomainPCIAddressBusPtr bus,
virDomainControllerModelPCI model)
{
switch (model) {
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
bus->flags = (VIR_PCI_CONNECT_HOTPLUGGABLE |
VIR_PCI_CONNECT_TYPE_PCI);
bus->minSlot = 1;
bus->maxSlot = VIR_PCI_ADDRESS_SLOT_LAST;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
/* slots 1 - 31, no hotplug, PCIe only unless the address was
* specified in user config *and* the particular device being
* attached also allows it.
*/
bus->flags = VIR_PCI_CONNECT_TYPE_PCIE | VIR_PCI_CONNECT_TYPE_PCIE_ROOT;
bus->minSlot = 1;
bus->maxSlot = VIR_PCI_ADDRESS_SLOT_LAST;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
/* slots 1 - 31, standard PCI slots,
* but *not* hot-pluggable */
bus->flags = VIR_PCI_CONNECT_TYPE_PCI;
bus->minSlot = 1;
bus->maxSlot = VIR_PCI_ADDRESS_SLOT_LAST;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
/* provides one slot which is pcie, can be used by devices
* that must connect to some type of "pcie-*-port", and
* is hotpluggable
*/
bus->flags = VIR_PCI_CONNECT_TYPE_PCIE
| VIR_PCI_CONNECT_TYPE_PCIE_PORT
| VIR_PCI_CONNECT_HOTPLUGGABLE;
bus->minSlot = 0;
bus->maxSlot = 0;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
/* 31 slots, can only accept pcie-switch-port, no hotplug */
bus->flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH;
bus->minSlot = 0;
bus->maxSlot = 31;
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid PCI controller model %d"), model);
return -1;
}
bus->model = model;
return 0;
}
/* Ensure addr fits in the address set, by expanding it if needed.
* This will only grow if the flags say that we need a normal
* hot-pluggable PCI slot. If we need a different type of slot, it
* will fail.
*
* Return value:
* -1 = OOM
* 0 = no action performed
* >0 = number of buses added
*/
int
virDomainPCIAddressSetGrow(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr,
virDomainPCIConnectFlags flags)
{
int add;
size_t i;
add = addr->bus - addrs->nbuses + 1;
i = addrs->nbuses;
if (add <= 0)
return 0;
/* auto-grow only works when we're adding plain PCI devices */
if (!(flags & VIR_PCI_CONNECT_TYPE_PCI)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot automatically add a new PCI bus for a "
"device requiring a slot other than standard PCI."));
return -1;
}
if (VIR_EXPAND_N(addrs->buses, addrs->nbuses, add) < 0)
return -1;
for (; i < addrs->nbuses; i++) {
/* Any time we auto-add a bus, we will want a multi-slot
* bus. Currently the only type of bus we will auto-add is a
* pci-bridge, which is hot-pluggable and provides standard
* PCI slots.
*/
virDomainPCIAddressBusSetModel(&addrs->buses[i],
VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE);
}
return add;
}
char *
virDomainPCIAddressAsString(virDevicePCIAddressPtr addr)
{
char *str;
ignore_value(virAsprintf(&str, "%.4x:%.2x:%.2x.%.1x",
addr->domain,
addr->bus,
addr->slot,
addr->function));
return str;
}
/*
* Check if the PCI slot is used by another device.
*/
bool
virDomainPCIAddressSlotInUse(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr)
{
return !!addrs->buses[addr->bus].slots[addr->slot];
}
/*
* Reserve a slot (or just one function) for a device. If
* reserveEntireSlot is true, all functions for the slot are reserved,
* otherwise only one. If fromConfig is true, the address being
* requested came directly from the config and errors should be worded
* appropriately. If fromConfig is false, the address was
* automatically created by libvirt, so it is an internal error (not
* XML).
*/
int
virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr,
virDomainPCIConnectFlags flags,
bool reserveEntireSlot,
bool fromConfig)
{
int ret = -1;
char *addrStr = NULL;
virDomainPCIAddressBusPtr bus;
virErrorNumber errType = (fromConfig
? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR);
if (!(addrStr = virDomainPCIAddressAsString(addr)))
goto cleanup;
/* Add an extra bus if necessary */
if (addrs->dryRun && virDomainPCIAddressSetGrow(addrs, addr, flags) < 0)
goto cleanup;
/* Check that the requested bus exists, is the correct type, and we
* are asking for a valid slot
*/
if (!virDomainPCIAddressValidate(addrs, addr, addrStr, flags, fromConfig))
goto cleanup;
bus = &addrs->buses[addr->bus];
if (reserveEntireSlot) {
if (bus->slots[addr->slot]) {
virReportError(errType,
_("Attempted double use of PCI slot %s "
"(may need \"multifunction='on'\" for "
"device on function 0)"), addrStr);
goto cleanup;
}
bus->slots[addr->slot] = 0xFF; /* reserve all functions of slot */
VIR_DEBUG("Reserving PCI slot %s (multifunction='off')", addrStr);
} else {
if (bus->slots[addr->slot] & (1 << addr->function)) {
if (addr->function == 0) {
virReportError(errType,
_("Attempted double use of PCI Address %s"),
addrStr);
} else {
virReportError(errType,
_("Attempted double use of PCI Address %s "
"(may need \"multifunction='on'\" "
"for device on function 0)"), addrStr);
}
goto cleanup;
}
bus->slots[addr->slot] |= (1 << addr->function);
VIR_DEBUG("Reserving PCI address %s", addrStr);
}
ret = 0;
cleanup:
VIR_FREE(addrStr);
return ret;
}
int
virDomainPCIAddressReserveSlot(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr,
virDomainPCIConnectFlags flags)
{
return virDomainPCIAddressReserveAddr(addrs, addr, flags, true, false);
}
int
virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs,
virDomainDeviceInfoPtr dev)
{
int ret = -1;
char *addrStr = NULL;
/* Flags should be set according to the particular device,
* but only the caller knows the type of device. Currently this
* function is only used for hot-plug, though, and hot-plug is
* only supported for standard PCI devices, so we can safely use
* the setting below */
virDomainPCIConnectFlags flags = (VIR_PCI_CONNECT_HOTPLUGGABLE |
VIR_PCI_CONNECT_TYPE_PCI);
if (!(addrStr = virDomainPCIAddressAsString(&dev->addr.pci)))
goto cleanup;
if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
/* We do not support hotplug multi-function PCI device now, so we should
* reserve the whole slot. The function of the PCI device must be 0.
*/
if (dev->addr.pci.function != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Only PCI device addresses with function=0"
" are supported"));
goto cleanup;
}
if (!virDomainPCIAddressValidate(addrs, &dev->addr.pci,
addrStr, flags, true))
goto cleanup;
ret = virDomainPCIAddressReserveSlot(addrs, &dev->addr.pci, flags);
} else {
ret = virDomainPCIAddressReserveNextSlot(addrs, dev, flags);
}
cleanup:
VIR_FREE(addrStr);
return ret;
}
int
virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr)
{
addrs->buses[addr->bus].slots[addr->slot] &= ~(1 << addr->function);
return 0;
}
int
virDomainPCIAddressReleaseSlot(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr addr)
{
/* permit any kind of connection type in validation, since we
* already had it, and are giving it back.
*/
virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPES_MASK;
int ret = -1;
char *addrStr = NULL;
if (!(addrStr = virDomainPCIAddressAsString(addr)))
goto cleanup;
if (!virDomainPCIAddressValidate(addrs, addr, addrStr, flags, false))
goto cleanup;
addrs->buses[addr->bus].slots[addr->slot] = 0;
ret = 0;
cleanup:
VIR_FREE(addrStr);
return ret;
}
virDomainPCIAddressSetPtr
virDomainPCIAddressSetAlloc(unsigned int nbuses)
{
virDomainPCIAddressSetPtr addrs;
if (VIR_ALLOC(addrs) < 0)
goto error;
if (VIR_ALLOC_N(addrs->buses, nbuses) < 0)
goto error;
addrs->nbuses = nbuses;
return addrs;
error:
virDomainPCIAddressSetFree(addrs);
return NULL;
}
void
virDomainPCIAddressSetFree(virDomainPCIAddressSetPtr addrs)
{
if (!addrs)
return;
VIR_FREE(addrs->buses);
VIR_FREE(addrs);
}
int
virDomainPCIAddressGetNextSlot(virDomainPCIAddressSetPtr addrs,
virDevicePCIAddressPtr next_addr,
virDomainPCIConnectFlags flags)
{
/* default to starting the search for a free slot from
* the first slot of domain 0 bus 0...
*/
virDevicePCIAddress a = { 0, 0, 0, 0, false };
char *addrStr = NULL;
if (addrs->nbuses == 0) {
virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses available"));
goto error;
}
/* ...unless this search is for the exact same type of device as
* last time, then continue the search from the next slot after
* the previous match (the "next slot" may possibly be the first
* slot of the next bus).
*/
if (flags == addrs->lastFlags) {
a = addrs->lastaddr;
if (++a.slot > addrs->buses[a.bus].maxSlot &&
++a.bus < addrs->nbuses)
a.slot = addrs->buses[a.bus].minSlot;
} else {
a.slot = addrs->buses[0].minSlot;
}
while (a.bus < addrs->nbuses) {
VIR_FREE(addrStr);
if (!(addrStr = virDomainPCIAddressAsString(&a)))
goto error;
if (!virDomainPCIAddressFlagsCompatible(&a, addrStr,
addrs->buses[a.bus].flags,
flags, false, false)) {
VIR_DEBUG("PCI bus %.4x:%.2x is not compatible with the device",
a.domain, a.bus);
} else {
while (a.slot <= addrs->buses[a.bus].maxSlot) {
if (!virDomainPCIAddressSlotInUse(addrs, &a))
goto success;
VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
a.domain, a.bus, a.slot);
a.slot++;
}
}
if (++a.bus < addrs->nbuses)
a.slot = addrs->buses[a.bus].minSlot;
}
/* There were no free slots after the last used one */
if (addrs->dryRun) {
/* a is already set to the first new bus */
if (virDomainPCIAddressSetGrow(addrs, &a, flags) < 0)
goto error;
/* this device will use the first slot of the new bus */
a.slot = addrs->buses[a.bus].minSlot;
goto success;
} else if (flags == addrs->lastFlags) {
/* Check the buses from 0 up to the last used one */
for (a.bus = 0; a.bus <= addrs->lastaddr.bus; a.bus++) {
a.slot = addrs->buses[a.bus].minSlot;
VIR_FREE(addrStr);
if (!(addrStr = virDomainPCIAddressAsString(&a)))
goto error;
if (!virDomainPCIAddressFlagsCompatible(&a, addrStr,
addrs->buses[a.bus].flags,
flags, false, false)) {
VIR_DEBUG("PCI bus %.4x:%.2x is not compatible with the device",
a.domain, a.bus);
} else {
while (a.slot <= addrs->buses[a.bus].maxSlot) {
if (!virDomainPCIAddressSlotInUse(addrs, &a))
goto success;
VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
a.domain, a.bus, a.slot);
a.slot++;
}
}
}
}
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("No more available PCI slots"));
error:
VIR_FREE(addrStr);
return -1;
success:
VIR_DEBUG("Found free PCI slot %.4x:%.2x:%.2x",
a.domain, a.bus, a.slot);
*next_addr = a;
VIR_FREE(addrStr);
return 0;
}
int
virDomainPCIAddressReserveNextSlot(virDomainPCIAddressSetPtr addrs,
virDomainDeviceInfoPtr dev,
virDomainPCIConnectFlags flags)
{
virDevicePCIAddress addr;
if (virDomainPCIAddressGetNextSlot(addrs, &addr, flags) < 0)
return -1;
if (virDomainPCIAddressReserveSlot(addrs, &addr, flags) < 0)
return -1;
if (!addrs->dryRun) {
dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
dev->addr.pci = addr;
}
addrs->lastaddr = addr;
addrs->lastFlags = flags;
return 0;
}
static char*
virDomainCCWAddressAsString(virDomainDeviceCCWAddressPtr addr)
{
char *addrstr = NULL;
ignore_value(virAsprintf(&addrstr, "%x.%x.%04x",
addr->cssid,
addr->ssid,
addr->devno));
return addrstr;
}
static int
virDomainCCWAddressIncrement(virDomainDeviceCCWAddressPtr addr)
{
virDomainDeviceCCWAddress ccwaddr = *addr;
/* We are not touching subchannel sets and channel subsystems */
if (++ccwaddr.devno > VIR_DOMAIN_DEVICE_CCW_MAX_DEVNO)
return -1;
*addr = ccwaddr;
return 0;
}
int
virDomainCCWAddressAssign(virDomainDeviceInfoPtr dev,
virDomainCCWAddressSetPtr addrs,
bool autoassign)
{
int ret = -1;
char *addr = NULL;
if (dev->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)
return 0;
if (!autoassign && dev->addr.ccw.assigned) {
if (!(addr = virDomainCCWAddressAsString(&dev->addr.ccw)))
goto cleanup;
if (virHashLookup(addrs->defined, addr)) {
virReportError(VIR_ERR_XML_ERROR,
_("The CCW devno '%s' is in use already "),
addr);
goto cleanup;
}
} else if (autoassign && !dev->addr.ccw.assigned) {
if (!(addr = virDomainCCWAddressAsString(&addrs->next)))
goto cleanup;
while (virHashLookup(addrs->defined, addr)) {
if (virDomainCCWAddressIncrement(&addrs->next) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("There are no more free CCW devnos."));
goto cleanup;
}
VIR_FREE(addr);
if (!(addr = virDomainCCWAddressAsString(&addrs->next)))
goto cleanup;
}
dev->addr.ccw = addrs->next;
dev->addr.ccw.assigned = true;
} else {
return 0;
}
if (virHashAddEntry(addrs->defined, addr, addr) < 0)
goto cleanup;
else
addr = NULL; /* memory will be freed by hash table */
ret = 0;
cleanup:
VIR_FREE(addr);
return ret;
}
int
virDomainCCWAddressAllocate(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *data)
{
return virDomainCCWAddressAssign(info, data, true);
}
int
virDomainCCWAddressValidate(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *data)
{
return virDomainCCWAddressAssign(info, data, false);
}
int
virDomainCCWAddressReleaseAddr(virDomainCCWAddressSetPtr addrs,
virDomainDeviceInfoPtr dev)
{
char *addr;
int ret;
addr = virDomainCCWAddressAsString(&(dev->addr.ccw));
if (!addr)
return -1;
if ((ret = virHashRemoveEntry(addrs->defined, addr)) == 0 &&
dev->addr.ccw.cssid == addrs->next.cssid &&
dev->addr.ccw.ssid == addrs->next.ssid &&
dev->addr.ccw.devno < addrs->next.devno) {
addrs->next.devno = dev->addr.ccw.devno;
addrs->next.assigned = false;
}
VIR_FREE(addr);
return ret;
}
void virDomainCCWAddressSetFree(virDomainCCWAddressSetPtr addrs)
{
if (!addrs)
return;
virHashFree(addrs->defined);
VIR_FREE(addrs);
}
virDomainCCWAddressSetPtr
virDomainCCWAddressSetCreate(void)
{
virDomainCCWAddressSetPtr addrs = NULL;
if (VIR_ALLOC(addrs) < 0)
goto error;
if (!(addrs->defined = virHashCreate(10, virHashValueFree)))
goto error;
/* must use cssid = 0xfe (254) for virtio-ccw devices */
addrs->next.cssid = 254;
addrs->next.ssid = 0;
addrs->next.devno = 0;
addrs->next.assigned = 0;
return addrs;
error:
virDomainCCWAddressSetFree(addrs);
return NULL;
}
#define VIR_DOMAIN_DEFAULT_VIRTIO_SERIAL_PORTS 31
/* virDomainVirtioSerialAddrSetCreate
*
* Allocates an address set for virtio serial addresses
*/
virDomainVirtioSerialAddrSetPtr
virDomainVirtioSerialAddrSetCreate(void)
{
virDomainVirtioSerialAddrSetPtr ret = NULL;
if (VIR_ALLOC(ret) < 0)
return NULL;
return ret;
}
static void
virDomainVirtioSerialControllerFree(virDomainVirtioSerialControllerPtr cont)
{
if (cont) {
virBitmapFree(cont->ports);
VIR_FREE(cont);
}
}
static ssize_t
virDomainVirtioSerialAddrPlaceController(virDomainVirtioSerialAddrSetPtr addrs,
virDomainVirtioSerialControllerPtr cont)
{
size_t i;
for (i = 0; i < addrs->ncontrollers; i++) {
if (addrs->controllers[i]->idx == cont->idx) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("virtio serial controller with index %u already exists"
" in the address set"),
cont->idx);
return -2;
}
if (addrs->controllers[i]->idx > cont->idx)
return i;
}
return -1;
}
static ssize_t
virDomainVirtioSerialAddrFindController(virDomainVirtioSerialAddrSetPtr addrs,
unsigned int idx)
{
size_t i;
for (i = 0; i < addrs->ncontrollers; i++) {
if (addrs->controllers[i]->idx == idx)
return i;
}
return -1;
}
/* virDomainVirtioSerialAddrSetAddController
*
* Adds virtio serial ports of the existing controller
* to the address set.
*/
static int
virDomainVirtioSerialAddrSetAddController(virDomainVirtioSerialAddrSetPtr addrs,
virDomainControllerDefPtr cont)
{
int ret = -1;
int ports;
virDomainVirtioSerialControllerPtr cnt = NULL;
ssize_t insertAt;
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 (VIR_ALLOC(cnt) < 0)
goto cleanup;
if (!(cnt->ports = virBitmapNew(ports)))
goto cleanup;
cnt->idx = cont->idx;
if ((insertAt = virDomainVirtioSerialAddrPlaceController(addrs, cnt)) < -1)
goto cleanup;
if (VIR_INSERT_ELEMENT(addrs->controllers, insertAt,
addrs->ncontrollers, cnt) < 0)
goto cleanup;
ret = 0;
cleanup:
virDomainVirtioSerialControllerFree(cnt);
return ret;
}
/* virDomainVirtioSerialAddrSetAddControllers
*
* Adds virtio serial ports of controllers present in the domain definition
* 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)
{
size_t i;
if (addrs) {
for (i = 0; i < addrs->ncontrollers; i++)
virDomainVirtioSerialControllerFree(addrs->controllers[i]);
VIR_FREE(addrs->controllers);
VIR_FREE(addrs);
}
}
static int
virDomainVirtioSerialAddrSetAutoaddController(virDomainDefPtr def,
virDomainVirtioSerialAddrSetPtr addrs,
unsigned int idx)
{
int contidx;
if (virDomainDefMaybeAddController(def,
VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL,
idx, -1) < 0)
return -1;
contidx = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, idx);
if (virDomainVirtioSerialAddrSetAddController(addrs, def->controllers[contidx]) < 0)
return -1;
return 0;
}
static int
virDomainVirtioSerialAddrNext(virDomainDefPtr def,
virDomainVirtioSerialAddrSetPtr addrs,
virDomainDeviceVirtioSerialAddress *addr,
bool allowZero)
{
int ret = -1;
ssize_t port, startPort = 0;
ssize_t i;
unsigned int controller;
/* port number 0 is reserved for virtconsoles */
if (allowZero)
startPort = -1;
if (addrs->ncontrollers == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("no virtio-serial controllers are available"));
goto cleanup;
}
for (i = 0; i < addrs->ncontrollers; i++) {
virBitmapPtr map = addrs->controllers[i]->ports;
if ((port = virBitmapNextClearBit(map, startPort)) >= 0) {
controller = addrs->controllers[i]->idx;
goto success;
}
}
if (def) {
for (i = 0; i < INT_MAX; i++) {
int idx = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, i);
if (idx == -1) {
if (virDomainVirtioSerialAddrSetAutoaddController(def, addrs, i) < 0)
goto cleanup;
controller = i;
port = startPort + 1;
goto success;
}
}
}
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Unable to find a free virtio-serial port"));
cleanup:
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;
}
static int
virDomainVirtioSerialAddrNextFromController(virDomainVirtioSerialAddrSetPtr addrs,
virDomainDeviceVirtioSerialAddress *addr)
{
ssize_t port;
ssize_t i;
virBitmapPtr map;
i = virDomainVirtioSerialAddrFindController(addrs, addr->controller);
if (i < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("virtio-serial controller %u not available"),
addr->controller);
return -1;
}
map = addrs->controllers[i]->ports;
if ((port = virBitmapNextClearBit(map, 0)) <= 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Unable to find a free port on virtio-serial controller %u"),
addr->controller);
return -1;
}
addr->bus = 0;
addr->port = port;
VIR_DEBUG("Found free virtio serial controller %u port %u", addr->controller,
addr->port);
return 0;
}
/* virDomainVirtioSerialAddrAutoAssign
*
* reserve a virtio serial address of the device (if it has one)
* or assign a virtio serial address to the device
*/
int
virDomainVirtioSerialAddrAutoAssign(virDomainDefPtr def,
virDomainVirtioSerialAddrSetPtr addrs,
virDomainDeviceInfoPtr info,
bool allowZero)
{
bool portOnly = info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL;
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
info->addr.vioserial.port)
return virDomainVirtioSerialAddrReserve(NULL, NULL, info, addrs);
else
return virDomainVirtioSerialAddrAssign(def, addrs, info, allowZero, portOnly);
}
int
virDomainVirtioSerialAddrAssign(virDomainDefPtr def,
virDomainVirtioSerialAddrSetPtr addrs,
virDomainDeviceInfoPtr info,
bool allowZero,
bool portOnly)
{
int ret = -1;
virDomainDeviceInfo nfo = { NULL };
virDomainDeviceInfoPtr ptr = allowZero ? &nfo : info;
ptr->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL;
if (portOnly) {
if (virDomainVirtioSerialAddrNextFromController(addrs,
&ptr->addr.vioserial) < 0)
goto cleanup;
} else {
if (virDomainVirtioSerialAddrNext(def, addrs, &ptr->addr.vioserial,
allowZero) < 0)
goto cleanup;
}
if (virDomainVirtioSerialAddrReserve(NULL, NULL, ptr, addrs) < 0)
goto cleanup;
ret = 0;
cleanup:
return ret;
}
/* virDomainVirtioSerialAddrIsComplete
*
* Check if the address is complete, or it needs auto-assignment
*/
bool
virDomainVirtioSerialAddrIsComplete(virDomainDeviceInfoPtr info)
{
return info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
info->addr.vioserial.port != 0;
}
/* 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;
char *str = NULL;
int ret = -1;
virBitmapPtr map = NULL;
bool b;
ssize_t i;
if (!virDomainVirtioSerialAddrIsComplete(info))
return 0;
VIR_DEBUG("Reserving virtio serial %u %u", info->addr.vioserial.controller,
info->addr.vioserial.port);
i = virDomainVirtioSerialAddrFindController(addrs, info->addr.vioserial.controller);
if (i < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("virtio serial controller %u is missing"),
info->addr.vioserial.controller);
goto cleanup;
}
map = addrs->controllers[i]->ports;
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;
ssize_t i;
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);
i = virDomainVirtioSerialAddrFindController(addrs, info->addr.vioserial.controller);
if (i < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("virtio serial controller %u is missing"),
info->addr.vioserial.controller);
goto cleanup;
}
map = addrs->controllers[i]->ports;
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;
}