[libvirt] [PATCH 4/5] Forward Mode Hostdev Implementation

Shradha Shah sshah at solarflare.com
Fri Jun 8 15:29:47 UTC 2012


This patch chooses a free network device from the interface pool and creates a PCI HostDef
to be passed to the guest, when forward mode is "hostdev".

networkNotifyActualDevice and networkReleaseActualDevice are modified accordingly.

Signed-off-by: Shradha Shah <sshah at solarflare.com>
---
 src/libvirt_private.syms    |    3 +
 src/network/bridge_driver.c |  180 +++++++++++++++++++++++++++++++++++++------
 src/qemu/qemu_command.c     |   14 ++++
 src/util/pci.c              |    2 +-
 src/util/pci.h              |    3 +
 src/util/virnetdev.c        |  128 ++++++++++++++++++++++++++++++
 src/util/virnetdev.h        |   19 +++++
 7 files changed, 325 insertions(+), 24 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index afb308d..5b8ab0b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1261,6 +1261,9 @@ virNetDevGetVLanID;
 virNetDevGetVirtualFunctionIndex;
 virNetDevGetVirtualFunctionInfo;
 virNetDevGetVirtualFunctions;
+virNetDevParsePciConfigAddress;
+virNetDevGetDeviceAddrString;
+virNetDevGetPciAddrFromName;
 virNetDevIsOnline;
 virNetDevIsVirtualFunction;
 virNetDevLinkDump;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index cc53551..691ab07 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2835,6 +2835,8 @@ networkAllocateActualDevice(virDomainNetDefPtr iface)
     virNetworkObjPtr network;
     virNetworkDefPtr netdef;
     virPortGroupDefPtr portgroup;
+    virNetDevVPortProfilePtr virtport = NULL;
+    virNetworkForwardIfDefPtr dev = NULL;
     int ii;
     int ret = -1;
 
@@ -2906,12 +2908,95 @@ networkAllocateActualDevice(virDomainNetDefPtr iface)
             virReportOOMError();
             goto cleanup;
         }
