mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 22:25:25 +00:00
util: support device stats collection for <interface type='hostdev'>
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>
This commit is contained in:
parent
b79abf9c3c
commit
b295f06da4
@ -2584,6 +2584,7 @@ virNetDevSetRootQDisc;
|
||||
virNetDevSetupControl;
|
||||
virNetDevSysfsFile;
|
||||
virNetDevValidateConfig;
|
||||
virNetDevVFInterfaceStats;
|
||||
|
||||
|
||||
# util/virnetdevbandwidth.h
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user