git: 41418a7cd00c - main - ifconfig: Add SR-IOV VF status output
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 06 Sep 2026 13:52:03 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=41418a7cd00c4a20eb72bc8c315c0e364d4e4ad2
commit 41418a7cd00c4a20eb72bc8c315c0e364d4e4ad2
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-10 22:30:22 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-06 13:51:53 +0000
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 <erj@FreeBSD.org>
---
sbin/ifconfig/Makefile | 1 +
sbin/ifconfig/ifconfig.8 | 70 ++++++++-
sbin/ifconfig/ifconfig.h | 3 +
sbin/ifconfig/ifconfig_netlink.c | 8 +-
sbin/ifconfig/ifvfstatus.c | 300 +++++++++++++++++++++++++++++++++++++++
usr.sbin/iovctl/iovctl.8 | 10 +-
usr.sbin/iovctl/iovctl.conf.5 | 8 ++
7 files changed, 395 insertions(+), 5 deletions(-)
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
index 26391023d54a..fa3cc26a3023 100644
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -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
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index 7a85ba314f43..c1d560fb4d12 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -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
diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h
index 672020443b8c..45e5b108accb 100644
--- a/sbin/ifconfig/ifconfig.h
+++ b/sbin/ifconfig/ifconfig.h
@@ -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);
diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c
index 38e3edb3e597..2b24cac7ab1c 100644
--- a/sbin/ifconfig/ifconfig_netlink.c
+++ b/sbin/ifconfig/ifconfig_netlink.c
@@ -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) {
diff --git a/sbin/ifconfig/ifvfstatus.c b/sbin/ifconfig/ifvfstatus.c
new file mode 100644
index 000000000000..82cf0c5400d7
--- /dev/null
+++ b/sbin/ifconfig/ifvfstatus.c
@@ -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);
+}
diff --git a/usr.sbin/iovctl/iovctl.8 b/usr.sbin/iovctl/iovctl.8
index 67c2ed22199c..3e1fc8c18639 100644
--- a/usr.sbin/iovctl/iovctl.8
+++ b/usr.sbin/iovctl/iovctl.8
@@ -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 rstone@FreeBSD.org .
diff --git a/usr.sbin/iovctl/iovctl.conf.5 b/usr.sbin/iovctl/iovctl.conf.5
index 864637626d92..c9d5404970c7 100644
--- a/usr.sbin/iovctl/iovctl.conf.5
+++ b/usr.sbin/iovctl/iovctl.conf.5
@@ -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