mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
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:
1 parent
a8eca531e2
commit
41418a7cd0
7 files changed
+395
-5
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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] .
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user