-
+    } else if (netdef->forwardType == VIR_NETWORK_FORWARD_HOSTDEV) {
+        int rc = -1;
+        if (!iface->data.network.actual
+            && (VIR_ALLOC(iface->data.network.actual) < 0)) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_HOSTDEV;
+        if ((netdef->nForwardPfs > 0) && (netdef->nForwardIfs <= 0)) {
+            if((rc = networkCreateInterfacePool(netdef)) < 0) {
+                networkReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Could not create Interface Pool from PF"));
+                goto cleanup;
+            }
+        }
+        /* pick first dev with 0 usageCount */
+        
+        for (ii = 0; ii < netdef->nForwardIfs; ii++) {
+            if (netdef->forwardIfs[ii].usageCount == 0) {
+                dev = &netdef->forwardIfs[ii];
+                break;
+            }
+        }
+        if (!dev) {
+            networkReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("network '%s' requires exclusive access to interfaces, but none are available"),
+                               netdef->name);
+            goto cleanup;
+        }
+        
+        iface->data.network.actual->data.hostdev.def.parent.type = VIR_DOMAIN_DEVICE_NET;
+        iface->data.network.actual->data.hostdev.def.parent.data.net = iface;
+        iface->data.network.actual->data.hostdev.def.info = &iface->info;
+        iface->data.network.actual->data.hostdev.def.mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
+        iface->data.network.actual->data.hostdev.def.managed = 1;
+        iface->data.network.actual->data.hostdev.def.source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
+        
+        if (dev->isPciAddr == true) {
+            virDomainDevicePCIAddressPtr addr = &iface->data.network.actual->data.hostdev.def.source.subsys.u.pci;
+            if (virNetDevParsePciConfigAddress(dev->dev,
+                                               &addr->domain,
+                                               &addr->bus,
+                                               &addr->slot,
+                                               &addr->function) < 0) {
+                goto cleanup;
+            }  
+        }
+        else if (dev->isPciAddr == false) {
+            virDomainDevicePCIAddressPtr addr = &iface->data.network.actual->data.hostdev.def.source.subsys.u.pci;
+            char *device_pci_addr = NULL;
+            if (virNetDevGetPciAddrFromName(dev->dev,
+                                            &device_pci_addr) < 0) {
+                goto cleanup;
+            }
+            if (virNetDevParsePciConfigAddress(device_pci_addr,
+                                               &addr->domain,
+                                               &addr->bus,
+                                               &addr->slot,
+                                               &addr->function) < 0) {
+                goto cleanup;
+            } 
+            VIR_FREE(device_pci_addr);
+        }
+        dev->usageCount++;
+        VIR_DEBUG("Using physical device %s, usageCount %d",
+                  dev->dev, dev->usageCount);
+        
+        if (iface->data.network.virtPortProfile) {
+            virtport = iface->data.network.virtPortProfile;
+        } else {
+            if (portgroup)
+                virtport = portgroup->virtPortProfile;
+            else
+                virtport = netdef->virtPortProfile;
+        }
+        if (virtport) {
+            if (VIR_ALLOC(iface->data.network.actual->data.hostdev.virtPortProfile) < 0) {
+                virReportOOMError();
+                goto cleanup;
+            }
+            /* There are no pointers in a virtualPortProfile, so a shallow copy
+             * is sufficient
+             */
+            *iface->data.network.actual->data.direct.virtPortProfile = *virtport;
+        }
     } else if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) ||
                (netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) ||
                (netdef->forwardType == VIR_NETWORK_FORWARD_VEPA) ||
                (netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH)) {
-        virNetDevVPortProfilePtr virtport = NULL;
         int rc = -1;
         /* <forward type='bridge|private|vepa|passthrough'> are all
          * VIR_DOMAIN_NET_TYPE_DIRECT.
@@ -2969,7 +3054,6 @@ networkAllocateActualDevice(virDomainNetDefPtr iface)
                                netdef->name);
             goto cleanup;
         } else {
-            virNetworkForwardIfDefPtr dev = NULL;
 
             /* pick an interface from the pool */
 
@@ -3070,14 +3154,16 @@ networkNotifyActualDevice(virDomainNetDefPtr iface)
     struct network_driver *driver = driverState;
     virNetworkObjPtr network;
     virNetworkDefPtr netdef;
-    const char *actualDev;
+    virDomainHostdevDefPtr def = NULL;
+    const char *actualDev = NULL;
     int ret = -1;
 
     if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
        return 0;
 
     if (!iface->data.network.actual ||
-        (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) {
+        ((virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT) &&
+         (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_HOSTDEV))) {
         VIR_DEBUG("Nothing to claim from network %s", iface->data.network.name);
         return 0;
     }
@@ -3092,23 +3178,45 @@ networkNotifyActualDevice(virDomainNetDefPtr iface)
         goto cleanup;
     }
 
-    actualDev = virDomainNetGetActualDirectDev(iface);
-    if (!actualDev) {
-        networkReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("the interface uses a direct mode, but has no source dev"));
-        goto cleanup;
+    if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_DIRECT) {
+        actualDev = virDomainNetGetActualDirectDev(iface);
+        if (!actualDev) {
+            networkReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("the interface uses a direct mode, but has no source dev"));
+            goto cleanup;
+        }
+    }
+    
+    if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+        def = virDomainNetGetActualHostdev(iface);
+        if (!def) {
+            networkReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("the interface uses a hostdev mode, but has no hostdev"));
+            goto cleanup;
+        }
     }
-
     netdef = network->def;
     if (netdef->nForwardIfs == 0) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("network '%s' uses a direct mode, but has no forward dev and no interface pool"),
+                           _("network '%s' uses a direct/hostdev mode, but has no forward dev and no interface pool"),
                            netdef->name);
         goto cleanup;
     } else {
         int ii;
         virNetworkForwardIfDefPtr dev = NULL;
-
+        
+        virDomainDevicePCIAddressPtr addr = &(def->source.subsys.u.pci);
+        if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+            if (virNetDevGetDeviceAddrString(addr->domain,
+                                             addr->bus,
+                                             addr->slot,
+                                             addr->function,
+                                             &actualDev) < 0) {
+                networkReportError(VIR_ERR_INTERNAL_ERROR,
+                                   "%s", _("Could not get device string for hostdev"));
+                goto cleanup;
+            }
+        }
         /* find the matching interface in the pool and increment its usageCount */
 
         for (ii = 0; ii < netdef->nForwardIfs; ii++) {
@@ -3125,12 +3233,12 @@ networkNotifyActualDevice(virDomainNetDefPtr iface)
             goto cleanup;
         }
 
