]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: support device stats collection for <interface type='hostdev'>
authorzhenwei pi <pizhenwei@bytedance.com>
Thu, 15 Oct 2020 11:21:14 +0000 (19:21 +0800)
committerLaine Stump <laine@redhat.com>
Tue, 20 Oct 2020 21:29:29 +0000 (17:29 -0400)
libvirt can retrieve traffic stats for emulated interfaces that are
backed by tap or macvtap devices, but this information wasn't
available for hostdev interfaces (those that are implemented by
assigning an SR-IOV VF device to a guest using vfio):

  #virsh domifstat instance --interface=52:54:00:2d:b2:35
  error: Failed to get interface stats instance 52:54:00:2d:b2:35
  error: internal error: Interface name not provided

For some SR-IOV VF devices this information is available via the
netlink VFINFO_LIST request/response, and that is what this patch uses
to implement stats retrieval for VF. Not that this is dependent on
support in the PF driver - for example, the Mellanox ConnectX-4 Lx
(mlx5) driver reports usable stats, while Intel 82599 (ixgbe) and
82576 (igb) just report all stats as 0.  (this is the same result as
"ip -s link show").

Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Reviewed-by: Laine Stump <laine@redhat.com>
src/libvirt_private.syms
src/qemu/qemu_driver.c
src/util/virnetdev.c
src/util/virnetdev.h

index 52e9c6313fb6f036ab07f664b8a1102382cb010a..ae543589f13383f0b09a79397bbfc5fc45b28e2f 100644 (file)
@@ -2584,6 +2584,7 @@ virNetDevSetRootQDisc;
 virNetDevSetupControl;
 virNetDevSysfsFile;
 virNetDevValidateConfig;
+virNetDevVFInterfaceStats;
 
 
 # util/virnetdevbandwidth.h
index bb4a46be986b915bb32ef798445e5e0d161b2510..f547abd25998f0d99d6bc4ecff3e3c555e078c65 100644 (file)
@@ -10191,6 +10191,20 @@ qemuDomainInterfaceStats(virDomainPtr dom,
     if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
         if (virNetDevOpenvswitchInterfaceStats(net->ifname, stats) < 0)
             goto cleanup;
+    } else if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+        virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net);
+        virPCIDeviceAddressPtr vfAddr;
+
+        if (!hostdev) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("hostdev interface missing hostdev data"));
+            goto cleanup;
+        }
+
+        vfAddr = &hostdev->source.subsys.u.pci.addr;
+        if (virNetDevVFInterfaceStats(vfAddr, stats) < 0)
+            goto cleanup;
+
     } else {
         if (virNetDevTapInterfaceStats(net->ifname, stats,
                                        !virDomainNetTypeSharesHostView(net)) < 0)
index 5c7660dab4f96326b05bbf4740c9f976a928e48b..f53e1751b340b2618957351fe76da202fbec15b6 100644 (file)
@@ -1514,6 +1514,17 @@ static struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
                             .maxlen = sizeof(struct ifla_vf_mac) },
     [IFLA_VF_VLAN]      = { .type = NLA_UNSPEC,
                             .maxlen = sizeof(struct ifla_vf_vlan) },
+    [IFLA_VF_STATS]     = { .type = NLA_NESTED },
+};
+
+
+static struct nla_policy ifla_vfstats_policy[IFLA_VF_STATS_MAX+1] = {
+    [IFLA_VF_STATS_RX_PACKETS]  = { .type = NLA_U64 },
+    [IFLA_VF_STATS_TX_PACKETS]  = { .type = NLA_U64 },
+    [IFLA_VF_STATS_RX_BYTES]    = { .type = NLA_U64 },
+    [IFLA_VF_STATS_TX_BYTES]    = { .type = NLA_U64 },
+    [IFLA_VF_STATS_BROADCAST]   = { .type = NLA_U64 },
+    [IFLA_VF_STATS_MULTICAST]   = { .type = NLA_U64 },
 };
 
 
