ifconfig: Add SR-IOV VF status output

Add NIC-specific VF status to the existing ifconfig -v output.  Fetch
the data through libifconfig using a separate native route Netlink
query.

Group optional identity, initialization, resources, VLAN policy,
administrator policy, protocol, traffic-permission, and
fault containment fields.  Omitted fields remain distinct from false or
zero.

Refer users to iovctl -L for device-neutral PCI attachment and
passthrough state.

This is a Netlink-native evolution of the original interface by Eric
Joyner.

Relnotes:	yes
Sponsored by:	Intel Corporation (initial version)
Sponsored by:	BBOX.io
Differential Revision:	https://reviews.freebsd.org/D58778

Co-authored-by: Eric Joyner <[email protected]>
This commit is contained in:
Kevin BowlingandEric Joyner committed 2026-09-06 06:51:53 -07:00
1 parent a8eca531e2
commit 41418a7cd0
7 files changed
+395 -5

No files matched your search

+1
View File
@@ -66,6 +66,7 @@ LIBADD+= nv
.if ${MK_NETLINK_SUPPORT} != "no"
SRCS+= ifconfig_netlink.c
SRCS+= ifvfstatus.c # VF status information
SRCS+= ifgeneve.c # GENEVE support
.else
CFLAGS+=-DWITHOUT_NETLINK
+69 -1
View File
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd August 10, 2026
.Dd September 6, 2026
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -325,6 +325,74 @@ will attempt to load it.
Display only the interfaces that are up.
.It Fl v
Get more verbose status for an interface.
When supported, this includes the status of configured SR-IOV virtual
functions.
Fields that the PF driver cannot observe are omitted; an omitted field does
not mean false or zero.
The status begins with the PF link state and speed normally advertised to
VFs, followed by the number of VFs and one record for each VF.
PF link status does not demonstrate that a VF driver is operational.
.Pp
Per-VF fields are arranged in the following groups:
.Bl -tag -width "driver.*"
.It Cm identity
The primary MAC address known to the PF.
.It Cm state
.Cm configured=yes
means that the PF accepted the VF configuration.
.Cm initialized=yes
means that the VF completed its driver or mailbox handshake since its last
reset.
When available, this group also reports whether the PF permits traffic,
whether fault containment blocked the VF, and whether it is quarantined.
Traffic permission does not imply that the VF has enabled queues, has link,
or is actively passing packets.
.It Cm resources
The numbers of transmit and receive queues allocated to the VF.
These are not necessarily the numbers of queues currently used by its driver.
When available, configured minimum and maximum aggregate transmit rates are
also shown.
A zero minimum is displayed as
.Cm min-tx-rate=none
(no guaranteed allocation), while a zero maximum is displayed as
.Cm max-tx-rate=unlimited .
.It Cm vlan
An access VLAN is imposed by the PF; trunk mode means that no access VLAN is
imposed and does not promise unlimited filter capacity.
Access mode may also report the priority code point and VLAN protocol.
The VLAN identifier can be zero when the PF imposes priority tagging only.
The filter count includes explicit filters recorded by the PF and excludes
implicit untagged and priority-tag membership.
.It Cm policy
Administrative permissions for VF requests, MAC anti-spoofing state, and an
optional per-VF link-state policy.
Permissions describe what the VF may request, not its current requests.
.It Cm protocol
The negotiated PF/VF mailbox API version, when known.
.It Cm driver.*
Driver-specific extension namespaces.
The namespace version and scalar boolean, numeric, string, and binary fields
are displayed using their stable schema names.
See the PF driver's manual page for their meanings.
.El
.Pp
For example:
.Bd -literal -offset indent
VF-visible PF link: state=up speed=10000Mbps
virtual functions: 1
vf 0:
identity: mac=02:00:00:00:00:01
state: configured=yes initialized=yes traffic=allowed
resources: tx-queues=4 rx-queues=4
vlan: mode=trunk filters=0
policy: set-mac=denied set-vlan=allowed anti-spoof=on
.Ed
.Pp
See
.Xr iovctl.conf 5
for VF configuration and
.Xr iovctl 8
for PCI attachment and passthrough status.
.It Ar address
For the inet family,
the address is either a host name present in the host name data
+3
View File
@@ -281,6 +281,9 @@ void clone_setdefcallback_prefix(const char *, clone_callback_func *);
void clone_setdefcallback_filter(clone_match_func *, clone_callback_func *);
void sfp_status(if_ctx *ctx);
#ifndef WITHOUT_NETLINK
void vf_status(if_ctx *);
#endif
struct sockaddr_dl;
bool match_ether(const struct sockaddr_dl *sdl);
+5 -3
View File
@@ -150,8 +150,8 @@ prepare_ifmap(struct snl_state *ss, const char *ifname)
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
hdr->nlmsg_flags |= NLM_F_DUMP;
snl_reserve_msg_object(&nw, struct ifinfomsg);
if (ifname != NULL)
snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
if (ifname != NULL)
snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
return (NULL);
@@ -450,6 +450,8 @@ status_nl(if_ctx *ctx, struct iface *iface)
args->afp->af_other_status(ctx);
print_ifstatus(ctx);
if (args->verbose > 0)
vf_status(ctx);
if (args->drivername || args->verbose) {
if (ifconfig_get_orig_name(lifh, link->ifla_ifname,
&drivername) != 0) {
@@ -493,7 +495,7 @@ list_interfaces_nl(struct ifconfig_args *args)
nl_init_socket(&ss);
struct ifmap *ifmap = prepare_ifmap(&ss, args->ifname);
struct ifmap *ifmap = prepare_ifmap(&ss, args->ifname);
struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
for (uint32_t i = 0, num = 0; i < ifmap->size; i++) {
if (ifmap->ifaces[i] != NULL) {
+300
View File
@@ -0,0 +1,300 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Intel Corporation
* Copyright (c) 2026 Kevin Bowling <kbowling@FreeBSD.org>
*/
#include <sys/types.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include "ifconfig.h"
static void
vf_group_begin(bool *printed, const char *name)
{
if (!*printed) {
printf("\t\t\t%s:", name);
*printed = true;
}
}
static void
vf_group_end(bool printed)
{
if (printed)
putchar('\n');
}
static void
vf_print_rate(const char *name, uint64_t rate, const char *zero)
{
if (rate == 0)
printf(" %s=%s", name, zero);
else if (rate % IF_Mbps(1) == 0)
printf(" %s=%" PRIu64 "Mbps", name, rate / IF_Mbps(1));
else
printf(" %s=%" PRIu64 "bps", name, rate);
}
static const char *
vf_link_state_name(enum ifconfig_vf_link_state state)
{
switch (state) {
case IFCONFIG_VF_LINK_DOWN:
return ("down");
case IFCONFIG_VF_LINK_UP:
return ("up");
case IFCONFIG_VF_LINK_AUTO:
return ("auto");
default:
return ("unknown");
}
}
static const char *
vf_vlan_mode_name(enum ifconfig_vf_vlan_mode mode)
{
switch (mode) {
case IFCONFIG_VF_VLAN_ACCESS:
return ("access");
case IFCONFIG_VF_VLAN_TRUNK:
return ("trunk");
default:
return ("unknown");
}
}
static void
vf_print_vlan_proto(uint16_t proto)
{
switch (proto) {
case ETHERTYPE_VLAN:
printf("802.1q");
break;
case ETHERTYPE_QINQ:
printf("802.1ad");
break;
default:
printf("0x%04x", proto);
break;
}
}
static void
vf_driver_field(const struct ifconfig_vf_extension_field *field)
{
const uint8_t *data;
size_t i, length;
printf(" %s=", field->name);
switch (field->type) {
case IFCONFIG_VF_EXT_BOOL:
printf("%s", field->value.boolean ? "yes" : "no");
break;
case IFCONFIG_VF_EXT_NUMBER:
printf("%" PRIu64, field->value.number);
break;
case IFCONFIG_VF_EXT_STRING:
printf("%s", field->value.string);
break;
case IFCONFIG_VF_EXT_BINARY:
data = field->value.binary.data;
length = field->value.binary.length;
printf("0x");
for (i = 0; i < length; i++)
printf("%02x", data[i]);
break;
default:
break;
}
}
static void
vf_driver_status(const struct ifconfig_vf_info *vf)
{
const struct ifconfig_vf_extension *driver;
const struct ifconfig_vf_extension_field *field;
size_t i, j;
for (i = 0; i < vf->num_extensions; i++) {
driver = &vf->extensions[i];
printf("\t\t\t%s: version=%u", driver->name,
driver->version);
for (j = 0; j < driver->num_fields; j++) {
field = &driver->fields[j];
vf_driver_field(field);
}
putchar('\n');
}
}
void
vf_status(if_ctx *ctx)
{
struct ifconfig_vf_status *status;
const struct ifconfig_vf_info *vf;
const struct ether_addr *mac;
const char *mode;
uint64_t speed;
bool printed;
size_t i;
int error;
if (ifconfig_get_vf_status(lifh, ctx->ifname, &status) != 0) {
error = ifconfig_err_errno(lifh);
if (error != EOPNOTSUPP)
warnc(error, "%s: VF status", ctx->ifname);
return;
}
if (status->pf_link_state_present) {
printf("\tVF-visible PF link: state=%s",
vf_link_state_name(status->pf_link_state));
}
speed = status->pf_link_speed;
if (status->pf_link_speed_present) {
if (speed % IF_Mbps(1) == 0)
printf(" speed=%" PRIu64 "Mbps", speed / IF_Mbps(1));
else
printf(" speed=%" PRIu64 "bps", speed);
}
if (status->pf_link_state_present || status->pf_link_speed_present)
putchar('\n');
printf("\tvirtual functions: %zu\n", status->num_vfs);
for (i = 0; i < status->num_vfs; i++) {
vf = status->vfs[i];
printf("\t\tvf %3u:\n", vf->index);
printed = false;
if ((vf->fields & (1ULL << IFLAF_VF_MAC)) != 0) {
mac = (const struct ether_addr *)(const void *)vf->mac;
vf_group_begin(&printed, "identity");
printf(" mac=%s", ether_ntoa(mac));
}
vf_group_end(printed);
printed = false;
if ((vf->fields & (1ULL << IFLAF_VF_CONFIGURED)) != 0) {
vf_group_begin(&printed, "state");
printf(" configured=%s", vf->configured ? "yes" : "no");
}
if ((vf->fields & (1ULL << IFLAF_VF_INITIALIZED)) != 0) {
vf_group_begin(&printed, "state");
printf(" initialized=%s", vf->initialized ? "yes" : "no");
}
if ((vf->fields & (1ULL << IFLAF_VF_TRAFFIC_ALLOWED)) != 0) {
vf_group_begin(&printed, "state");
printf(" traffic=%s", vf->traffic_allowed ?
"allowed" : "denied");
}
if ((vf->fields & (1ULL << IFLAF_VF_FAULT_BLOCKED)) != 0) {
vf_group_begin(&printed, "state");
printf(" fault-blocked=%s", vf->fault_blocked ?
"yes" : "no");
}
if ((vf->fields & (1ULL << IFLAF_VF_QUARANTINED)) != 0) {
vf_group_begin(&printed, "state");
printf(" quarantined=%s", vf->quarantined ? "yes" : "no");
}
vf_group_end(printed);
printed = false;
if ((vf->fields & (1ULL << IFLAF_VF_NUM_TX_QUEUES)) != 0) {
vf_group_begin(&printed, "resources");
printf(" tx-queues=%u", vf->tx_queue_count);
}
if ((vf->fields & (1ULL << IFLAF_VF_NUM_RX_QUEUES)) != 0) {
vf_group_begin(&printed, "resources");
printf(" rx-queues=%u", vf->rx_queue_count);
}
if ((vf->fields & (1ULL << IFLAF_VF_MIN_TX_RATE)) != 0) {
vf_group_begin(&printed, "resources");
vf_print_rate("min-tx-rate", vf->min_tx_rate_bps, "none");
}
if ((vf->fields & (1ULL << IFLAF_VF_MAX_TX_RATE)) != 0) {
vf_group_begin(&printed, "resources");
vf_print_rate("max-tx-rate", vf->max_tx_rate_bps,
"unlimited");
}
vf_group_end(printed);
printed = false;
if ((vf->fields & (1ULL << IFLAF_VF_VLAN_MODE)) != 0) {
mode = vf_vlan_mode_name(vf->vlan_mode);
vf_group_begin(&printed, "vlan");
printf(" mode=%s", mode);
if (vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS &&
(vf->fields & (1ULL << IFLAF_VF_VLAN)) != 0)
printf(" vid=%u", vf->vlan);
if (vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS &&
(vf->fields & (1ULL << IFLAF_VF_VLAN_PCP)) != 0)
printf(" pcp=%u", vf->vlan_pcp);
if (vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS &&
(vf->fields & (1ULL << IFLAF_VF_VLAN_PROTO)) != 0) {
printf(" proto=");
vf_print_vlan_proto(vf->vlan_proto);
}
}
if ((vf->fields & (1ULL << IFLAF_VF_VLAN_COUNT)) != 0) {
vf_group_begin(&printed, "vlan");
printf(" filters=%u", vf->vlan_count);
}
if ((vf->fields & (1ULL << IFLAF_VF_VLAN_LIMIT)) != 0) {
vf_group_begin(&printed, "vlan");
printf(" limit=%u", vf->vlan_limit);
}
vf_group_end(printed);
printed = false;
if ((vf->fields & (1ULL << IFLAF_VF_ALLOW_SET_MAC)) != 0) {
vf_group_begin(&printed, "policy");
printf(" set-mac=%s", vf->allow_set_mac ?
"allowed" : "denied");
}
if ((vf->fields & (1ULL << IFLAF_VF_ALLOW_SET_VLAN)) != 0) {
vf_group_begin(&printed, "policy");
printf(" set-vlan=%s", vf->allow_set_vlan ?
"allowed" : "denied");
}
if ((vf->fields & (1ULL << IFLAF_VF_MAC_ANTI_SPOOF)) != 0) {
vf_group_begin(&printed, "policy");
printf(" anti-spoof=%s", vf->mac_anti_spoof ? "on" : "off");
}
if ((vf->fields & (1ULL << IFLAF_VF_ALLOW_PROMISC)) != 0) {
vf_group_begin(&printed, "policy");
printf(" promisc=%s", vf->allow_promisc ?
"allowed" : "denied");
}
if ((vf->fields & (1ULL << IFLAF_VF_LINK_STATE_POLICY)) != 0) {
vf_group_begin(&printed, "policy");
printf(" link-state=%s",
vf_link_state_name(vf->link_state_policy));
}
vf_group_end(printed);
printed = false;
if ((vf->fields & (1ULL << IFLAF_VF_API_VERSION)) != 0) {
vf_group_begin(&printed, "protocol");
printf(" api=%s", vf->api_version);
}
vf_group_end(printed);
vf_driver_status(vf);
}
ifconfig_free_vf_status(status);
}
+9 -1
View File
@@ -131,10 +131,18 @@ to stdout.
This action may be used to discover the configuration parameters supported on
a given PF device.
.El
.Pp
For network devices, use
.Xr ifconfig 8
with the
.Fl v
option on the PF interface to display NIC-specific VF initialization,
resource, and policy state.
.Sh SEE ALSO
.Xr vmm 4 ,
.Xr iovctl.conf 5 ,
.Xr rc.conf 5
.Xr rc.conf 5 ,
.Xr ifconfig 8
.Sh AUTHORS
This manual page was written by
.An Ryan Stone Aq Mt [email protected] .
+8
View File
@@ -145,7 +145,14 @@ After creating VFs, display their PCI attachment and passthrough state with:
.Bd -literal -offset indent
iovctl -L -d ix0
.Ed
For network devices, display NIC-specific initialization, resources, and
policy state with:
.Bd -literal -offset indent
ifconfig -v ix0
.Ed
See
.Xr ifconfig 8
and
.Xr iovctl 8
for the meaning of the reported fields.
.Sh EXAMPLES
@@ -174,6 +181,7 @@ VF-0 {
.Ed
.Sh SEE ALSO
.Xr rc.conf 5 ,
.Xr ifconfig 8 ,
.Xr iovctl 8
.Sh AUTHORS
This manual page was written by