-        /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both require
-         * exclusive access to a device, so current usageCount must be
+        /* PASSTHROUGH mode, HOSTDEV mode and PRIVATE Mode + 802.1Qbh both               * require exclusive access to a device, so current usageCount must be
          * 0 in those cases.
          */
         if ((dev->usageCount > 0) &&
             ((netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH) ||
+             (netdef->forwardType == VIR_NETWORK_FORWARD_HOSTDEV) || 
              ((netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) &&
               iface->data.network.actual->data.direct.virtPortProfile &&
               (iface->data.network.actual->data.direct.virtPortProfile->virtPortType
@@ -3170,14 +3278,16 @@ networkReleaseActualDevice(virDomainNetDefPtr iface)
     struct network_driver *driver = driverState;
     virNetworkObjPtr network = NULL;
     virNetworkDefPtr netdef;
-    const char *actualDev;
+    virDomainHostdevDefPtr def = NULL;
+    const char *actualDev = NULL;
     int ret = -1;
 
     if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
        return 0;
 
     if (!iface->data.network.actual ||
-        (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) {
+        ((virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT) &&
+         (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_HOSTDEV))) {
         VIR_DEBUG("Nothing to release to network %s", iface->data.network.name);
         ret = 0;
         goto cleanup;
@@ -3193,23 +3303,47 @@ networkReleaseActualDevice(virDomainNetDefPtr iface)
         goto cleanup;
     }
 
-    actualDev = virDomainNetGetActualDirectDev(iface);
-    if (!actualDev) {
-        networkReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("the interface uses a direct mode, but has no source dev"));
-        goto cleanup;
+    if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_DIRECT) {
+        actualDev = virDomainNetGetActualDirectDev(iface);
+        if (!actualDev) {
+            networkReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("the interface uses a direct mode, but has no source dev"));
+            goto cleanup;
+        }
+    }
+    
+    if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+        def = virDomainNetGetActualHostdev(iface);
+        if (!def) {
+            networkReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s", _("the interface uses a hostdev mode, but has no hostdev"));
+            goto cleanup;
+        }
     }
 
     netdef = network->def;
     if (netdef->nForwardIfs == 0) {
         networkReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("network '%s' uses a direct mode, but has no forward dev and no interface pool"),
+                           _("network '%s' uses a direct/hostdev mode, but has no forward dev and no interface pool"),
                            netdef->name);
         goto cleanup;
     } else {
         int ii;
         virNetworkForwardIfDefPtr dev = NULL;
 
+        virDomainDevicePCIAddressPtr addr = &(def->source.subsys.u.pci);
+        if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+            if (virNetDevGetDeviceAddrString(addr->domain,
+                                             addr->bus,
+                                             addr->slot,
+                                             addr->function,
+                                             &actualDev) < 0) {
+                networkReportError(VIR_ERR_INTERNAL_ERROR,
+                                   "%s", _("Could not get device string for hostdev"));
+                goto cleanup;
+            }
+        }
+
         for (ii = 0; ii < netdef->nForwardIfs; ii++) {
             if (STREQ(actualDev, netdef->forwardIfs[ii].dev)) {
                 dev = &netdef->forwardIfs[ii];
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 117542f..ea1c9c5 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -4897,6 +4897,20 @@ qemuBuildCommandLine(virConnectPtr conn,
                  * code here that adds the newly minted hostdev to the
                  * hostdevs array).
                  */
+                if (qemuAssignDeviceHostdevAlias(def,
+                                                 virDomainNetGetActualHostdev(net),
+                                                 (def->nhostdevs-1)) < 0) {
+                    qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                    _("Could not assign alias to Net Hostdev"));
+                    goto error;
+                }
+                
+                if (virDomainHostdevInsert(def, 
+                                           virDomainNetGetActualHostdev(net)) < 0) {
+                    qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                    _("Hostdev not inserted into the array"));
+                    goto error;
+                }
                 continue;
             }
 
diff --git a/src/util/pci.c b/src/util/pci.c
index 88e4ac5..6dd9953 100644
--- a/src/util/pci.c
+++ b/src/util/pci.c
@@ -1812,7 +1812,7 @@ logStrToLong_ui(char const *s,
     return ret;
 }
 