@@ -1651,13 +1662,14 @@ virNetDevSetVfConfig(const char *ifname, int vf,
 
 static int
 virNetDevParseVfConfig(struct nlattr **tb, int32_t vf, virMacAddrPtr mac,
-                       int *vlanid)
+                       int *vlanid, virDomainInterfaceStatsPtr stats)
 {
     int rc = -1;
     struct ifla_vf_mac *vf_mac;
     struct ifla_vf_vlan *vf_vlan;
     struct nlattr *tb_vf_info = {NULL, };
     struct nlattr *tb_vf[IFLA_VF_MAX+1];
+    struct nlattr *tb_vf_stats[IFLA_VF_STATS_MAX+1];
     int rem;
 
     if (!tb[IFLA_VFINFO_LIST]) {
@@ -1693,6 +1705,26 @@ virNetDevParseVfConfig(struct nlattr **tb, int32_t vf, virMacAddrPtr mac,
             }
         }
 
+        if (stats && tb_vf[IFLA_VF_STATS] && tb_vf[IFLA_VF_MAC]) {
+            vf_mac = RTA_DATA(tb_vf[IFLA_VF_MAC]);
+            if (vf_mac && vf_mac->vf == vf)  {
+                rc = nla_parse_nested(tb_vf_stats, IFLA_VF_STATS_MAX,
+                                      tb_vf[IFLA_VF_STATS],
+                                      ifla_vfstats_policy);
+                if (rc < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("error parsing IFLA_VF_STATS"));
+                     return rc;
+                }
+
+                stats->rx_bytes = nla_get_u64(tb_vf_stats[IFLA_VF_STATS_RX_BYTES]);
+                stats->tx_bytes = nla_get_u64(tb_vf_stats[IFLA_VF_STATS_TX_BYTES]);
+                stats->rx_packets = nla_get_u64(tb_vf_stats[IFLA_VF_STATS_RX_PACKETS]);
+                stats->tx_packets = nla_get_u64(tb_vf_stats[IFLA_VF_STATS_TX_PACKETS]);
+                rc = 0;
+            }
+        }
+
         if (rc == 0)
             break;
     }
@@ -1714,7 +1746,43 @@ virNetDevGetVfConfig(const char *ifname, int vf, virMacAddrPtr mac,
     if (virNetlinkDumpLink(ifname, ifindex, &nlData, tb, 0, 0) < 0)
         return -1;
 
-    return virNetDevParseVfConfig(tb, vf, mac, vlanid);
+    return virNetDevParseVfConfig(tb, vf, mac, vlanid, NULL);
+}
+
+
+/**
+ * virNetDevVFInterfaceStats:
+ * @vfAddr: PCI address of a VF
+ * @stats: returns stats of the VF interface
+ *
+ * Get the VF interface from kernel by netlink.
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virNetDevVFInterfaceStats(virPCIDeviceAddressPtr vfAddr,
+                          virDomainInterfaceStatsPtr stats)
+{
+    g_autofree void *nlData = NULL;
+    struct nlattr *tb[IFLA_MAX + 1] = {NULL, };
+    g_autofree char *vfSysfsPath = NULL;
+    g_autofree char *pfname = NULL;
+    int vf = -1;
+
+    if (virPCIDeviceAddressGetSysfsFile(vfAddr, &vfSysfsPath) < 0)
+        return -1;
+
+    if (!virPCIIsVirtualFunction(vfSysfsPath)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("'%s' is not a VF device"), vfSysfsPath);
+       return -1;
+    }
+
+    if (virPCIGetVirtualFunctionInfo(vfSysfsPath, -1, &pfname, &vf) < 0)
+        return -1;
+
+    if (virNetlinkDumpLink(pfname, -1, &nlData, tb, 0, 0) < 0)
+        return -1;
+
+    return virNetDevParseVfConfig(tb, vf, NULL, NULL, stats);
 }
 
 
@@ -2330,6 +2398,16 @@ virNetDevSetNetConfig(const char *linkdev G_GNUC_UNUSED,
 }
 
 
+int
+virNetDevVFInterfaceStats(virPCIDeviceAddressPtr vfAddr G_GNUC_UNUSED,
+                          virDomainInterfaceStatsPtr stats G_GNUC_UNUSED)
+{
+    virReportSystemError(ENOSYS, "%s",
+                         _("Unable to get VF net device stats on this platform"));
+    return -1;
+}
+
+
 #endif /* defined(WITH_LIBNL) */
 
 VIR_ENUM_IMPL(virNetDevIfState,
index dfef49938f33fe58c6778e131e83ec426ea705c4..53e606c61cb7bcdd5a94f537d161f3489d14e835 100644 (file)
@@ -316,4 +316,8 @@ int virNetDevSetRootQDisc(const char *ifname,
                           const char *qdisc)
     G_GNUC_NO_INLINE;
 
+int virNetDevVFInterfaceStats(virPCIDeviceAddressPtr vfAddr,
+                              virDomainInterfaceStatsPtr stats)
+ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetDevRxFilter, virNetDevRxFilterFree);