nfp: add offloads on representors
authorJakub Kicinski <jakub.kicinski@netronome.com>
Wed, 28 Nov 2018 06:24:56 +0000 (22:24 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 30 Nov 2018 21:30:44 +0000 (13:30 -0800)
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: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: John Hurley <john.hurley@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/nfp_app.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.h

index 1175539143421a40608755037d4f0bc6453b6f00..3a973282b2bb78aecf29552d3e215809510ec8d0 100644 (file)
@@ -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;
index f2aaef976c7def6d86707f082f4907a1d035167b..d26ea32df8d9cf36983b4d7effe039c8e28acaaf 100644 (file)
@@ -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;
index ccec69944de5b5bfaec14eb51d8aa7f17cb0753e..166d7f71442e5fa765c7dfb8891a99d7cb774cad 100644 (file)
  * 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
 #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,
index 0a1d761c6692f982c413de6f022102d4e7b394d6..69d7aebda09bd34aebc3563868c898d7a2228c4f 100644 (file)
@@ -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 @@ err_port_disable:
        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;
 
index c412b94bfb97a47f29a5516db1221794663fa2a1..e0f13dfe1f39b5e89eb89edce7d4d233788ecc53 100644 (file)
@@ -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);