-static int
+int
 pciParsePciConfigAddress(char *address,
                          struct pci_config_address *bdf)
 {
diff --git a/src/util/pci.h b/src/util/pci.h
index b71bb12..8d85558 100644
--- a/src/util/pci.h
+++ b/src/util/pci.h
@@ -105,6 +105,9 @@ int pciGetVirtualFunctions(const char *sysfs_path,
                            struct pci_config_address ***virtual_functions,
                            unsigned int *num_virtual_functions);
 
+int pciParsePciConfigAddress(char *address,
+                             struct pci_config_address *bdf);
+
 int pciDeviceIsVirtualFunction(const char *vf_sysfs_device_link);
 
 int pciGetVirtualFunctionIndex(const char *pf_sysfs_device_link,
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index a328294..dad6035 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -38,6 +38,7 @@
 
 #ifdef __linux__
 # include <linux/sockios.h>
+# include <linux/ethtool.h>
 # include <linux/if_vlan.h>
 #elif !defined(AF_PACKET)
 # undef HAVE_STRUCT_IFREQ
@@ -463,6 +464,65 @@ int virNetDevSetNamespace(const char *ifname, pid_t pidInNs)
     return rc;
 }
 
+#if defined(SIOCETHTOOL) && defined(HAVE_STRUCT_IFREQ)
+/**
+ * virNetDevGetPciAddrFromName:
+ * @device_pci_addr : string containing the BDF of the device
+ * @domain : pointer to the integer domain
+ * @bus : pointer to the integer bus
+ * @slot : pointer to integer slot
+ * @function: pointer to integer function
+ *
+ * Parses the PCI config address string.
+ *
+ * Returns 0 if success and -1 if error
+ *
+ */
+int
+virNetDevGetPciAddrFromName(const char *ifname,
+                            char **device_pci_addr)
+{
+    int ret = -1;
+    int fd = -1;
+    struct ifreq ifr;
+    struct ethtool_drvinfo drvinfo;
+
+    memset(&drvinfo, 0, sizeof(drvinfo));
+    
+    drvinfo.cmd = ETHTOOL_GDRVINFO;
+    
+    if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0)
+        return -1;
+
+     ifr.ifr_ifru.ifru_data = (void *)&drvinfo;
+    
+    if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
+        virReportSystemError(errno,
+                             _("Cannot get driver info on '%s'"),
+                             ifname);
+       goto error;
+    }
+
+    if (virAsprintf(device_pci_addr, "%s", drvinfo.bus_info) < 0) {
+        virReportOOMError();
+        goto error;
+    }
+    
+    ret = 0;
+error:
+    return ret;
+}
+#else
+int virNetDevGetPciAddrFromName(const char* ifname ATTRIBUTE_UNUSED,
+                                char **device_pci_addr ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(errno,
+                         _("Cannot get driver info on '%s'"),
+                         ifname);
+    return -1;
+}
+#endif
+
 #if defined(SIOCSIFNAME) && defined(HAVE_STRUCT_IFREQ)
 /**
  * virNetDevSetName:
@@ -1040,6 +1100,74 @@ cleanup:
 }
 
 /**
+ * virNetDevGetDeviceAddrString:
+ * @domain : pointer to the integer domain
+ * @bus : pointer to the integer bus
+ * @slot : pointer to integer slot
+ * @function: pointer to integer function
+ * @addr: pointer to the memory allocated to hold the BDF string
+ *
+ * Creates the PCI config address string.
+ *
+ * Returns 0 if success and -1 if error
+ *
+ */
+int
+virNetDevGetDeviceAddrString(unsigned domain,
+                             unsigned bus,
+                             unsigned slot,
+                             unsigned function,
+                             const char **addr)
+{
+    int ret = -1;
+
+    if (pciGetDeviceAddrString(domain, bus, slot, function, (char **)addr) < 0) 
+        goto error;
+    
+    ret = 0;
+
+error:
+    return ret;
+}
+
+/**
+ * virNetDevParsePciConfigAddress:
+ * @device_pci_addr : string containing the BDF of the device
+ * @domain : pointer to the integer domain
+ * @bus : pointer to the integer bus
+ * @slot : pointer to integer slot
+ * @function: pointer to integer function
+ *
+ * Parses the PCI config address string.
+ *
+ * Returns 0 if success and -1 if error
+ *
+ */
+int
+virNetDevParsePciConfigAddress(const char *device_pci_addr,
+                               unsigned int *domain,
+                               unsigned int *bus,
+                               unsigned int *slot,
+                               unsigned int *function)
+{
+    int ret = -1;
+    struct pci_config_address device;
+    
+    if (pciParsePciConfigAddress((char *)device_pci_addr, &device) != 0) {
+        goto error;
+    }
+    
+    *domain = device.domain;
+    *bus = device.bus;
+    *slot = device.slot;
+    *function = device.function;
+    
+    ret = 0;
+error:
+    return ret;
+}
+
+/**
  * virNetDevIsVirtualFunction:
  * @ifname : name of the interface
  *
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 660d2db..4248a85 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -106,6 +106,25 @@ int virNetDevGetVirtualFunctions(const char *pfname,
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
     ATTRIBUTE_RETURN_CHECK;
 
+int virNetDevGetDeviceAddrString(unsigned domain,
+                                 unsigned bus,
+                                 unsigned slot,
+                                 unsigned function,
+                                 const char **addr)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
+    ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK;
+
+int virNetDevParsePciConfigAddress(const char *device_pci_addr,
+                                   unsigned int *domain,
+                                   unsigned int *bus,
+                                   unsigned int *slot,
+                                   unsigned int *function)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+
+int virNetDevGetPciAddrFromName(const char *ifname,
+                                char **device_pci_addr)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; 
+
 int virNetDevLinkDump(const char *ifname, int ifindex,
                       struct nlattr **tb,
                       unsigned char **recvbuf,
-- 
1.7.4.4





More information about the libvir-list mailing list