Date: Sun, 09 Aug 2026 06:47:44 +0000 From: Kevin Bowling <kbowling@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 2c04cfa148ec - main - ifconfig: Use nvlist to report SR-IOV VF status Message-ID: <6a782290.180ab.29135cbb@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=2c04cfa148ec4dd5cef7e228aaea6a05957fcb15 commit 2c04cfa148ec4dd5cef7e228aaea6a05957fcb15 Author: Kevin Bowling <kbowling@FreeBSD.org> AuthorDate: 2026-08-08 17:34:07 +0000 Commit: Kevin Bowling <kbowling@FreeBSD.org> CommitDate: 2026-08-09 06:46:41 +0000 ifconfig: Use nvlist to report SR-IOV VF status Replace the records with a versioned nvlist transported through struct ifreq, following SIOCGIFCAPNV. The network stack now packs and copies results, supports bounded retry for larger results, and handles native and 32-bit callers centrally. Drivers only populate a kernel nvlist while their state is locked. Define optional common fields for identity, configuration and handshake state, VLAN policy, queue resources, runtime blocks, PF link state, and namespaced driver extensions. Document the extension and versioning contract and require providers to omit values they cannot observe. Improve the ixl provider to track its mailbox handshake and report the expanded common policy. Render the expanded status as grouped output under ifconfig -v. --- sbin/ifconfig/ifconfig.8 | 54 +++++++++- sbin/ifconfig/ifvfstatus.c | 228 +++++++++++++++++++++++++++++++++++++----- sys/dev/ixl/if_ixl.c | 52 ++++++---- sys/dev/ixl/ixl_pf.h | 1 + sys/dev/ixl/ixl_pf_iov.c | 2 + sys/net/if.c | 174 ++++++++++++++++++++++++++++++-- sys/net/if.h | 86 +++++++++++++--- sys/net/if_var.h | 1 + sys/net/ifdi_if.m | 9 +- sys/net/iflib.c | 9 +- sys/sys/sockio.h | 2 +- usr.sbin/iovctl/iovctl.8 | 12 ++- usr.sbin/iovctl/iovctl.conf.5 | 12 ++- 13 files changed, 555 insertions(+), 87 deletions(-) diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 1dc599a61623..eb0f663d149c 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 December 11, 2025 +.Dd August 8, 2026 .Dt IFCONFIG 8 .Os .Sh NAME @@ -325,6 +325,58 @@ 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 "resources" +.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 traffic is enabled, whether +malicious-driver detection blocked the VF, and whether it is quarantined. +.It Cm resources +The number of symmetric RX/TX queue pairs allocated to the VF. +This is not necessarily the number of queue pairs currently used by its +driver. +.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. +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. +.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=enabled + resources: queue-pairs=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. .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/ifvfstatus.c b/sbin/ifconfig/ifvfstatus.c index 0c90d667855c..af857944a12a 100644 --- a/sbin/ifconfig/ifvfstatus.c +++ b/sbin/ifconfig/ifvfstatus.c @@ -28,51 +28,231 @@ */ #include <sys/ioctl.h> +#include <sys/nv.h> #include <sys/socket.h> #include <net/ethernet.h> #include <net/if.h> #include <err.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.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'); +} + void vf_status(if_ctx *ctx) { - struct ifvfstatus_entry *entries; - struct ifvfstatus ifvfs; + const nvlist_t * const *vfs; + const nvlist_t *vf; + struct ifreq ifr; + const void *mac; + nvlist_t *status; + const char *mode, *state; + size_t maclen, num_vfs; + uint64_t speed; + void *buf, *newbuf; + u_int buflen, nextlen; + bool printed; - memset(&ifvfs, 0, sizeof(ifvfs)); - strlcpy(ifvfs.ifvfs_name, ctx->ifname, sizeof(ifvfs.ifvfs_name)); - if (ioctl_ctx(ctx, SIOCGIFVFSTATUS, &ifvfs) < 0) - return; - if (ifvfs.ifvfs_count == 0) - return; + buflen = 16 * 1024; + buf = malloc(buflen); + if (buf == NULL) + err(1, "malloc"); + for (;;) { + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ctx->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_vf_status_nv.buffer = buf; + ifr.ifr_vf_status_nv.buf_length = buflen; + if (ioctl_ctx(ctx, SIOCGIFVFSTATUS, &ifr) == 0) + break; + if (errno == EINVAL || errno == ENOTTY || errno == ENXIO || + errno == ENOTSUP || errno == EOPNOTSUPP) { + free(buf); + return; + } + if (errno != EFBIG || + buflen == IFR_VF_STATUS_NV_MAXBUFSIZE || + ifr.ifr_vf_status_nv.length > + IFR_VF_STATUS_NV_MAXBUFSIZE) { + free(buf); + warn("SIOCGIFVFSTATUS"); + return; + } + if (ifr.ifr_vf_status_nv.length > buflen) + nextlen = ifr.ifr_vf_status_nv.length; + else if (buflen > IFR_VF_STATUS_NV_MAXBUFSIZE / 2) + nextlen = IFR_VF_STATUS_NV_MAXBUFSIZE; + else + nextlen = buflen * 2; + newbuf = realloc(buf, nextlen); + if (newbuf == NULL) { + free(buf); + err(1, "realloc"); + } + buf = newbuf; + buflen = nextlen; + } - entries = calloc(ifvfs.ifvfs_count, sizeof(*entries)); - if (entries == NULL) - err(1, "calloc"); - ifvfs.ifvfs_list = entries; - if (ioctl_ctx(ctx, SIOCGIFVFSTATUS, &ifvfs) < 0) { - free(entries); - warn("SIOCGIFVFSTATUS"); + status = nvlist_unpack(buf, ifr.ifr_vf_status_nv.length, 0); + free(buf); + if (status == NULL || + !nvlist_exists_number(status, IFVF_STATUS_VERSION_KEY) || + nvlist_get_number(status, IFVF_STATUS_VERSION_KEY) != + IFVF_STATUS_VERSION || + !nvlist_exists_nvlist_array(status, IFVF_STATUS_VFS)) { + warnx("SIOCGIFVFSTATUS returned an unsupported format"); + nvlist_destroy(status); return; } - printf("\tvirtual functions: %d\n", ifvfs.ifvfs_count); - for (int i = 0; i < ifvfs.ifvfs_count; i++) { - printf("\t\tvf %3d: mac %s", i, - ether_ntoa((const struct ether_addr *)entries[i].mac_addr)); - if (entries[i].vlan > -1) - printf(" vlan %d", entries[i].vlan); - if (entries[i].active) - printf(" active"); + if (nvlist_exists_string(status, IFVF_STATUS_PF_LINK_STATE)) { + state = nvlist_get_string(status, IFVF_STATUS_PF_LINK_STATE); + printf("\tVF-visible PF link: state=%s", state); + if (nvlist_exists_number(status, IFVF_STATUS_PF_LINK_SPEED)) { + speed = nvlist_get_number(status, + IFVF_STATUS_PF_LINK_SPEED); + if (speed % IF_Mbps(1) == 0) + printf(" speed=%juMbps", (uintmax_t)(speed / + IF_Mbps(1))); + else + printf(" speed=%jubps", (uintmax_t)speed); + } putchar('\n'); } - free(entries); + vfs = nvlist_get_nvlist_array(status, IFVF_STATUS_VFS, &num_vfs); + printf("\tvirtual functions: %zu\n", num_vfs); + for (size_t i = 0; i < num_vfs; i++) { + vf = vfs[i]; + printf("\t\tvf %3ju:\n", + (uintmax_t)(nvlist_exists_number(vf, IFVF_STATUS_INDEX) ? + nvlist_get_number(vf, IFVF_STATUS_INDEX) : i)); + + printed = false; + if (nvlist_exists_binary(vf, IFVF_STATUS_MAC)) { + mac = nvlist_get_binary(vf, IFVF_STATUS_MAC, &maclen); + if (maclen == ETHER_ADDR_LEN) { + vf_group_begin(&printed, "identity"); + printf(" mac=%s", ether_ntoa(mac)); + } + } + vf_group_end(printed); + + printed = false; + if (nvlist_exists_bool(vf, IFVF_STATUS_CONFIGURED)) { + vf_group_begin(&printed, "state"); + printf(" configured=%s", + nvlist_get_bool(vf, IFVF_STATUS_CONFIGURED) ? + "yes" : "no"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_INITIALIZED)) { + vf_group_begin(&printed, "state"); + printf(" initialized=%s", nvlist_get_bool(vf, + IFVF_STATUS_INITIALIZED) ? "yes" : "no"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_TRAFFIC_ENABLED)) { + vf_group_begin(&printed, "state"); + printf(" traffic=%s", nvlist_get_bool(vf, + IFVF_STATUS_TRAFFIC_ENABLED) ? "enabled" : "disabled"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_MDD_BLOCKED)) { + vf_group_begin(&printed, "state"); + printf(" mdd-blocked=%s", nvlist_get_bool(vf, + IFVF_STATUS_MDD_BLOCKED) ? "yes" : "no"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_QUARANTINED)) { + vf_group_begin(&printed, "state"); + printf(" quarantined=%s", nvlist_get_bool(vf, + IFVF_STATUS_QUARANTINED) ? "yes" : "no"); + } + vf_group_end(printed); + + printed = false; + if (nvlist_exists_number(vf, IFVF_STATUS_NUM_QUEUES)) { + vf_group_begin(&printed, "resources"); + printf(" queue-pairs=%ju", + (uintmax_t)nvlist_get_number(vf, + IFVF_STATUS_NUM_QUEUES)); + } + vf_group_end(printed); + + printed = false; + if (nvlist_exists_string(vf, IFVF_STATUS_VLAN_MODE)) { + mode = nvlist_get_string(vf, IFVF_STATUS_VLAN_MODE); + vf_group_begin(&printed, "vlan"); + printf(" mode=%s", mode); + if (strcmp(mode, IFVF_VLAN_MODE_ACCESS) == 0 && + nvlist_exists_number(vf, IFVF_STATUS_VLAN)) + printf(" vid=%ju", (uintmax_t)nvlist_get_number(vf, + IFVF_STATUS_VLAN)); + } + if (nvlist_exists_number(vf, IFVF_STATUS_VLAN_COUNT)) { + vf_group_begin(&printed, "vlan"); + printf(" filters=%ju", (uintmax_t)nvlist_get_number(vf, + IFVF_STATUS_VLAN_COUNT)); + } + if (nvlist_exists_number(vf, IFVF_STATUS_VLAN_LIMIT)) { + vf_group_begin(&printed, "vlan"); + printf(" limit=%ju", (uintmax_t)nvlist_get_number(vf, + IFVF_STATUS_VLAN_LIMIT)); + } + vf_group_end(printed); + + printed = false; + if (nvlist_exists_bool(vf, IFVF_STATUS_ALLOW_SET_MAC)) { + vf_group_begin(&printed, "policy"); + printf(" set-mac=%s", nvlist_get_bool(vf, + IFVF_STATUS_ALLOW_SET_MAC) ? "allowed" : "denied"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_ALLOW_SET_VLAN)) { + vf_group_begin(&printed, "policy"); + printf(" set-vlan=%s", nvlist_get_bool(vf, + IFVF_STATUS_ALLOW_SET_VLAN) ? "allowed" : "denied"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_MAC_ANTI_SPOOF)) { + vf_group_begin(&printed, "policy"); + printf(" anti-spoof=%s", nvlist_get_bool(vf, + IFVF_STATUS_MAC_ANTI_SPOOF) ? "on" : "off"); + } + if (nvlist_exists_bool(vf, IFVF_STATUS_ALLOW_PROMISC)) { + vf_group_begin(&printed, "policy"); + printf(" promisc=%s", nvlist_get_bool(vf, + IFVF_STATUS_ALLOW_PROMISC) ? "allowed" : "denied"); + } + if (nvlist_exists_string(vf, IFVF_STATUS_LINK_STATE_POLICY)) { + vf_group_begin(&printed, "policy"); + printf(" link-state=%s", nvlist_get_string(vf, + IFVF_STATUS_LINK_STATE_POLICY)); + } + vf_group_end(printed); + + printed = false; + if (nvlist_exists_string(vf, IFVF_STATUS_API_VERSION)) { + vf_group_begin(&printed, "protocol"); + printf(" api=%s", nvlist_get_string(vf, + IFVF_STATUS_API_VERSION)); + } + vf_group_end(printed); + + } + nvlist_destroy(status); } diff --git a/sys/dev/ixl/if_ixl.c b/sys/dev/ixl/if_ixl.c index 439b93f0b80a..11e78235f9f9 100644 --- a/sys/dev/ixl/if_ixl.c +++ b/sys/dev/ixl/if_ixl.c @@ -122,7 +122,7 @@ static uint64_t ixl_if_get_counter(if_ctx_t ctx, ift_counter cnt); static int ixl_if_i2c_req(if_ctx_t ctx, struct ifi2creq *req); static int ixl_if_priv_ioctl(if_ctx_t ctx, u_long command, caddr_t data); static bool ixl_if_needs_restart(if_ctx_t ctx, enum iflib_restart_event event); -static int ixl_if_vfstat_ioctl(if_ctx_t ctx, struct ifvfstatus *ifvfs); +static int ixl_if_vf_status(if_ctx_t ctx, nvlist_t *status); #ifdef PCI_IOV static void ixl_if_vflr_handle(if_ctx_t ctx); #endif @@ -194,7 +194,7 @@ static device_method_t ixl_if_methods[] = { DEVMETHOD(ifdi_i2c_req, ixl_if_i2c_req), DEVMETHOD(ifdi_priv_ioctl, ixl_if_priv_ioctl), DEVMETHOD(ifdi_needs_restart, ixl_if_needs_restart), - DEVMETHOD(ifdi_vfstat_ioctl, ixl_if_vfstat_ioctl), + DEVMETHOD(ifdi_vf_status, ixl_if_vf_status), #ifdef PCI_IOV DEVMETHOD(ifdi_iov_init, ixl_if_iov_init), DEVMETHOD(ifdi_iov_uninit, ixl_if_iov_uninit), @@ -1911,35 +1911,49 @@ ixl_if_needs_restart(if_ctx_t ctx __unused, enum iflib_restart_event event) } static int -ixl_if_vfstat_ioctl(if_ctx_t ctx, struct ifvfstatus *ifvfs) +ixl_if_vf_status(if_ctx_t ctx, nvlist_t *status) { struct ixl_pf *pf; - struct ifvfstatus_entry ent; + nvlist_t **vfs; struct ixl_vf *vf; int error; pf = iflib_get_softc(ctx); if (pf->num_vfs < 1) return (ENXIO); - if (ifvfs->ifvfs_count == 0) { - ifvfs->ifvfs_count = pf->num_vfs; - return (0); - } - if (ifvfs->ifvfs_count != pf->num_vfs) - return (EINVAL); + vfs = mallocarray(pf->num_vfs, sizeof(*vfs), M_IXL, + M_WAITOK | M_ZERO); for (int i = 0; i < pf->num_vfs; i++) { vf = &pf->vfs[i]; - memset(&ent, 0, sizeof(ent)); - ent.active = !!(vf->vf_flags & VF_FLAG_ENABLED); - memcpy(ent.mac_addr, vf->mac, sizeof(ent.mac_addr)); - /* No host VLAN support. */ - ent.vlan = -1; - error = copyout(&ent, &ifvfs->ifvfs_list[i], sizeof(ent)); - if (error != 0) - return (error); + vfs[i] = nvlist_create(0); + nvlist_add_number(vfs[i], IFVF_STATUS_INDEX, vf->vf_num); + nvlist_add_bool(vfs[i], IFVF_STATUS_CONFIGURED, + (vf->vf_flags & VF_FLAG_ENABLED) != 0); + nvlist_add_bool(vfs[i], IFVF_STATUS_INITIALIZED, + (vf->vf_flags & VF_FLAG_INITIALIZED) != 0); + nvlist_add_binary(vfs[i], IFVF_STATUS_MAC, vf->mac, + ETHER_ADDR_LEN); + nvlist_add_string(vfs[i], IFVF_STATUS_VLAN_MODE, + IFVF_VLAN_MODE_TRUNK); + nvlist_add_number(vfs[i], IFVF_STATUS_NUM_QUEUES, + vf->qtag.num_active); + nvlist_add_bool(vfs[i], IFVF_STATUS_ALLOW_SET_MAC, + (vf->vf_flags & VF_FLAG_SET_MAC_CAP) != 0); + nvlist_add_bool(vfs[i], IFVF_STATUS_ALLOW_SET_VLAN, + (vf->vf_flags & VF_FLAG_VLAN_CAP) != 0); + nvlist_add_bool(vfs[i], IFVF_STATUS_MAC_ANTI_SPOOF, + (vf->vf_flags & VF_FLAG_MAC_ANTI_SPOOF) != 0); + nvlist_add_bool(vfs[i], IFVF_STATUS_ALLOW_PROMISC, + (vf->vf_flags & VF_FLAG_PROMISC_CAP) != 0); } - return (0); + nvlist_add_nvlist_array(status, IFVF_STATUS_VFS, + (const nvlist_t * const *)vfs, pf->num_vfs); + error = nvlist_error(status); + for (int i = 0; i < pf->num_vfs; i++) + nvlist_destroy(vfs[i]); + free(vfs, M_IXL); + return (error); } /* diff --git a/sys/dev/ixl/ixl_pf.h b/sys/dev/ixl/ixl_pf.h index f0d42f18cef0..465a72092da1 100644 --- a/sys/dev/ixl/ixl_pf.h +++ b/sys/dev/ixl/ixl_pf.h @@ -45,6 +45,7 @@ #define VF_FLAG_VLAN_CAP 0x04 #define VF_FLAG_PROMISC_CAP 0x08 #define VF_FLAG_MAC_ANTI_SPOOF 0x10 +#define VF_FLAG_INITIALIZED 0x20 #define IXL_ICR0_CRIT_ERR_MASK \ (I40E_PFINT_ICR0_PCI_EXCEPTION_MASK | \ diff --git a/sys/dev/ixl/ixl_pf_iov.c b/sys/dev/ixl/ixl_pf_iov.c index 1e475e4767cc..de8e2c27b9eb 100644 --- a/sys/dev/ixl/ixl_pf_iov.c +++ b/sys/dev/ixl/ixl_pf_iov.c @@ -397,6 +397,7 @@ ixl_reinit_vf(struct ixl_pf *pf, struct ixl_vf *vf) int i, error; hw = &pf->hw; + vf->vf_flags &= ~VF_FLAG_INITIALIZED; error = ixl_flush_pcie(pf, vf); if (error != 0) @@ -550,6 +551,7 @@ ixl_vf_get_resources_msg(struct ixl_pf *pf, struct ixl_vf *vf, void *msg, ixl_send_vf_msg(pf, vf, VIRTCHNL_OP_GET_VF_RESOURCES, I40E_SUCCESS, &reply, sizeof(reply)); + vf->vf_flags |= VF_FLAG_INITIALIZED; } static int diff --git a/sys/net/if.c b/sys/net/if.c index 1c57eb1492c2..4598337f3964 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -124,6 +124,12 @@ struct ifreq_buffer32 { uint32_t buffer; /* (void *) */ }; +struct ifreq_nv_req32 { + u_int buf_length; + u_int length; + uint32_t buffer; /* (void *) */ +}; + /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter @@ -148,6 +154,7 @@ struct ifreq32 { int ifru_cap[2]; u_int ifru_fib; u_char ifru_vlan_pcp; + struct ifreq_nv_req32 ifru_nv; } ifr_ifru; }; CTASSERT(sizeof(struct ifreq) == sizeof(struct ifreq32)); @@ -2237,6 +2244,74 @@ ifr_buffer_set_length(void *data, size_t len) ifrup->ifr.ifr_ifru.ifru_buffer.length = len; } +static void * +ifr_nv_get_buffer(void *data) +{ + union ifreq_union *ifrup; + + ifrup = data; +#ifdef COMPAT_FREEBSD32 + if (SV_CURPROC_FLAG(SV_ILP32)) + return ((void *)(uintptr_t) + ifrup->ifr32.ifr_ifru.ifru_nv.buffer); +#endif + return (ifrup->ifr.ifr_ifru.ifru_nv.buffer); +} + +static void +ifr_nv_set_buffer_null(void *data) +{ + union ifreq_union *ifrup; + + ifrup = data; +#ifdef COMPAT_FREEBSD32 + if (SV_CURPROC_FLAG(SV_ILP32)) + ifrup->ifr32.ifr_ifru.ifru_nv.buffer = 0; + else +#endif + ifrup->ifr.ifr_ifru.ifru_nv.buffer = NULL; +} + +static u_int +ifr_nv_get_buf_length(void *data) +{ + union ifreq_union *ifrup; + + ifrup = data; +#ifdef COMPAT_FREEBSD32 + if (SV_CURPROC_FLAG(SV_ILP32)) + return (ifrup->ifr32.ifr_ifru.ifru_nv.buf_length); +#endif + return (ifrup->ifr.ifr_ifru.ifru_nv.buf_length); +} + +static u_int +ifr_nv_get_length(void *data) +{ + union ifreq_union *ifrup; + + ifrup = data; +#ifdef COMPAT_FREEBSD32 + if (SV_CURPROC_FLAG(SV_ILP32)) + return (ifrup->ifr32.ifr_ifru.ifru_nv.length); +#endif + return (ifrup->ifr.ifr_ifru.ifru_nv.length); +} + +static void +ifr_nv_set_length(void *data, u_int len) +{ + union ifreq_union *ifrup; + + ifrup = data; +#ifdef COMPAT_FREEBSD32 + if (SV_CURPROC_FLAG(SV_ILP32)) + ifrup->ifr32.ifr_ifru.ifru_nv.length = len; + else +#endif + ifrup->ifr.ifr_ifru.ifru_nv.length = len; +} + void * ifr_data_get_ptr(void *ifrp) { @@ -2336,6 +2411,53 @@ if_capint_to_capnv(nvlist_t *nv, const struct ifcap_nv_bit_name *nn, } } +int +if_get_vf_status(if_t ifp, nvlist_t **statusp) +{ + nvlist_t *status; + uint64_t baudrate; + int error; + + *statusp = NULL; + if (ifp->if_ioctl == NULL) + return (EOPNOTSUPP); + + status = nvlist_create(0); + nvlist_add_number(status, IFVF_STATUS_VERSION_KEY, + IFVF_STATUS_VERSION); + error = (*ifp->if_ioctl)(ifp, SIOCGIFVFSTATUS, + __DECONST(caddr_t, status)); + if (error != 0) + goto out; + + switch (if_getlinkstate(ifp)) { + case LINK_STATE_DOWN: + nvlist_add_string(status, IFVF_STATUS_PF_LINK_STATE, + IFVF_LINK_STATE_DOWN); + break; + case LINK_STATE_UP: + nvlist_add_string(status, IFVF_STATUS_PF_LINK_STATE, + IFVF_LINK_STATE_UP); + break; + default: + nvlist_add_string(status, IFVF_STATUS_PF_LINK_STATE, + IFVF_LINK_STATE_UNKNOWN); + break; + } + baudrate = if_getbaudrate(ifp); + if (baudrate != 0) + nvlist_add_number(status, IFVF_STATUS_PF_LINK_SPEED, baudrate); + error = nvlist_error(status); + if (error == 0) { + *statusp = status; + return (0); + } + +out: + nvlist_destroy(status); + return (error); +} + /* * Hardware specific interface ioctls. */ @@ -2349,7 +2471,7 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) char *descrbuf; char new_name[IFNAMSIZ]; void *buf; - nvlist_t *nvcap; + nvlist_t *nvcap, *nvstatus; struct siocsifcapnv_driver_data drv_ioctl_data; ifr = (struct ifreq *)data; @@ -2396,20 +2518,50 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) error = EDOOFUS; break; } - if (nvbuflen > ifr->ifr_cap_nv.buf_length) { - ifr->ifr_cap_nv.length = nvbuflen; - ifr->ifr_cap_nv.buffer = NULL; + if (nvbuflen > ifr_nv_get_buf_length(data)) { + ifr_nv_set_length(data, nvbuflen); + ifr_nv_set_buffer_null(data); error = EFBIG; break; } - ifr->ifr_cap_nv.length = nvbuflen; - error = copyout(buf, ifr->ifr_cap_nv.buffer, nvbuflen); + ifr_nv_set_length(data, nvbuflen); + error = copyout(buf, ifr_nv_get_buffer(data), nvbuflen); break; } free(buf, M_NVLIST); nvlist_destroy(nvcap); break; + case SIOCGIFVFSTATUS: + buf = NULL; + nvstatus = NULL; + for (;;) { + error = if_get_vf_status(ifp, &nvstatus); + if (error != 0) + break; + buf = nvlist_pack(nvstatus, &nvbuflen); + if (buf == NULL) { + error = nvlist_error(nvstatus); + if (error == 0) + error = EDOOFUS; + break; + } + if (nvbuflen > IFR_VF_STATUS_NV_MAXBUFSIZE) { + error = E2BIG; + break; + } + ifr_nv_set_length(data, nvbuflen); + if (nvbuflen > ifr_nv_get_buf_length(data)) { + error = EFBIG; + break; + } + error = copyout(buf, ifr_nv_get_buffer(data), nvbuflen); + break; + } + free(buf, M_NVLIST); + nvlist_destroy(nvstatus); + break; + case SIOCGIFDATA: { struct if_data ifd; @@ -2563,16 +2715,16 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) return (EOPNOTSUPP); if ((ifp->if_capabilities & IFCAP_NV) == 0) return (EINVAL); - if (ifr->ifr_cap_nv.length > IFR_CAP_NV_MAXBUFSIZE) + if (ifr_nv_get_length(data) > IFR_CAP_NV_MAXBUFSIZE) return (EINVAL); nvcap = NULL; - buf = malloc(ifr->ifr_cap_nv.length, M_TEMP, M_WAITOK); + buf = malloc(ifr_nv_get_length(data), M_TEMP, M_WAITOK); for (;;) { - error = copyin(ifr->ifr_cap_nv.buffer, buf, - ifr->ifr_cap_nv.length); + error = copyin(ifr_nv_get_buffer(data), buf, + ifr_nv_get_length(data)); if (error != 0) break; - nvcap = nvlist_unpack(buf, ifr->ifr_cap_nv.length, 0); + nvcap = nvlist_unpack(buf, ifr_nv_get_length(data), 0); if (nvcap == NULL) { error = EINVAL; break; diff --git a/sys/net/if.h b/sys/net/if.h index c5b7768f033d..0e729a275f66 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -450,6 +450,76 @@ struct ifreq_nv_req { }; #define IFR_CAP_NV_MAXBUFSIZE (2 * 1024 * 1024) +#define IFR_VF_STATUS_NV_MAXBUFSIZE (2 * 1024 * 1024) + +/* + * SIOCGIFVFSTATUS schema contract. + * + * The top-level nvlist contains a version number and an array of per-VF + * nvlists. Per-VF keys other than the index are optional: providers omit + * values that are unknown or cannot be observed rather than reporting a + * false or zero value. "configured" means that the PF accepted the VF + * configuration, while "initialized" means that the VF completed its + * driver/mailbox handshake since its last reset. The MAC address is the + * PF's known primary address for the VF. The queue count is the number of + * allocated symmetric RX/TX queue pairs, not necessarily the number the + * guest currently uses. + * + * An access VLAN is imposed by the PF. Trunk mode means that no access + * VLAN is imposed; it does not promise unlimited VLAN-filter capacity. + * The VLAN count is the number of explicit filters recorded by the PF and + * excludes implicit untagged or priority-tag membership. "allow-*" values + * are administrative permissions, not the VF's current requests, and the + * blocked values describe current runtime state. The top-level PF link + * state and speed are the PF ifnet values normally advertised to VFs, not + * evidence that a guest driver is operational; speed is in bits per second. + * A per-VF link-state policy, when present, can override PF link visibility. + * Driver-specific data is stored in the optional "extensions" nvlist. Its + * keys are stable namespace names, not runtime device names. Each namespace + * contains a mandatory version number and owns the types and meanings of its + * remaining keys. Consumers must ignore unknown namespaces and keys. A + * provider omits an extension value that it cannot observe, and must not + * duplicate a field whose semantics are already represented by the common + * schema. Additive optional keys retain the namespace version; incompatible + * type or structural changes require a new namespace version. + * + * Additive optional common keys retain the top-level version. Change the + * top-level version only for an incompatible type or structural change. + */ +#define IFVF_STATUS_VERSION 1 +#define IFVF_STATUS_VERSION_KEY "version" +#define IFVF_STATUS_VFS "vfs" +#define IFVF_STATUS_PF_LINK_STATE "pf-link-state" +#define IFVF_STATUS_PF_LINK_SPEED "pf-link-speed" +#define IFVF_STATUS_INDEX "index" +#define IFVF_STATUS_CONFIGURED "configured" +#define IFVF_STATUS_INITIALIZED "initialized" +#define IFVF_STATUS_MAC "mac-address" +#define IFVF_STATUS_VLAN_MODE "vlan-mode" +#define IFVF_STATUS_VLAN "vlan" +#define IFVF_STATUS_VLAN_COUNT "vlan-count" +#define IFVF_STATUS_VLAN_LIMIT "vlan-limit" +#define IFVF_STATUS_NUM_QUEUES "num-queues" +#define IFVF_STATUS_ALLOW_SET_MAC "allow-set-mac" +#define IFVF_STATUS_ALLOW_SET_VLAN "allow-set-vlan" +#define IFVF_STATUS_MAC_ANTI_SPOOF "mac-anti-spoof" +#define IFVF_STATUS_ALLOW_PROMISC "allow-promisc" +#define IFVF_STATUS_TRAFFIC_ENABLED "traffic-enabled" +#define IFVF_STATUS_MDD_BLOCKED "mdd-blocked" +#define IFVF_STATUS_QUARANTINED "quarantined" +#define IFVF_STATUS_API_VERSION "api-version" +#define IFVF_STATUS_LINK_STATE_POLICY "link-state-policy" +#define IFVF_STATUS_EXTENSIONS "extensions" + +#define IFVF_STATUS_EXT_VERSION "version" + +#define IFVF_VLAN_MODE_ACCESS "access" +#define IFVF_VLAN_MODE_TRUNK "trunk" + +#define IFVF_LINK_STATE_UNKNOWN "unknown" +#define IFVF_LINK_STATE_DOWN "down" +#define IFVF_LINK_STATE_UP "up" +#define IFVF_LINK_STATE_AUTO "auto" /* * Interface request structure used for socket @@ -500,6 +570,7 @@ struct ifreq { #define ifr_vlan_pcp ifr_ifru.ifru_vlan_pcp /* VLAN priority */ #define ifr_lan_pcp ifr_ifru.ifru_vlan_pcp /* VLAN priority */ #define ifr_cap_nv ifr_ifru.ifru_nv /* nv-based cap interface */ +#define ifr_vf_status_nv ifr_ifru.ifru_nv /* nv-based VF status */ }; #define _SIZEOF_ADDR_IFREQ(ifr) \ @@ -662,21 +733,6 @@ struct ifdownreason { char ifdr_msg[IFDR_MSG_SIZE]; }; -struct ifvfstatus_entry { - uint8_t mac_addr[6]; - int16_t vlan; - uint8_t active; - uint8_t spare0; - uint16_t spare1; - uint32_t spare2; -}; - -struct ifvfstatus { - char ifvfs_name[IFNAMSIZ]; - int ifvfs_count; - struct ifvfstatus_entry *ifvfs_list; -}; - #endif /* __BSD_VISIBLE */ /* diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 9a425fd81054..5b892ab7f426 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -763,6 +763,7 @@ void *ifr_buffer_get_buffer(void *data); size_t ifr_buffer_get_length(void *data); int ifhwioctl(u_long, if_t, caddr_t, struct thread *); +int if_get_vf_status(if_t, struct nvlist **); #ifdef DEVICE_POLLING enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS }; diff --git a/sys/net/ifdi_if.m b/sys/net/ifdi_if.m index 65a130d64ee4..70e8a44e5f00 100644 --- a/sys/net/ifdi_if.m +++ b/sys/net/ifdi_if.m @@ -113,8 +113,7 @@ CODE { } static int - null_vfstat_ioctl(if_ctx_t _ctx __unused, - struct ifvfstatus *_ifvfs __unused) + null_vf_status(if_ctx_t _ctx __unused, nvlist_t *_status __unused) { return (ENOTSUP); } @@ -383,7 +382,7 @@ METHOD int get_downreason { struct ifdownreason *_ifdr; } DEFAULT null_get_downreason; -METHOD int vfstat_ioctl { +METHOD int vf_status { if_ctx_t _ctx; - struct ifvfstatus *_ifvfs; -} DEFAULT null_vfstat_ioctl; + nvlist_t *_status; +} DEFAULT null_vf_status; diff --git a/sys/net/iflib.c b/sys/net/iflib.c index 314e8e527628..54001be3eae5 100644 --- a/sys/net/iflib.c +++ b/sys/net/iflib.c @@ -4658,17 +4658,10 @@ iflib_if_ioctl(if_t ifp, u_long command, caddr_t data) CTX_UNLOCK(ctx); break; case SIOCGIFVFSTATUS: - { - struct ifvfstatus *ifvfs; - - ifvfs = (struct ifvfstatus *)data; - if (ifvfs->ifvfs_count < 0) - return (EINVAL); CTX_LOCK(ctx); - err = IFDI_VFSTAT_IOCTL(ctx, ifvfs); + err = IFDI_VF_STATUS(ctx, (nvlist_t *)data); CTX_UNLOCK(ctx); break; - } default: err = ether_ioctl(ifp, command, data); break; diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h index a3ce196c7959..b3c499f1ccca 100644 --- a/sys/sys/sockio.h +++ b/sys/sys/sockio.h @@ -152,6 +152,6 @@ #define SIOCSUMBPARAM _IOW('i', 158, struct ifreq) /* set MBIM param */ #define SIOCGUMBPARAM _IOWR('i', 159, struct ifreq) /* get MBIM param */ -#define SIOCGIFVFSTATUS _IOWR('i', 163, struct ifvfstatus) /* get VF status */ +#define SIOCGIFVFSTATUS _IOWR('i', 163, struct ifreq) /* get VF status */ #endif /* !_SYS_SOCKIO_H_ */ diff --git a/usr.sbin/iovctl/iovctl.8 b/usr.sbin/iovctl/iovctl.8 index 2574503e5ae7..84c5d05cc795 100644 --- a/usr.sbin/iovctl/iovctl.8 +++ b/usr.sbin/iovctl/iovctl.8 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 31, 2020 +.Dd August 8, 2026 .Dt IOVCTL 8 .Os .Sh NAME @@ -113,9 +113,17 @@ to stdout. This action may be used to discover the configuration parameters supported on a given PF device. .El +.Pp +After creating VFs, 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 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 1bae96e150ba..741f343e7e5f 100644 --- a/usr.sbin/iovctl/iovctl.conf.5 +++ b/usr.sbin/iovctl/iovctl.conf.5 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 30, 2024 +.Dd August 8, 2026 .Dt IOVCTL.CONF 5 .Os .Sh NAME @@ -140,6 +140,15 @@ The default value of this parameter is false. .Pp See the PF driver manual page for configuration parameters specific to particular hardware. +.Pp +For network devices, display VF initialization, resources, and policy state +with: +.Bd -literal -offset indent +ifconfig -v ix0 +.Ed +See +.Xr ifconfig 8 +for the meaning of the reported fields. .Sh EXAMPLES This sample file will create 3 VFs as children of the ix0 device. VF-1 and VF-2 are set as @@ -166,6 +175,7 @@ VF-0 { .Ed .Sh SEE ALSO .Xr rc.conf 5 , +.Xr ifconfig 8 , .Xr iovctl 8 .Sh AUTHORS *** 1 LINES SKIPPED ***home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a782290.180ab.29135cbb>
