Commit 51a6588e authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

nfp: add offloads on representors

FW/HW can generally support the standard networking offloads
on representors without any trouble.  Add the ability for FW
to advertise which features should be available on representors.

Because representors are muxed on top of the vNIC we need to listen
on feature changes of their lower devices, and update their features
appropriately.
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: default avatarJohn Hurley <john.hurley@netronome.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 71844fac
......@@ -138,6 +138,38 @@ nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type,
return old;
}
static void
nfp_app_netdev_feat_change(struct nfp_app *app, struct net_device *netdev)
{
struct nfp_net *nn;
unsigned int type;
if (!nfp_netdev_is_nfp_net(netdev))
return;
nn = netdev_priv(netdev);
if (nn->app != app)
return;
for (type = 0; type < __NFP_REPR_TYPE_MAX; type++) {
struct nfp_reprs *reprs;
unsigned int i;
reprs = rtnl_dereference(app->reprs[type]);
if (!reprs)
continue;
for (i = 0; i < reprs->num_reprs; i++) {
struct net_device *repr;
repr = rtnl_dereference(reprs->reprs[i]);
if (!repr)
continue;
nfp_repr_transfer_features(repr, netdev);
}
}
}
static int
nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr)
{
......@@ -147,6 +179,14 @@ nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr)
netdev = netdev_notifier_info_to_dev(ptr);
app = container_of(nb, struct nfp_app, netdev_nb);
/* Handle events common code is interested in */
switch (event) {
case NETDEV_FEAT_CHANGE:
nfp_app_netdev_feat_change(app, netdev);
break;
}
/* Call offload specific handlers */
if (app->type->netdev_event)
return app->type->netdev_event(app, netdev, event, ptr);
return NOTIFY_DONE;
......
......@@ -90,6 +90,15 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
offset, length);
break;
case NFP_NET_CFG_TLV_TYPE_REPR_CAP:
if (length < 4) {
dev_err(dev, "REPR CAP TLV short %dB < 4B\n",
length);
return -EINVAL;
}
caps->repr_cap = readl(data);
break;
default:
if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
break;
......
......@@ -466,6 +466,10 @@
* Variable, experimental IDs. IDs designated for internal development and
* experiments before a stable TLV ID has been allocated to a feature. Should
* never be present in production firmware.
*
* %NFP_NET_CFG_TLV_TYPE_REPR_CAP:
* Single word, equivalent of %NFP_NET_CFG_CAP for representors, features which
* can be used on representors.
*/
#define NFP_NET_CFG_TLV_TYPE_UNKNOWN 0
#define NFP_NET_CFG_TLV_TYPE_RESERVED 1
......@@ -474,6 +478,7 @@
#define NFP_NET_CFG_TLV_TYPE_MBOX 4
#define NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL0 5
#define NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL1 6
#define NFP_NET_CFG_TLV_TYPE_REPR_CAP 7
struct device;
......@@ -482,11 +487,13 @@ struct device;
* @me_freq_mhz: ME clock_freq (MHz)
* @mbox_off: vNIC mailbox area offset
* @mbox_len: vNIC mailbox area length
* @repr_cap: capabilities for representors
*/
struct nfp_net_tlv_caps {
u32 me_freq_mhz;
unsigned int mbox_off;
unsigned int mbox_len;
u32 repr_cap;
};
int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
......
......@@ -11,6 +11,7 @@
#include "nfpcore/nfp_nsp.h"
#include "nfp_app.h"
#include "nfp_main.h"
#include "nfp_net.h"
#include "nfp_net_ctrl.h"
#include "nfp_net_repr.h"
#include "nfp_net_sriov.h"
......@@ -231,6 +232,27 @@ static int nfp_repr_open(struct net_device *netdev)
return err;
}
static netdev_features_t
nfp_repr_fix_features(struct net_device *netdev, netdev_features_t features)
{
struct nfp_repr *repr = netdev_priv(netdev);
netdev_features_t old_features = features;
netdev_features_t lower_features;
struct net_device *lower_dev;
lower_dev = repr->dst->u.port_info.lower_dev;
lower_features = lower_dev->features;
if (lower_features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
lower_features |= NETIF_F_HW_CSUM;
features = netdev_intersect_features(features, lower_features);
features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_HW_TC);
features |= NETIF_F_LLTX;
return features;
}
const struct net_device_ops nfp_repr_netdev_ops = {
.ndo_init = nfp_app_ndo_init,
.ndo_uninit = nfp_app_ndo_uninit,
......@@ -248,10 +270,25 @@ const struct net_device_ops nfp_repr_netdev_ops = {
.ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk,
.ndo_get_vf_config = nfp_app_get_vf_config,
.ndo_set_vf_link_state = nfp_app_set_vf_link_state,
.ndo_fix_features = nfp_repr_fix_features,
.ndo_set_features = nfp_port_set_features,
.ndo_set_mac_address = eth_mac_addr,
};
void
nfp_repr_transfer_features(struct net_device *netdev, struct net_device *lower)
{
struct nfp_repr *repr = netdev_priv(netdev);
if (repr->dst->u.port_info.lower_dev != lower)
return;
netdev->gso_max_size = lower->gso_max_size;
netdev->gso_max_segs = lower->gso_max_segs;
netdev_update_features(netdev);
}
static void nfp_repr_clean(struct nfp_repr *repr)
{
unregister_netdev(repr->netdev);
......@@ -281,6 +318,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
struct net_device *pf_netdev)
{
struct nfp_repr *repr = netdev_priv(netdev);
struct nfp_net *nn = netdev_priv(pf_netdev);
u32 repr_cap = nn->tlv_caps.repr_cap;
int err;
nfp_repr_set_lockdep_class(netdev);
......@@ -299,6 +338,52 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops);
/* Set features the lower device can support with representors */
if (repr_cap & NFP_NET_CFG_CTRL_LIVE_ADDR)
netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
netdev->hw_features = NETIF_F_HIGHDMA;
if (repr_cap & NFP_NET_CFG_CTRL_RXCSUM_ANY)
netdev->hw_features |= NETIF_F_RXCSUM;
if (repr_cap & NFP_NET_CFG_CTRL_TXCSUM)
netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
if (repr_cap & NFP_NET_CFG_CTRL_GATHER)
netdev->hw_features |= NETIF_F_SG;
if ((repr_cap & NFP_NET_CFG_CTRL_LSO && nn->fw_ver.major > 2) ||
repr_cap & NFP_NET_CFG_CTRL_LSO2)
netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
if (repr_cap & NFP_NET_CFG_CTRL_RSS_ANY)
netdev->hw_features |= NETIF_F_RXHASH;
if (repr_cap & NFP_NET_CFG_CTRL_VXLAN) {
if (repr_cap & NFP_NET_CFG_CTRL_LSO)
netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
}
if (repr_cap & NFP_NET_CFG_CTRL_NVGRE) {
if (repr_cap & NFP_NET_CFG_CTRL_LSO)
netdev->hw_features |= NETIF_F_GSO_GRE;
}
if (repr_cap & (NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE))
netdev->hw_enc_features = netdev->hw_features;
netdev->vlan_features = netdev->hw_features;
if (repr_cap & NFP_NET_CFG_CTRL_RXVLAN)
netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
if (repr_cap & NFP_NET_CFG_CTRL_TXVLAN) {
if (repr_cap & NFP_NET_CFG_CTRL_LSO2)
netdev_warn(netdev, "Device advertises both TSO2 and TXVLAN. Refusing to enable TXVLAN.\n");
else
netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
}
if (repr_cap & NFP_NET_CFG_CTRL_CTAG_FILTER)
netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
netdev->features = netdev->hw_features;
/* Advertise but disable TSO by default. */
netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
netdev->gso_max_segs = NFP_NET_LSO_MAX_SEGS;
netdev->priv_flags |= IFF_NO_QUEUE;
netdev->features |= NETIF_F_LLTX;
......
......@@ -92,6 +92,8 @@ nfp_repr_get_locked(struct nfp_app *app, struct nfp_reprs *set,
unsigned int id);
void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len);
void
nfp_repr_transfer_features(struct net_device *netdev, struct net_device *lower);
int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
u32 cmsg_port_id, struct nfp_port *port,
struct net_device *pf_netdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment