[libvirt] [PATCH 4/4 v3] qemu_hostdev: Add support to install port profile and mac address on hostdevs
Laine Stump
laine at laine.org
Tue Mar 6 10:38:04 UTC 2012
On 03/05/2012 08:12 PM, Roopa Prabhu wrote:
> From: Roopa Prabhu <roprabhu at cisco.com>
>
> These changes are applied only if the hostdev has a parent net device.
> If the parent netdevice has virtual port information, the original virtualport
> associate functions are called (these set and restore both mac and port profile
> on an interface). Else, only mac address is set on the device
> using other methods depending on if its a sriov device or not.
>
> Changes also include hotplug pci devices
>
> Signed-off-by: Roopa Prabhu <roprabhu at cisco.com>
> ---
> src/qemu/qemu_hostdev.c | 241 +++++++++++++++++++++++++++++++++++++++++++++--
> src/qemu/qemu_hostdev.h | 8 +-
> src/qemu/qemu_hotplug.c | 10 ++
> 3 files changed, 246 insertions(+), 13 deletions(-)
>
>
> diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
> index b3cad8e..ebcdc52 100644
> --- a/src/qemu/qemu_hostdev.c
> +++ b/src/qemu/qemu_hostdev.c
> @@ -29,6 +29,13 @@
> #include "memory.h"
> #include "pci.h"
> #include "hostusb.h"
> +#include "virnetdev.h"
> +
> +VIR_ENUM_IMPL(virNetDevVPort, VIR_NETDEV_VPORT_PROFILE_LAST,
> + "none",
> + "802.1Qbg",
> + "802.1Qbh",
> + "openvswitch")
>
> static pciDeviceList *
> qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs)
> @@ -156,19 +163,192 @@ int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
> return 0;
> }
>
> +static int
> +qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
> +{
> + struct pci_config_address config_address;
> +
> + config_address.domain = hostdev->source.subsys.u.pci.domain;
> + config_address.bus = hostdev->source.subsys.u.pci.bus;
> + config_address.slot = hostdev->source.subsys.u.pci.slot;
> + config_address.function = hostdev->source.subsys.u.pci.function;
> +
> + return pciConfigAddressToSysfsFile(&config_address, sysfs_path);
> +}
> +
> +int
> +qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev)
> +{
> + char *sysfs_path = NULL;
> + int ret = -1;
> +
> + if (qemuDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
> + return ret;
> +
> + ret = pciDeviceIsVirtualFunction(sysfs_path);
> +
> + VIR_FREE(sysfs_path);
> +
> + return ret;
> +}
> +
> +static int
> +qemuDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev,
> + int *vf)
> +{
> + int ret = -1;
> + char *sysfs_path = NULL;
> +
> + if (qemuDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
> + return ret;
> +
> + if (pciDeviceIsVirtualFunction(sysfs_path) == 1) {
> + if (pciDeviceGetVirtualFunctionInfo(sysfs_path, linkdev,
> + vf) < 0)
> + goto cleanup;
> + } else {
> + if (pciDeviceNetName(sysfs_path, linkdev) < 0)
> + goto cleanup;
> + *vf = -1;
> + }
> +
> + ret = 0;
> +
> +cleanup:
> + VIR_FREE(sysfs_path);
> +
> + return ret;
> +}
> +
> +static int
> +qemuDomainHostdevNetConfigVirtPortProfile(const char *linkdev, int vf,
> + virNetDevVPortProfilePtr virtPort,
> + const unsigned char *macaddr,
> + const unsigned char *uuid,
> + int associate)
> +{
> + int ret = -1;
> +
> + if (!virtPort)
> + return ret;
> +
> + switch(virtPort->virtPortType) {
> + case VIR_NETDEV_VPORT_PROFILE_NONE:
> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:
> + case VIR_NETDEV_VPORT_PROFILE_LAST:
> + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("virtualport type %s is "
> + "currently not supported on interfaces of type "
> + "hostdev"),
> + virNetDevVPortTypeToString(virtPort->virtPortType));
> + break;
> +
> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:
> + if (associate)
> + ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr,
> + linkdev, vf, uuid,
> + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false);
> + else
> + ret = virNetDevVPortProfileDisassociate(NULL, virtPort,
> + macaddr, linkdev, vf,
> + VIR_NETDEV_VPORT_PROFILE_OP_DESTROY);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +int
> +qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
> + const unsigned char *uuid,
> + char *stateDir)
> +{
> + char *linkdev = NULL;
> + virNetDevVPortProfilePtr virtPort;
> + int ret = -1;
> + int vf = -1;
> + int vlanid = -1;
> + int port_profile_associate = 1;
> + int isvf;
> +
> + isvf = qemuDomainHostdevIsVirtualFunction(hostdev);
> + if (isvf <= 0) {
> + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("Interface type hostdev is currently supported on"
> + " sriov Virtual functions only"));
I changed the capitalization a bit here: "SR-IOV Virtual Functions".
> + return ret;
> + }
> +
> + if (qemuDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
> + return ret;
> +
> + virtPort = virDomainNetGetActualVirtPortProfile(
> + hostdev->parent.data.net);
> + if (virtPort)
> + ret = qemuDomainHostdevNetConfigVirtPortProfile(linkdev, vf,
> + virtPort, hostdev->parent.data.net->mac, uuid,
> + port_profile_associate);
> + else
> + /* Set only mac */
> + ret = virNetDevReplaceNetConfig(linkdev, vf,
> + hostdev->parent.data.net->mac, vlanid,
> + stateDir);
> + VIR_FREE(linkdev);
> +
> + return ret;
> +}
> +
> +int
> +qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
> + char *stateDir)
> +{
> + char *linkdev = NULL;
> + virNetDevVPortProfilePtr virtPort;
> + int ret = -1;
> + int vf = -1;
> + int port_profile_associate = 0;
> + int isvf;
> +
> + isvf = qemuDomainHostdevIsVirtualFunction(hostdev);
> + if (isvf <= 0) {
> + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("Interface type hostdev is currently supported on"
> + " sriov Virtual functions only"));
ditto.
> + return ret;
> + }
> +
> + if (qemuDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
> + return ret;
> +
> + virtPort = virDomainNetGetActualVirtPortProfile(
> + hostdev->parent.data.net);
> + if (virtPort)
> + ret = qemuDomainHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort,
> + hostdev->parent.data.net->mac, NULL,
> + port_profile_associate);
> + else
> + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir);
> +
> + VIR_FREE(linkdev);
> +
> + return ret;
> +}
> +
> int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
> const char *name,
> + const unsigned char *uuid,
> virDomainHostdevDefPtr *hostdevs,
> int nhostdevs)
> {
> pciDeviceList *pcidevs;
> + int last_processed_hostdev_vf = -1;
> int i;
> int ret = -1;
>
> if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs)))
> return -1;
>
> - /* We have to use 7 loops here. *All* devices must
> + /* We have to use 9 loops here. *All* devices must
> * be detached before we reset any of them, because
> * in some cases you have to reset the whole PCI,
> * which impacts all devices on it. Also, all devices
> @@ -225,7 +405,22 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
> goto reattachdevs;
> }
>
> - /* Loop 4: Now mark all the devices as active */
> + /* Loop 4: For SRIOV network devices, Now that we have detached the
> + * the network device, set the netdev config */
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> + if (hostdev->parent.data.net)
While this currently will work, I had figured that (to allow for the
possibility of other smart hostdev types) we should check this way:
if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&
hostdev->parent.data.net)
I'm squashing that in to the three places you check for this.
> + if (qemuDomainHostdevNetConfigReplace(hostdev, uuid,
> + driver->stateDir) < 0)
> + goto resetvfnetconfig;
> + last_processed_hostdev_vf = i;
> + }
> +
> + /* Loop 5: Now mark all the devices as active */
> for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
> pciDevice *dev = pciDeviceListGet(pcidevs, i);
> if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) {
> @@ -234,13 +429,13 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
> }
> }
>
> - /* Loop 5: Now remove the devices from inactive list. */
> + /* Loop 6: Now remove the devices from inactive list. */
> for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
> pciDevice *dev = pciDeviceListGet(pcidevs, i);
> pciDeviceListDel(driver->inactivePciHostdevs, dev);
> }
>
> - /* Loop 6: Now set the used_by_domain of the device in
> + /* Loop 7: Now set the used_by_domain of the device in
> * driver->activePciHostdevs as domain name.
> */
> for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
> @@ -252,7 +447,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
> pciDeviceSetUsedBy(activeDev, name);
> }
>
> - /* Loop 7: Now set the original states for hostdev def */
> + /* Loop 8: Now set the original states for hostdev def */
> for (i = 0; i < nhostdevs; i++) {
> pciDevice *dev;
> pciDevice *pcidev;
> @@ -284,7 +479,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
> pciFreeDevice(dev);
> }
>
> - /* Loop 8: Now steal all the devices from pcidevs */
> + /* Loop 9: Now steal all the devices from pcidevs */
> while (pciDeviceListCount(pcidevs) > 0) {
> pciDevice *dev = pciDeviceListGet(pcidevs, 0);
> pciDeviceListSteal(pcidevs, dev);
> @@ -302,6 +497,13 @@ inactivedevs:
> pciDeviceListSteal(driver->activePciHostdevs, dev);
> }
>
> +resetvfnetconfig:
> + for (i = 0; i < last_processed_hostdev_vf; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + if (hostdev->parent.data.net)
> + qemuDomainHostdevNetConfigRestore(hostdev, driver->stateDir);
> + }
> +
> reattachdevs:
> for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
> pciDevice *dev = pciDeviceListGet(pcidevs, i);
> @@ -317,10 +519,10 @@ static int
> qemuPrepareHostPCIDevices(struct qemud_driver *driver,
> virDomainDefPtr def)
> {
> - return qemuPrepareHostdevPCIDevices(driver, def->name, def->hostdevs, def->nhostdevs);
> + return qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid,
> + def->hostdevs, def->nhostdevs);
> }
>
> -
> int
> qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
> const char *name,
> @@ -494,8 +696,10 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
> return;
> }
>
> - /* Again 3 loops; mark all devices as inactive before reset
> - * them and reset all the devices before re-attach */
> + /* Again 4 loops; mark all devices as inactive before reset
> + * them and reset all the devices before re-attach.
> + * Attach mac and port profile parameters to devices
> + */
> for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
> pciDevice *dev = pciDeviceListGet(pcidevs, i);
> pciDevice *activeDev = NULL;
> @@ -514,6 +718,20 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
> pciDeviceListSteal(driver->activePciHostdevs, dev);
> }
>
> + /*
> + * For SRIOV net host devices, unset mac and port profile before
> + * reset and reattach device
> + */
> + for (i = 0; i < nhostdevs; i++) {
> + virDomainHostdevDefPtr hostdev = hostdevs[i];
> + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
> + continue;
> + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
> + continue;
> + if (hostdev->parent.data.net)
> + qemuDomainHostdevNetConfigRestore(hostdev, driver->stateDir);
> + }
> +
> for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
> pciDevice *dev = pciDeviceListGet(pcidevs, i);
> if (pciResetDevice(dev, driver->activePciHostdevs,
> @@ -540,5 +758,6 @@ void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
> if (!def->nhostdevs)
> return;
>
> - qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs, def->nhostdevs);
> + qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs,
> + def->nhostdevs);
> }
> diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h
> index d852f5b..173e4f4 100644
> --- a/src/qemu/qemu_hostdev.h
> +++ b/src/qemu/qemu_hostdev.h
> @@ -31,6 +31,7 @@ int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
> virDomainDefPtr def);
> int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
> const char *name,
> + const unsigned char *uuid,
> virDomainHostdevDefPtr *hostdevs,
> int nhostdevs);
> int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
> @@ -46,6 +47,11 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
> int nhostdevs);
> void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
> virDomainDefPtr def);
> -
> +int qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev);
> +int qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
> + const unsigned char *uuid,
> + char *stateDir);
> +int qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
> + char *stateDir);
>
> #endif /* __QEMU_HOSTDEV_H__ */
> diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
> index 50563c5..c5ed9f7 100644
> --- a/src/qemu/qemu_hotplug.c
> +++ b/src/qemu/qemu_hotplug.c
> @@ -927,7 +927,8 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver,
> return -1;
> }
>
> - if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, &hostdev, 1) < 0)
> + if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid,
> + &hostdev, 1) < 0)
> return -1;
>
> if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
> @@ -1886,6 +1887,13 @@ qemuDomainDetachHostPciDevice(struct qemud_driver *driver,
> if (ret < 0)
> return -1;
>
> + /*
> + * For SRIOV net host devices, unset mac and port profile before
> + * reset and reattach device
> + */
> + if (detach->parent.data.net)
> + qemuDomainHostdevNetConfigRestore(detach, driver->stateDir);
> +
> pci = pciGetDevice(subsys->u.pci.domain, subsys->u.pci.bus,
> subsys->u.pci.slot, subsys->u.pci.function);
> if (pci) {
>
>
It turned out just like I'd hoped it would!
ACK with those small nits (which I'm applying before I push).
More information about the libvir-list
mailing list