[libvirt] [PATCH v3 13/13] Enable PCI Multifunction hotplug/unplug

Nikunj A Dadhania nikunj at linux.vnet.ibm.com
Tue May 24 14:33:38 UTC 2016


Shivaprasad G Bhat <shivaprasadbhat at gmail.com> writes:

> The flow is to parse and create a list of devices and pass onto the
> hotplug functions.
>
> The patch also removes all checks forbidding the multifunction hotplug.
>
> Signed-off-by: Shivaprasad G Bhat <sbhat at linux.vnet.ibm.com>
> ---
>  src/qemu/qemu_driver.c  |  166 ++++++++++++++++++++++++++++++++++++++---------
>  src/qemu/qemu_hotplug.c |   66 +------------------
>  2 files changed, 140 insertions(+), 92 deletions(-)
>
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index e970ad6..0519a33 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -7453,6 +7453,77 @@ qemuDomainUndefine(virDomainPtr dom)
>      return qemuDomainUndefineFlags(dom, 0);
>  }
>
> +static bool
> +qemuDomainPCIAddressIsSingleFunctionAddr(virDomainDeviceDefPtr dev)
> +{
> +
> +    virDomainDeviceInfoPtr info = NULL;
> +    switch ((virDomainDeviceType) dev->type) {
> +    case VIR_DOMAIN_DEVICE_DISK:
> +        info = &dev->data.disk->info;
> +        break;
> +    case VIR_DOMAIN_DEVICE_NET:
> +        info = &dev->data.net->info;
> +        break;
> +    case VIR_DOMAIN_DEVICE_RNG:
> +        info = &dev->data.rng->info;
> +        break;
> +    case VIR_DOMAIN_DEVICE_HOSTDEV:
> +        info = dev->data.hostdev->info;
> +        break;
> +    case VIR_DOMAIN_DEVICE_CONTROLLER:
> +        info = &dev->data.controller->info;
> +        break;
> +    case VIR_DOMAIN_DEVICE_CHR:
> +        info = &dev->data.chr->info;

Remove this, not supported for PCI

> +        break;
> +    case VIR_DOMAIN_DEVICE_FS:
> +    case VIR_DOMAIN_DEVICE_INPUT:
> +    case VIR_DOMAIN_DEVICE_SOUND:
> +    case VIR_DOMAIN_DEVICE_VIDEO:
> +    case VIR_DOMAIN_DEVICE_WATCHDOG:
> +    case VIR_DOMAIN_DEVICE_HUB:
> +    case VIR_DOMAIN_DEVICE_SMARTCARD:
> +    case VIR_DOMAIN_DEVICE_MEMBALLOON:
> +    case VIR_DOMAIN_DEVICE_NVRAM:
> +    case VIR_DOMAIN_DEVICE_SHMEM:
> +    case VIR_DOMAIN_DEVICE_LEASE:
> +    case VIR_DOMAIN_DEVICE_REDIRDEV:
> +    case VIR_DOMAIN_DEVICE_MEMORY:
> +    case VIR_DOMAIN_DEVICE_NONE:
> +    case VIR_DOMAIN_DEVICE_TPM:
> +    case VIR_DOMAIN_DEVICE_PANIC:
> +    case VIR_DOMAIN_DEVICE_GRAPHICS:
> +    case VIR_DOMAIN_DEVICE_LAST:
> +        break;
> +    }
> +
> +    if (info && info->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 (info->addr.pci.function != 0) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("Single function device addresses with function=0"
> +                             " expected"));
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static bool isMultifunctionDeviceXML(const char *xml)

isPCIMultifunctionDeviceXML

> +{
> +   xmlDocPtr xmlptr;
> +
> +   if (!(xmlptr = virXMLParse(NULL, xml, _("(device_definition)")))) {
> +       /* We report error anyway later */
> +       return false;
> +   }
> +
> +   return STREQ((const char *)(xmlDocGetRootElement(xmlptr))->name, "devices");
> +}
> +
>
>  static int qemuDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
>                                         unsigned int flags)
> @@ -7469,6 +7540,8 @@ static int qemuDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
>      qemuDomainObjPrivatePtr priv;
>      virQEMUDriverConfigPtr cfg = NULL;
>      virCapsPtr caps = NULL;
> +    bool multifunction = false;
> +    size_t i;
>
>      virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
>                    VIR_DOMAIN_AFFECT_CONFIG, -1);
> @@ -7494,14 +7567,25 @@ static int qemuDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
>      if (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
>          goto endjob;
>
> -    dev = virDomainDeviceDefParse(xml, vm->def,
> -                                             caps, driver->xmlopt,
> -                                             parse_flags);
> -    if (!dev || VIR_ALLOC(devlist) < 0)
> -        goto endjob;
> +    multifunction = isMultifunctionDeviceXML(xml);
>
> -    if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0)
> -        goto endjob;
> +    if (multifunction) {
> +        if (!(devlist = virDomainDeviceDefParseXMLMany(xml, vm->def,
> +                                                       caps, driver->xmlopt,
> +                                                       parse_flags)))
> +            goto endjob;
> +        if (virDomainPCIMultifunctionDeviceAddressValidateAssign(priv->pciaddrs, devlist) < 0)
> +            goto endjob;
> +    } else {
> +        dev = virDomainDeviceDefParse(xml, vm->def,
> +                                      caps, driver->xmlopt,
> +                                      parse_flags);
> +        if (!dev || VIR_ALLOC(devlist) < 0)
> +            goto endjob;
> +        if (!qemuDomainPCIAddressIsSingleFunctionAddr(dev) ||
> +            VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0)
> +                goto endjob;
> +    }
>
>      devcopylist = devlist;
>
> @@ -7513,15 +7597,16 @@ static int qemuDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
>           */
>          if (VIR_ALLOC(devcopylist) < 0)
>              goto endjob;
> -        if (virDomainDeviceDefListAddCopy(devcopylist, devlist->devs[0],
> -                                          vm->def, caps, driver->xmlopt) < 0)
> -            goto endjob;
> +        for (i = 0; i < devlist->count; i++)
> +            if (virDomainDeviceDefListAddCopy(devcopylist, devlist->devs[i],
> +                                              vm->def, caps, driver->xmlopt) < 0)
> +                goto endjob;
>      }
>
>      if (priv->qemuCaps)
>          qemuCaps = virObjectRef(priv->qemuCaps);
>      else if (!(qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, vm->def->emulator)))
> -        goto cleanup;
> +        goto endjob;
>
>      if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
>          /* Make a copy for updated domain. */
> @@ -7529,14 +7614,14 @@ static int qemuDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
>          if (!vmdef)
>              goto endjob;
>
> -        if ((ret = qemuDomainAttachDeviceConfig(qemuCaps, vmdef, devlist,
> -                                                dom->conn)) < 0)
> +        if ((ret = qemuDomainAttachDeviceConfig(qemuCaps, vmdef, devlist, dom->conn)) < 0)
>              goto endjob;
>      }
>
>      if (flags & VIR_DOMAIN_AFFECT_LIVE) {
>          if ((ret = qemuDomainAttachDeviceLive(vm, devcopylist, dom)) < 0)
>              goto endjob;
> +
>          /*
>           * update domain status forcibly because the domain status may be
>           * changed even if we failed to attach the device. For example,
> @@ -7595,6 +7680,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
>      qemuDomainObjPrivatePtr priv;
>      virQEMUDriverConfigPtr cfg = NULL;
>      virCapsPtr caps = NULL;
> +    bool multifunction = false;
> +    size_t i;
>
>      virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
>                    VIR_DOMAIN_AFFECT_CONFIG |
> @@ -7618,14 +7705,22 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
>      if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
>          goto cleanup;
>
> -    dev = virDomainDeviceDefParse(xml, vm->def,
> -                                             caps, driver->xmlopt,
> -                                             VIR_DOMAIN_DEF_PARSE_INACTIVE);
> -    if (!dev || VIR_ALLOC(devlist) < 0)
> -        goto endjob;
> +    multifunction = isMultifunctionDeviceXML(xml);
>
> -    if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0)
> -        goto endjob;
> +    if (multifunction) {
> +        if (!(devlist = virDomainDeviceDefParseXMLMany(xml, vm->def,
> +                                                       caps, driver->xmlopt,
> +                                                       VIR_DOMAIN_DEF_PARSE_INACTIVE)))
> +            goto endjob;
> +    } else {
> +        dev = virDomainDeviceDefParse(xml, vm->def, caps, driver->xmlopt,
> +                                      VIR_DOMAIN_DEF_PARSE_INACTIVE);
> +        if (!dev || VIR_ALLOC(devlist) < 0)
> +            goto endjob;
> +
> +        if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0)
> +            goto endjob;
> +    }
>
>      devcopylist = devlist;
>
> @@ -7640,9 +7735,10 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
>           */
>          if (VIR_ALLOC(devcopylist) < 0)
>              goto endjob;
> -        if (virDomainDeviceDefListAddCopy(devcopylist, devlist->devs[0],
> -                                          vm->def, caps, driver->xmlopt) < 0)
> -            goto endjob;
> +        for (i = 0; i < devlist->count; i++)
> +            if (virDomainDeviceDefListAddCopy(devcopylist, dev, vm->def,
> +                                              caps, driver->xmlopt) < 0)
> +                goto endjob;
>      }
>
>      if (priv->qemuCaps)
> @@ -7714,6 +7810,7 @@ static int qemuDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
>      qemuDomainObjPrivatePtr priv;
>      virQEMUDriverConfigPtr cfg = NULL;
>      virCapsPtr caps = NULL;
> +    bool multifunction = false;
>
>      virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
>                    VIR_DOMAIN_AFFECT_CONFIG, -1);
> @@ -7741,14 +7838,21 @@ static int qemuDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
>          !(flags & VIR_DOMAIN_AFFECT_LIVE))
>          parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE;
>
> -    dev = virDomainDeviceDefParse(xml, vm->def,
> -                                             caps, driver->xmlopt,
> -                                             parse_flags);
> -    if (!dev || VIR_ALLOC(devlist) < 0)
> -        goto endjob;
> +    multifunction = isMultifunctionDeviceXML(xml);
>
> -    if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0)
> -        goto endjob;
> +    if (multifunction) {
> +        if (!(devlist = virDomainDeviceDefParseXMLMany(xml, vm->def,
> +                                                   caps, driver->xmlopt,
> +                                                   parse_flags)))
> +            goto endjob;
> +    } else {
> +        dev = virDomainDeviceDefParse(xml, vm->def, caps, driver->xmlopt, parse_flags);
> +        if (!dev || VIR_ALLOC(devlist) < 0)
> +            goto endjob;
> +        if (!qemuDomainPCIAddressIsSingleFunctionAddr(dev) ||
> +            VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0)
> +            goto endjob;
> +    }
>
>      devcopylist = devlist;
>
> @@ -7811,7 +7915,7 @@ static int qemuDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
>      virDomainDefFree(vmdef);
>      if (devlist != devcopylist)
>          virDomainDeviceDefListFree(devcopylist);
> -    virDomainDeviceDefFree(dev);
> +    virDomainDeviceDefListFree(devlist);
>      virDomainObjEndAPI(&vm);
>      virObjectUnref(caps);
>      virObjectUnref(cfg);
> diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
> index adee545..35c1071 100644
> --- a/src/qemu/qemu_hotplug.c
> +++ b/src/qemu/qemu_hotplug.c
> @@ -2793,35 +2793,6 @@ qemuDomainChangeGraphics(virQEMUDriverPtr driver,
>      return ret;
>  }
>
> -
> -static int qemuComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED,
> -                                virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
> -                                virDomainDeviceInfoPtr info1,
> -                                void *opaque)
> -{
> -    virDomainDeviceInfoPtr info2 = opaque;
> -
> -    if (info1->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI ||
> -        info2->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
> -        return 0;
> -
> -    if (info1->addr.pci.domain == info2->addr.pci.domain &&
> -        info1->addr.pci.bus == info2->addr.pci.bus &&
> -        info1->addr.pci.slot == info2->addr.pci.slot &&
> -        info1->addr.pci.function != info2->addr.pci.function)
> -        return -1;
> -    return 0;
> -}
> -
> -static bool qemuIsMultiFunctionDevice(virDomainDefPtr def,
> -                                      virDomainDeviceInfoPtr dev)
> -{
> -    if (virDomainDeviceInfoIterate(def, qemuComparePCIDevice, dev) < 0)
> -        return true;
> -    return false;
> -}
> -
> -
>  static int
>  qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
>                             virDomainObjPtr vm,
> @@ -3430,13 +3401,6 @@ qemuDomainDetachVirtioDiskDevice(virQEMUDriverPtr driver,
>      int ret = -1;
>      qemuDomainObjPrivatePtr priv = vm->privateData;
>
> -    if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
> -        virReportError(VIR_ERR_OPERATION_FAILED,
> -                       _("cannot hot unplug multifunction PCI device: %s"),
> -                       detach->dst);
> -        goto cleanup;
> -    }
> -
>      if (qemuDomainMachineIsS390CCW(vm->def) &&
>          virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VIRTIO_CCW)) {
>          if (!virDomainDeviceAddressIsValid(&detach->info,
> @@ -3659,14 +3623,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver,
>          goto cleanup;
>      }
>
> -    if (detach->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
> -        qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
> -        virReportError(VIR_ERR_OPERATION_FAILED,
> -                       _("cannot hot unplug multifunction PCI device: %s"),
> -                       dev->data.disk->dst);
> -        goto cleanup;
> -    }
> -
>      if (qemuDomainControllerIsBusy(vm, detach)) {
>          virReportError(VIR_ERR_OPERATION_FAILED, "%s",
>                         _("device cannot be detached: device is busy"));
> @@ -3702,17 +3658,8 @@ qemuDomainDetachHostPCIDevice(virQEMUDriverPtr driver,
>                                virDomainHostdevDefPtr detach)
>  {
>      qemuDomainObjPrivatePtr priv = vm->privateData;
> -    virDomainHostdevSubsysPCIPtr pcisrc = &detach->source.subsys.u.pci;
>      int ret;
>
> -    if (qemuIsMultiFunctionDevice(vm->def, detach->info)) {
> -        virReportError(VIR_ERR_OPERATION_FAILED,
> -                       _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"),
> -                       pcisrc->addr.domain, pcisrc->addr.bus,
> -                       pcisrc->addr.slot, pcisrc->addr.function);
> -        return -1;
> -    }
> -
>      if (!virDomainDeviceAddressIsValid(detach->info,
>                                         VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
>          virReportError(VIR_ERR_OPERATION_FAILED,
> @@ -3944,13 +3891,6 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver,
>                              "%s", _("device cannot be detached without a PCI address"));
>              goto cleanup;
>          }
> -
> -        if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) {
> -            virReportError(VIR_ERR_OPERATION_FAILED,
> -                            _("cannot hot unplug multifunction PCI device :%s"),
> -                            dev->data.disk->dst);
> -            goto cleanup;
> -        }
>      }
>
>      if (!detach->info.alias) {
> @@ -4556,9 +4496,13 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
>                                           VIR_DOMAIN_DEVICE_ACTION_DETACH) < 0)
>              return -1;
>
> -    for (i = 0; i < devlist->count; i++)
> +    for (i = 0; i < devlist->count; i++) {
>          if (qemuDomainDetachDeviceLiveInternal(vm, devlist->devs[i], dom) < 0)
>              return -1;
> +        /* Detaching any one of the functions is sufficient to unplug */
> +        if (ARCH_IS_X86(vm->def->os.arch))
> +            break;
> +    }
>
>      return 0;
>  }
>
> --
> libvir-list mailing list
> libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list




More information about the libvir-list mailing list