diff --git a/share/man/man9/ifnet.9 b/share/man/man9/ifnet.9 --- a/share/man/man9/ifnet.9 +++ b/share/man/man9/ifnet.9 @@ -1335,6 +1335,74 @@ .Va if_data.ifi_hwassist appropriately. .Pp +.It Dv SIOCGIFCAPNV +.Xr NV 9 +version of the +.Dv SIOCGIFCAP +ioctl. +Caller must provide the pointer to +.Vt struct ifreq_cap_nv +as +.Fa data , +where the member +.Dv buffer +points to some buffer containing +.Dv buf_length +bytes. +The serialized nvlist with description of the device capabilities +is written to the buffer. +If buffer is too short, then the structure is updated with +.Dv buffer +member set to +.Dv NULL, +.Dv length set to the minimal required length, +and error +.Er EFBIG +is returned. +.Pp +Elements of the returned nvlist for simple capabilities are boolean, +identified by names. +Presence of the boolean element means that corresponding capability is +supported by the interface. +Element's value describes the current configured state: +.Dv true +means that the capability is enabled, and +.Dv false +that it is disabled. +.Pp +Driver indicates support for both +.Dv SIOCGIFCAPNV +and +.Dv SIOCSIFCAPNV +requests by setting +.Dv IFCAP_NV +non-modifiable capability bit in +.Dv if_capabilities . +.Pp +.It Dv SIOCSIFCAPNV +.Xr NV 9 +version of the +.Dv SIOCSIFCAP +ioctl. +Caller must provide the pointer to +.Vt struct ifreq_cap_nv +as +.Fa data , +where the member +.Dv buffer +points to unpacked nvlist of +.Dv length +bytes. +Each element of nvlist describes a requested update of one capability, +identified by the element name. +For simple capabilities, the element must be boolean. +Its +.Dv true +value means that the caller asks to enable the capability, and +.Dv false +value to disable. +Only capabilities listed in the nvlist are affected by the call. +.Pp .It Dv SIOCSIFFIB Sets interface FIB. Caller must have appropriate privilege. diff --git a/sys/dev/mlx5/mlx5_en/en.h b/sys/dev/mlx5/mlx5_en/en.h --- a/sys/dev/mlx5/mlx5_en/en.h +++ b/sys/dev/mlx5/mlx5_en/en.h @@ -1077,6 +1077,35 @@ bool sw_is_port_buf_owner; struct pfil_head *pfil; + + bool ifcap_rxcsum : 1; + bool ifcap_txcsum : 1; + bool ifcap_vlan_mtu : 1; + bool ifcap_vlan_hwtagging : 1; + bool ifcap_jumbo_mtu : 1; + bool ifcap_vlan_hwcsum : 1; + bool ifcap_tso4 : 1; + bool ifcap_tso6 : 1; + bool ifcap_lro : 1; + bool ifcap_vlan_hwfilter : 1; + bool ifcap_vlan_hwtso : 1; + bool ifcap_linkstate : 1; + bool ifcap_rxcsum_ipv6 : 1; + bool ifcap_txcsum_ipv6 : 1; + bool ifcap_hwstats_name : 1; +#ifdef RATELIMIT + bool ifcap_txrtlmt : 1; +#endif + bool ifcap_hwrxtstmp : 1; + bool ifcap_mextpg : 1; + bool ifcap_txtls4 : 1; + bool ifcap_txtls6 : 1; + bool ifcap_vxlan_hwcsum : 1; + bool ifcap_vxlan_hwtso : 1; +#ifdef RATELIMIT + bool ifcap_txtls_rtlmt : 1; +#endif + struct mlx5e_channel channel[]; }; diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c --- a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c +++ b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c @@ -30,6 +30,7 @@ #include "en.h" #include +#include #include #include @@ -3279,6 +3280,54 @@ queue_work(priv->wq, &priv->set_rx_mode_work); } +static int +mlx5e_if_caps(struct mlx5e_priv *priv) +{ + int res; + + res = IFCAP_NV; + if (priv->ifcap_rxcsum) + res |= IFCAP_RXCSUM; + if (priv->ifcap_txcsum) + res |= IFCAP_TXCSUM; + if (priv->ifcap_rxcsum_ipv6) + res |= IFCAP_RXCSUM_IPV6; + if (priv->ifcap_txcsum_ipv6) + res |= IFCAP_TXCSUM_IPV6; + if (priv->ifcap_vlan_mtu) + res |= IFCAP_VLAN_MTU; + if (priv->ifcap_vlan_hwfilter) + res |= IFCAP_VLAN_HWFILTER; + if (priv->ifcap_vlan_hwtagging) + res |= IFCAP_VLAN_HWTAGGING; + if (priv->ifcap_txtls4) + res |= IFCAP_TXTLS4; + if (priv->ifcap_txtls6) + res |= IFCAP_TXTLS6; +#ifdef RATELIMIT + if (priv->ifcap_txrtlmt) + res |= IFCAP_TXRTLMT; + if (priv->ifcap_txtls_rtlmt) + res |= IFCAP_TXTLS_RTLMT; +#endif + if (priv->ifcap_vxlan_hwcsum) + res |= IFCAP_VXLAN_HWCSUM; + if (priv->ifcap_vxlan_hwtso) + res |= IFCAP_VXLAN_HWTSO; + if (priv->ifcap_mextpg) + res |= IFCAP_MEXTPG; + if (priv->ifcap_tso4) + res |= IFCAP_TSO4; + if (priv->ifcap_tso6) + res |= IFCAP_TSO6; + if (priv->ifcap_lro) + res |= IFCAP_LRO; + if (priv->ifcap_hwrxtstmp) + res |= IFCAP_HWRXTSTMP; + + return (res); +} + static int mlx5e_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { @@ -3288,8 +3337,8 @@ struct ifi2creq i2c; struct ifrsskey *ifrk; struct ifrsshash *ifrh; + nvlist_t *nv; int error = 0; - int mask = 0; int size_read = 0; int module_status; int module_num; @@ -3366,121 +3415,205 @@ ifr = (struct ifreq *)data; error = ifmedia_ioctl(ifp, ifr, &priv->media, command); break; - case SIOCSIFCAP: - ifr = (struct ifreq *)data; + case SIOCGIFCAPNV: + nv = (nvlist_t *)data; + PRIV_LOCK(priv); + nvlist_add_bool(nv, IFCAP_RXCSUM_NAME, priv->ifcap_rxcsum); + nvlist_add_bool(nv, IFCAP_TXCSUM_NAME, priv->ifcap_txcsum); + nvlist_add_bool(nv, IFCAP_VLAN_MTU_NAME, priv->ifcap_vlan_mtu); + nvlist_add_bool(nv, IFCAP_VLAN_HWTAGGING_NAME, + priv->ifcap_vlan_hwtagging); + nvlist_add_bool(nv, IFCAP_JUMBO_MTU_NAME, + priv->ifcap_jumbo_mtu); + nvlist_add_bool(nv, IFCAP_VLAN_HWCSUM_NAME, + priv->ifcap_vlan_hwcsum); + nvlist_add_bool(nv, IFCAP_TSO4_NAME, priv->ifcap_tso4); + nvlist_add_bool(nv, IFCAP_TSO6_NAME, priv->ifcap_tso6); + nvlist_add_bool(nv, IFCAP_LRO_NAME, priv->ifcap_lro); + nvlist_add_bool(nv, IFCAP_VLAN_HWFILTER_NAME, + priv->ifcap_vlan_hwfilter); + nvlist_add_bool(nv, IFCAP_VLAN_HWTSO_NAME, + priv->ifcap_vlan_hwtso); + nvlist_add_bool(nv, IFCAP_LINKSTATE_NAME, + priv->ifcap_linkstate); + nvlist_add_bool(nv, IFCAP_RXCSUM_IPV6_NAME, + priv->ifcap_rxcsum_ipv6); + nvlist_add_bool(nv, IFCAP_TXCSUM_IPV6_NAME, + priv->ifcap_txcsum_ipv6); + nvlist_add_bool(nv, IFCAP_HWSTATS_NAME, + priv->ifcap_hwstats_name); +#ifdef RATELIMIT + nvlist_add_bool(nv, IFCAP_TXRTLMT_NAME, priv->ifcap_txrtlmt); +#endif + nvlist_add_bool(nv, IFCAP_HWRXTSTMP_NAME, + priv->ifcap_hwrxtstmp); + nvlist_add_bool(nv, IFCAP_MEXTPG_NAME, priv->ifcap_mextpg); + nvlist_add_bool(nv, IFCAP_TXTLS4_NAME, priv->ifcap_txtls4); + nvlist_add_bool(nv, IFCAP_TXTLS6_NAME, priv->ifcap_txtls6); + nvlist_add_bool(nv, IFCAP_VXLAN_HWCSUM_NAME, + priv->ifcap_vxlan_hwcsum); + nvlist_add_bool(nv, IFCAP_VXLAN_HWTSO_NAME, + priv->ifcap_vxlan_hwtso); +#ifdef RATELIMIT + nvlist_add_bool(nv, IFCAP_TXTLS_RTLMT_NAME, + priv->ifcap_txtls_rtlmt); +#endif + PRIV_UNLOCK(priv); + break; + case SIOCSIFCAPNV: + nv = (nvlist_t *)data; PRIV_LOCK(priv); - mask = ifr->ifr_reqcap ^ ifp->if_capenable; - - if (mask & IFCAP_TXCSUM) { - ifp->if_capenable ^= IFCAP_TXCSUM; - ifp->if_hwassist ^= (CSUM_TCP | CSUM_UDP | CSUM_IP); - if (IFCAP_TSO4 & ifp->if_capenable && - !(IFCAP_TXCSUM & ifp->if_capenable)) { - mask &= ~IFCAP_TSO4; - ifp->if_capenable &= ~IFCAP_TSO4; - ifp->if_hwassist &= ~CSUM_IP_TSO; - mlx5_en_err(ifp, - "tso4 disabled due to -txcsum.\n"); + if (nvlist_exists_bool(nv, IFCAP_TXCSUM_NAME)) { + if (nvlist_get_bool(nv, IFCAP_TXCSUM_NAME)) { + priv->ifcap_txcsum = true; + ifp->if_hwassist |= CSUM_TCP | CSUM_UDP | + CSUM_IP; + } else { + priv->ifcap_txcsum = false; + ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP | + CSUM_IP); + if (priv->ifcap_tso4) { + nvlist_free_bool(nv, IFCAP_TSO4_NAME); + nvlist_add_bool(nv, IFCAP_TSO4_NAME, + false); + priv->ifcap_tso4 = false; + ifp->if_hwassist &= ~CSUM_IP_TSO; + mlx5_en_err(ifp, + "tso4 disabled due to -txcsum.\n"); + } } } - if (mask & IFCAP_TXCSUM_IPV6) { - ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; - ifp->if_hwassist ^= (CSUM_UDP_IPV6 | CSUM_TCP_IPV6); - - if (IFCAP_TSO6 & ifp->if_capenable && - !(IFCAP_TXCSUM_IPV6 & ifp->if_capenable)) { - mask &= ~IFCAP_TSO6; - ifp->if_capenable &= ~IFCAP_TSO6; - ifp->if_hwassist &= ~CSUM_IP6_TSO; - mlx5_en_err(ifp, + if (nvlist_exists_bool(nv, IFCAP_TXCSUM_IPV6_NAME)) { + if (nvlist_get_bool(nv, IFCAP_TXCSUM_IPV6_NAME)) { + priv->ifcap_txcsum_ipv6 = true; + ifp->if_hwassist |= CSUM_UDP_IPV6 | + CSUM_TCP_IPV6; + } else { + priv->ifcap_txcsum_ipv6 = false; + ifp->if_hwassist &= ~(CSUM_UDP_IPV6 | + CSUM_TCP_IPV6); + if (priv->ifcap_tso6) { + nvlist_free_bool(nv, IFCAP_TSO6_NAME); + nvlist_add_bool(nv, IFCAP_TSO6_NAME, + false); + priv->ifcap_tso6 = false; + ifp->if_hwassist &= ~CSUM_IP6_TSO; + mlx5_en_err(ifp, "tso6 disabled due to -txcsum6.\n"); + } } } - if (mask & IFCAP_MEXTPG) - ifp->if_capenable ^= IFCAP_MEXTPG; - if (mask & IFCAP_TXTLS4) - ifp->if_capenable ^= IFCAP_TXTLS4; - if (mask & IFCAP_TXTLS6) - ifp->if_capenable ^= IFCAP_TXTLS6; + if (nvlist_exists_bool(nv, IFCAP_MEXTPG_NAME)) + priv->ifcap_mextpg = nvlist_get_bool(nv, + IFCAP_MEXTPG_NAME); + if (nvlist_exists_bool(nv, IFCAP_TXTLS4_NAME)) + priv->ifcap_txtls4 = nvlist_get_bool(nv, + IFCAP_TXTLS4_NAME); + if (nvlist_exists_bool(nv, IFCAP_TXTLS6_NAME)) + priv->ifcap_txtls6 = nvlist_get_bool(nv, + IFCAP_TXTLS6_NAME); #ifdef RATELIMIT - if (mask & IFCAP_TXTLS_RTLMT) - ifp->if_capenable ^= IFCAP_TXTLS_RTLMT; + if (nvlist_exists_bool(nv, IFCAP_TXTLS_RTLMT_NAME)) + priv->ifcap_txtls_rtlmt = nvlist_get_bool(nv, + IFCAP_TXTLS_RTLMT_NAME); #endif - if (mask & IFCAP_RXCSUM) - ifp->if_capenable ^= IFCAP_RXCSUM; - if (mask & IFCAP_RXCSUM_IPV6) - ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; - if (mask & IFCAP_TSO4) { - if (!(IFCAP_TSO4 & ifp->if_capenable) && - !(IFCAP_TXCSUM & ifp->if_capenable)) { - mlx5_en_err(ifp, "enable txcsum first.\n"); - error = EAGAIN; - goto out; + if (nvlist_exists_bool(nv, IFCAP_RXCSUM_NAME)) + priv->ifcap_rxcsum = nvlist_get_bool(nv, + IFCAP_RXCSUM_NAME); + if (nvlist_exists_bool(nv, IFCAP_RXCSUM_IPV6_NAME)) + priv->ifcap_rxcsum_ipv6 = nvlist_get_bool(nv, + IFCAP_RXCSUM_IPV6_NAME); + if (nvlist_exists_bool(nv, IFCAP_TSO4_NAME)) { + if (nvlist_get_bool(nv, IFCAP_TSO4_NAME)) { + if (!priv->ifcap_txcsum) { + mlx5_en_err(ifp, + "enable txcsum first.\n"); + error = EAGAIN; + goto out; + } + priv->ifcap_tso4 = true; + ifp->if_hwassist |= CSUM_IP_TSO; + } else { + priv->ifcap_tso4 = false; + ifp->if_hwassist &= ~CSUM_IP_TSO; } - ifp->if_capenable ^= IFCAP_TSO4; - ifp->if_hwassist ^= CSUM_IP_TSO; } - if (mask & IFCAP_TSO6) { - if (!(IFCAP_TSO6 & ifp->if_capenable) && - !(IFCAP_TXCSUM_IPV6 & ifp->if_capenable)) { - mlx5_en_err(ifp, "enable txcsum6 first.\n"); - error = EAGAIN; - goto out; + if (nvlist_exists_bool(nv, IFCAP_TSO6_NAME)) { + if (nvlist_get_bool(nv, IFCAP_TSO6_NAME)) { + if (!priv->ifcap_txcsum_ipv6) { + mlx5_en_err(ifp, + "enable txcsum6 first.\n"); + error = EAGAIN; + goto out; + } + priv->ifcap_tso6 = true; + ifp->if_hwassist |= CSUM_IP6_TSO; + } else { + priv->ifcap_tso6 = false; + ifp->if_hwassist &= ~CSUM_IP6_TSO; } - ifp->if_capenable ^= IFCAP_TSO6; - ifp->if_hwassist ^= CSUM_IP6_TSO; } - if (mask & IFCAP_VLAN_HWTSO) - ifp->if_capenable ^= IFCAP_VLAN_HWTSO; - if (mask & IFCAP_VLAN_HWFILTER) { - if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) + if (nvlist_exists_bool(nv, IFCAP_VLAN_HWTSO_NAME)) + priv->ifcap_vlan_hwtso = nvlist_get_bool(nv, + IFCAP_VLAN_HWTSO_NAME); + if (nvlist_exists_bool(nv, IFCAP_VLAN_HWFILTER_NAME)) { + priv->ifcap_vlan_hwfilter = nvlist_get_bool(nv, + IFCAP_VLAN_HWFILTER_NAME); + if (priv->ifcap_vlan_hwfilter) mlx5e_disable_vlan_filter(priv); else mlx5e_enable_vlan_filter(priv); - - ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; } - if (mask & IFCAP_VLAN_HWTAGGING) - ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; - if (mask & IFCAP_WOL_MAGIC) - ifp->if_capenable ^= IFCAP_WOL_MAGIC; - if (mask & IFCAP_VXLAN_HWCSUM) { + if (nvlist_exists_bool(nv, IFCAP_VLAN_HWTAGGING_NAME)) + priv->ifcap_vlan_hwtagging = nvlist_get_bool(nv, + IFCAP_VLAN_HWTAGGING_NAME); + if (nvlist_exists_bool(nv, IFCAP_VXLAN_HWCSUM_NAME)) { int was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); if (was_opened) mlx5e_close_locked(ifp); - ifp->if_capenable ^= IFCAP_VXLAN_HWCSUM; - ifp->if_hwassist ^= CSUM_INNER_IP | CSUM_INNER_IP_UDP | - CSUM_INNER_IP_TCP | CSUM_INNER_IP6_UDP | - CSUM_INNER_IP6_TCP; - if (was_opened) - mlx5e_open_locked(ifp); + if (nvlist_get_bool(nv, IFCAP_VXLAN_HWCSUM_NAME)) { + priv->ifcap_vxlan_hwcsum = true; + ifp->if_hwassist |= CSUM_INNER_IP | + CSUM_INNER_IP_UDP | CSUM_INNER_IP_TCP | + CSUM_INNER_IP6_UDP | + CSUM_INNER_IP6_TCP; + } else { + priv->ifcap_vxlan_hwcsum = false; + ifp->if_hwassist &= ~(CSUM_INNER_IP | + CSUM_INNER_IP_UDP | CSUM_INNER_IP_TCP | + CSUM_INNER_IP6_UDP | + CSUM_INNER_IP6_TCP); + } } - if (mask & IFCAP_VXLAN_HWTSO) { - ifp->if_capenable ^= IFCAP_VXLAN_HWTSO; - ifp->if_hwassist ^= CSUM_INNER_IP_TSO | - CSUM_INNER_IP6_TSO; + if (nvlist_exists_bool(nv, IFCAP_VXLAN_HWTSO_NAME)) { + if (nvlist_get_bool(nv, IFCAP_VXLAN_HWTSO_NAME)) { + priv->ifcap_vxlan_hwtso = true; + ifp->if_hwassist |= CSUM_INNER_IP_TSO | + CSUM_INNER_IP6_TSO; + } else { + priv->ifcap_vxlan_hwtso = false; + ifp->if_hwassist &= ~(CSUM_INNER_IP_TSO | + CSUM_INNER_IP6_TSO); + } } - VLAN_CAPABILITIES(ifp); /* turn off LRO means also turn of HW LRO - if it's on */ - if (mask & IFCAP_LRO) { + if (nvlist_exists_bool(nv, IFCAP_LRO_NAME)) { int was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); bool need_restart = false; - ifp->if_capenable ^= IFCAP_LRO; - - /* figure out if updating HW LRO is needed */ - if (!(ifp->if_capenable & IFCAP_LRO)) { + if (nvlist_get_bool(nv, IFCAP_LRO_NAME)) { + priv->ifcap_lro = true; if (priv->params.hw_lro_en) { - priv->params.hw_lro_en = false; + priv->params.hw_lro_en = true; need_restart = true; } } else { - if (priv->params.hw_lro_en == false && - priv->params_ethtool.hw_lro != 0) { - priv->params.hw_lro_en = true; + priv->ifcap_lro = false; + if (priv->params.hw_lro_en) { + priv->params.hw_lro_en = false; need_restart = true; } } @@ -3489,16 +3622,19 @@ mlx5e_open_locked(ifp); } } - if (mask & IFCAP_HWRXTSTMP) { - ifp->if_capenable ^= IFCAP_HWRXTSTMP; - if (ifp->if_capenable & IFCAP_HWRXTSTMP) { + if (nvlist_exists_bool(nv, IFCAP_HWRXTSTMP_NAME)) { + if (nvlist_get_bool(nv, IFCAP_HWRXTSTMP_NAME)) { + priv->ifcap_hwrxtstmp = true; if (priv->clbr_done == 0) mlx5e_reset_calibration_callout(priv); } else { + priv->ifcap_hwrxtstmp = false; callout_drain(&priv->tstmp_clbr); priv->clbr_done = 0; } } + ifp->if_capenable = mlx5e_if_caps(priv); + VLAN_CAPABILITIES(ifp); out: PRIV_UNLOCK(priv); break; @@ -4387,19 +4523,33 @@ /* * Set driver features */ - ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6; - ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; - ifp->if_capabilities |= IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWFILTER; - ifp->if_capabilities |= IFCAP_LINKSTATE | IFCAP_JUMBO_MTU; - ifp->if_capabilities |= IFCAP_LRO; - ifp->if_capabilities |= IFCAP_TSO | IFCAP_VLAN_HWTSO; - ifp->if_capabilities |= IFCAP_HWSTATS | IFCAP_HWRXTSTMP; - ifp->if_capabilities |= IFCAP_MEXTPG; - ifp->if_capabilities |= IFCAP_TXTLS4 | IFCAP_TXTLS6; + priv->ifcap_rxcsum = true; + priv->ifcap_txcsum = true; + priv->ifcap_vlan_mtu = true; + priv->ifcap_vlan_hwtagging = true; + priv->ifcap_jumbo_mtu = true; + priv->ifcap_vlan_hwcsum = true; + priv->ifcap_tso4 = true; + priv->ifcap_tso6 = true; + priv->ifcap_lro = true; + priv->ifcap_vlan_hwfilter = true; + priv->ifcap_vlan_hwtso = true; + priv->ifcap_linkstate = true; + priv->ifcap_rxcsum_ipv6 = true; + priv->ifcap_txcsum_ipv6 = true; + priv->ifcap_hwstats_name = true; +#ifdef RATELIMIT + priv->ifcap_txrtlmt = true; +#endif + priv->ifcap_hwrxtstmp = true; + priv->ifcap_mextpg = true; + priv->ifcap_txtls4 = true; + priv->ifcap_txtls6 = true; + priv->ifcap_vxlan_hwcsum = true; + priv->ifcap_vxlan_hwtso = true; #ifdef RATELIMIT - ifp->if_capabilities |= IFCAP_TXRTLMT | IFCAP_TXTLS_RTLMT; + priv->ifcap_txtls_rtlmt = true; #endif - ifp->if_capabilities |= IFCAP_VXLAN_HWCSUM | IFCAP_VXLAN_HWTSO; ifp->if_snd_tag_alloc = mlx5e_snd_tag_alloc; #ifdef RATELIMIT ifp->if_ratelimit_query = mlx5e_ratelimit_query; @@ -4409,7 +4559,7 @@ ifp->if_hw_tsomaxsegcount = MLX5E_MAX_TX_MBUF_FRAGS - 1 /* hdr */; ifp->if_hw_tsomaxsegsize = MLX5E_MAX_TX_MBUF_SIZE; - ifp->if_capenable = ifp->if_capabilities; + ifp->if_capenable = ifp->if_capabilities = mlx5e_if_caps(priv); ifp->if_hwassist = 0; if (ifp->if_capenable & IFCAP_TSO) ifp->if_hwassist |= CSUM_TSO; diff --git a/sys/net/if.h b/sys/net/if.h --- a/sys/net/if.h +++ b/sys/net/if.h @@ -236,7 +236,7 @@ #define IFCAP_TOE4 0x04000 /* interface can offload TCP */ #define IFCAP_TOE6 0x08000 /* interface can offload TCP6 */ #define IFCAP_VLAN_HWFILTER 0x10000 /* interface hw can filter vlan tag */ -/* available 0x20000 */ +#define IFCAP_NV 0x20000 /* can do SIOCGIFCAPNV/SIOCSIFCAPNV */ #define IFCAP_VLAN_HWTSO 0x40000 /* can do IFCAP_TSO on VLANs */ #define IFCAP_LINKSTATE 0x80000 /* the runtime link state is dynamic */ #define IFCAP_NETMAP 0x100000 /* netmap mode supported/enabled */ @@ -260,7 +260,40 @@ #define IFCAP_TOE (IFCAP_TOE4 | IFCAP_TOE6) #define IFCAP_TXTLS (IFCAP_TXTLS4 | IFCAP_TXTLS6) -#define IFCAP_CANTCHANGE (IFCAP_NETMAP) +#define IFCAP_CANTCHANGE (IFCAP_NETMAP | IFCAP_NV) +#define IFCAP_ALLCAPS 0xffffffff + +#define IFCAP_RXCSUM_NAME "RXCSUM" +#define IFCAP_TXCSUM_NAME "TXCSUM" +#define IFCAP_NETCONS_NAME "NETCONS" +#define IFCAP_VLAN_MTU_NAME "VLAN_MTU" +#define IFCAP_VLAN_HWTAGGING_NAME "VLAN_HWTAGGING" +#define IFCAP_JUMBO_MTU_NAME "JUMBO_MTU" +#define IFCAP_POLLING_NAME "POLLING" +#define IFCAP_VLAN_HWCSUM_NAME "VLAN_HWCSUM" +#define IFCAP_TSO4_NAME "TSO4" +#define IFCAP_TSO6_NAME "TSO6" +#define IFCAP_LRO_NAME "LRO" +#define IFCAP_WOL_UCAST_NAME "WOL_UCAST" +#define IFCAP_WOL_MCAST_NAME "WOL_MCAST" +#define IFCAP_WOL_MAGIC_NAME "WOL_MAGIC" +#define IFCAP_TOE4_NAME "TOE4" +#define IFCAP_TOE6_NAME "TOE6" +#define IFCAP_VLAN_HWFILTER_NAME "VLAN_HWFILTER" +#define IFCAP_VLAN_HWTSO_NAME "VLAN_HWTSO" +#define IFCAP_LINKSTATE_NAME "LINKSTATE" +#define IFCAP_NETMAP_NAME "NETMAP" +#define IFCAP_RXCSUM_IPV6_NAME "RXCSUM_IPV6" +#define IFCAP_TXCSUM_IPV6_NAME "TXCSUM_IPV6" +#define IFCAP_HWSTATS_NAME "HWSTATS" +#define IFCAP_TXRTLMT_NAME "TXRTLMT" +#define IFCAP_HWRXTSTMP_NAME "HWRXTSTMP" +#define IFCAP_MEXTPG_NAME "MEXTPG" +#define IFCAP_TXTLS4_NAME "TXTLS4" +#define IFCAP_TXTLS6_NAME "TXTLS6" +#define IFCAP_VXLAN_HWCSUM_NAME "VXLAN_HWCSUM" +#define IFCAP_VXLAN_HWTSO_NAME "VXLAN_HWTSO" +#define IFCAP_TXTLS_RTLMT_NAME "TXTLS_RTLMT" #define IFQ_MAXLEN 50 #define IFNET_SLOWHZ 1 /* granularity is 1 second */ @@ -387,6 +420,15 @@ void *buffer; }; +struct ifreq_nv_req { + u_int buf_length; /* Total size of buffer, + u_int for ABI struct ifreq */ + u_int length; /* Length of the filled part */ + void *buffer; /* Buffer itself, containing packed nv */ +}; + +#define IFR_CAP_NV_MAXBUFSIZE (2 * 1024 * 1024) + /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter @@ -411,6 +453,7 @@ int ifru_cap[2]; u_int ifru_fib; u_char ifru_vlan_pcp; + struct ifreq_nv_req ifru_nv; } ifr_ifru; #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ @@ -434,6 +477,7 @@ #define ifr_fib ifr_ifru.ifru_fib /* interface fib */ #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 _SIZEOF_ADDR_IFREQ(ifr) \ @@ -605,6 +649,10 @@ extern struct sx ifnet_detach_sxlock; +struct nvlist; +int if_capnv_to_capint(const struct nvlist *nv, bool all); +void if_capint_to_capnv(struct nvlist *nv, int ifr_cap, int ifr_req); + #endif #ifndef _KERNEL diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -2461,6 +2462,78 @@ return (ifrup->ifr.ifr_ifru.ifru_data); } +struct ifcap_nv_bit_name { + int cap_bit; + const char *cap_name; +}; +#define CAPNV(x) {.cap_bit = IFCAP_##x, \ + .cap_name = __CONCAT(IFCAP_, __CONCAT(x, _NAME)) } +const struct ifcap_nv_bit_name ifcap_nv_bit_names[] = { + CAPNV(RXCSUM), + CAPNV(TXCSUM), + CAPNV(NETCONS), + CAPNV(VLAN_MTU), + CAPNV(VLAN_HWTAGGING), + CAPNV(JUMBO_MTU), + CAPNV(POLLING), + CAPNV(VLAN_HWCSUM), + CAPNV(TSO4), + CAPNV(TSO6), + CAPNV(LRO), + CAPNV(WOL_UCAST), + CAPNV(WOL_MCAST), + CAPNV(WOL_MAGIC), + CAPNV(TOE4), + CAPNV(TOE6), + CAPNV(VLAN_HWFILTER), + CAPNV(VLAN_HWTSO), + CAPNV(LINKSTATE), + CAPNV(NETMAP), + CAPNV(RXCSUM_IPV6), + CAPNV(TXCSUM_IPV6), + CAPNV(HWSTATS), + CAPNV(TXRTLMT), + CAPNV(HWRXTSTMP), + CAPNV(MEXTPG), + CAPNV(TXTLS4), + CAPNV(TXTLS6), + CAPNV(VXLAN_HWCSUM), + CAPNV(VXLAN_HWTSO), + CAPNV(TXTLS_RTLMT), +}; +#undef CAPNV + +int +if_capnv_to_capint(const nvlist_t *nv, bool all) +{ + const struct ifcap_nv_bit_name *nn; + int i, res; + + res = 0; + for (i = 0; i < nitems(ifcap_nv_bit_names); i++) { + nn = &ifcap_nv_bit_names[i]; + if (nvlist_exists_bool(nv, nn->cap_name) && (all || + nvlist_get_bool(nv, nn->cap_name))) + res |= nn->cap_bit; + } + return (res); +} + +void +if_capint_to_capnv(nvlist_t *nv, int ifr_cap, int ifr_req) +{ + const struct ifcap_nv_bit_name *nn; + int i; + + for (i = 0; i < nitems(ifcap_nv_bit_names); i++) { + nn = &ifcap_nv_bit_names[i]; + if ((nn->cap_bit & ifr_cap) != 0) { + nvlist_add_bool(nv, nn->cap_name, + (nn->cap_bit & ifr_req) != 0); + } + } +} + /* * Hardware specific interface ioctls. */ @@ -2471,12 +2544,14 @@ int error = 0, do_ifup = 0; int new_flags, temp_flags; size_t namelen, onamelen; - size_t descrlen; + size_t descrlen, nvbuflen; char *descrbuf, *odescrbuf; char new_name[IFNAMSIZ]; char old_name[IFNAMSIZ], strbuf[IFNAMSIZ + 8]; struct ifaddr *ifa; struct sockaddr_dl *sdl; + void *buf; + nvlist_t *nvcap; ifr = (struct ifreq *)data; switch (cmd) { @@ -2491,8 +2566,64 @@ break; case SIOCGIFCAP: - ifr->ifr_reqcap = ifp->if_capabilities; - ifr->ifr_curcap = ifp->if_capenable; + if ((ifp->if_capabilities & IFCAP_NV) == 0) { + ifr->ifr_reqcap = ifp->if_capabilities; + ifr->ifr_curcap = ifp->if_capenable; + break; + } + nvcap = nvlist_create(0); + error = (*ifp->if_ioctl)(ifp, SIOCGIFCAPNV, + __DECONST(caddr_t, nvcap)); + while (error == 0) { + error = nvlist_error(nvcap); + if (error != 0) { + if_printf(ifp, + "SIOCGIFPCAPNV driver mistake: nvlist error %d\n", + error); + break; + } + ifr->ifr_reqcap = if_capnv_to_capint(nvcap, true); + ifr->ifr_curcap = if_capnv_to_capint(nvcap, false); + break; + } + nvlist_destroy(nvcap); + break; + + case SIOCGIFCAPNV: + if ((ifp->if_capabilities & IFCAP_NV) != 0) { + error = EINVAL; + break; + } + buf = NULL; + nvcap = nvlist_create(0); + for (;;) { + error = (*ifp->if_ioctl)(ifp, SIOCGIFCAPNV, + __DECONST(caddr_t, nvcap)); + if (error != 0) + break; + if (error != 0) { + if_printf(ifp, + "SIOCSIFCAPNV driver mistake: nvlist error %d\n", + error); + break; + } + buf = nvlist_pack(nvcap, &nvbuflen); + if (buf == NULL) { + error = EDOOFUS; + break; + } + if (nvbuflen > ifr->ifr_cap_nv.buf_length) { + ifr->ifr_cap_nv.length = nvbuflen; + ifr->ifr_cap_nv.buffer = NULL; + error = EFBIG; + break; + } + ifr->ifr_cap_nv.length = nvbuflen; + error = copyout(buf, ifr->ifr_cap_nv.buffer, nvbuflen); + break; + } + free(buf, M_NVLIST); + nvlist_destroy(nvcap); break; case SIOCGIFDATA: @@ -2633,13 +2764,60 @@ case SIOCSIFCAP: error = priv_check(td, PRIV_NET_SETIFCAP); - if (error) + if (error != 0) + return (error); + if (ifp->if_ioctl == NULL) + return (EOPNOTSUPP); + if ((ifp->if_capabilities & IFCAP_NV) == 0) { + if (ifr->ifr_reqcap & ~ifp->if_capabilities) { + error = EINVAL; + break; + } + error = (*ifp->if_ioctl)(ifp, cmd, data); + break; + } else { + nvcap = nvlist_create(0); + for (;;) { + if_capint_to_capnv(nvcap, IFCAP_ALLCAPS, + ifr->ifr_reqcap); + error = (*ifp->if_ioctl)(ifp, SIOCSIFCAPNV, + __DECONST(caddr_t, nvcap)); + break; + } + nvlist_destroy(nvcap); + } + if (error == 0) + getmicrotime(&ifp->if_lastchange); + break; + + case SIOCSIFCAPNV: + error = priv_check(td, PRIV_NET_SETIFCAP); + if (error != 0) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); - if (ifr->ifr_reqcap & ~ifp->if_capabilities) + if ((ifp->if_capabilities & IFCAP_NV) == 0) return (EINVAL); - error = (*ifp->if_ioctl)(ifp, cmd, data); + if (ifr->ifr_cap_nv.length > IFR_CAP_NV_MAXBUFSIZE) + return (EINVAL); + nvcap = NULL; + buf = malloc(ifr->ifr_cap_nv.length, M_TEMP, M_WAITOK); + for (;;) { + error = copyin(ifr->ifr_cap_nv.buffer, buf, + ifr->ifr_cap_nv.length); + if (error != 0) + break; + nvcap = nvlist_unpack(buf, ifr->ifr_cap_nv.length, 0); + if (nvcap == NULL) { + error = EINVAL; + break; + } + error = (*ifp->if_ioctl)(ifp, cmd, + __DECONST(caddr_t, nvcap)); + break; + } + nvlist_destroy(nvcap); + free(buf, M_TEMP); if (error == 0) getmicrotime(&ifp->if_lastchange); break; diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h --- a/sys/sys/sockio.h +++ b/sys/sys/sockio.h @@ -147,4 +147,7 @@ #define SIOCGIFDOWNREASON _IOWR('i', 154, struct ifdownreason) +#define SIOCSIFCAPNV _IOW('i', 155, struct ifreq) /* set IF features */ +#define SIOCGIFCAPNV _IOWR('i', 156, struct ifreq) /* get IF features */ + #endif /* !_SYS_SOCKIO_H_ */