diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/dpaa2')
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 92 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 25 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 100 |
4 files changed, 181 insertions, 37 deletions
diff --git a/drivers/net/ethernet/freescale/dpaa2/Kconfig b/drivers/net/ethernet/freescale/dpaa2/Kconfig index f6d244c663fd..8bd384720f80 100644 --- a/drivers/net/ethernet/freescale/dpaa2/Kconfig +++ b/drivers/net/ethernet/freescale/dpaa2/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config FSL_DPAA2_ETH tristate "Freescale DPAA2 Ethernet" depends on FSL_MC_BUS && FSL_MC_DPIO diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index dc339dc1adb2..7d2390e3df77 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -435,7 +435,7 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, percpu_stats->rx_packets++; percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); - napi_gro_receive(&ch->napi, skb); + list_add_tail(&skb->list, ch->rx_list); return; @@ -1113,12 +1113,16 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) struct dpaa2_eth_fq *fq, *txc_fq = NULL; struct netdev_queue *nq; int store_cleaned, work_done; + struct list_head rx_list; int err; ch = container_of(napi, struct dpaa2_eth_channel, napi); ch->xdp.res = 0; priv = ch->priv; + INIT_LIST_HEAD(&rx_list); + ch->rx_list = &rx_list; + do { err = pull_channel(ch); if (unlikely(err)) @@ -1162,6 +1166,8 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) work_done = max(rx_cleaned, 1); out: + netif_receive_skb_list(ch->rx_list); + if (txc_fq && txc_fq->dq_frames) { nq = netdev_get_tx_queue(priv->net_dev, txc_fq->flowid); netdev_tx_completed_queue(nq, txc_fq->dq_frames, @@ -1966,7 +1972,7 @@ alloc_channel(struct dpaa2_eth_priv *priv) channel->dpcon = setup_dpcon(priv); if (IS_ERR_OR_NULL(channel->dpcon)) { - err = PTR_ERR(channel->dpcon); + err = PTR_ERR_OR_ZERO(channel->dpcon); goto err_setup; } @@ -2022,7 +2028,7 @@ static int setup_dpio(struct dpaa2_eth_priv *priv) /* Try to allocate a channel */ channel = alloc_channel(priv); if (IS_ERR_OR_NULL(channel)) { - err = PTR_ERR(channel); + err = PTR_ERR_OR_ZERO(channel); if (err != -EPROBE_DEFER) dev_info(dev, "No affine channel for cpu %d and above\n", i); @@ -2565,10 +2571,12 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = { .rxnfc_field = RXH_L2DA, .cls_prot = NET_PROT_ETH, .cls_field = NH_FLD_ETH_DA, + .id = DPAA2_ETH_DIST_ETHDST, .size = 6, }, { .cls_prot = NET_PROT_ETH, .cls_field = NH_FLD_ETH_SA, + .id = DPAA2_ETH_DIST_ETHSRC, .size = 6, }, { /* This is the last ethertype field parsed: @@ -2577,28 +2585,33 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = { */ .cls_prot = NET_PROT_ETH, .cls_field = NH_FLD_ETH_TYPE, + .id = DPAA2_ETH_DIST_ETHTYPE, .size = 2, }, { /* VLAN header */ .rxnfc_field = RXH_VLAN, .cls_prot = NET_PROT_VLAN, .cls_field = NH_FLD_VLAN_TCI, + .id = DPAA2_ETH_DIST_VLAN, .size = 2, }, { /* IP header */ .rxnfc_field = RXH_IP_SRC, .cls_prot = NET_PROT_IP, .cls_field = NH_FLD_IP_SRC, + .id = DPAA2_ETH_DIST_IPSRC, .size = 4, }, { .rxnfc_field = RXH_IP_DST, .cls_prot = NET_PROT_IP, .cls_field = NH_FLD_IP_DST, + .id = DPAA2_ETH_DIST_IPDST, .size = 4, }, { .rxnfc_field = RXH_L3_PROTO, .cls_prot = NET_PROT_IP, .cls_field = NH_FLD_IP_PROTO, + .id = DPAA2_ETH_DIST_IPPROTO, .size = 1, }, { /* Using UDP ports, this is functionally equivalent to raw @@ -2607,11 +2620,13 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = { .rxnfc_field = RXH_L4_B_0_1, .cls_prot = NET_PROT_UDP, .cls_field = NH_FLD_UDP_PORT_SRC, + .id = DPAA2_ETH_DIST_L4SRC, .size = 2, }, { .rxnfc_field = RXH_L4_B_2_3, .cls_prot = NET_PROT_UDP, .cls_field = NH_FLD_UDP_PORT_DST, + .id = DPAA2_ETH_DIST_L4DST, .size = 2, }, }; @@ -2677,12 +2692,15 @@ static int config_cls_key(struct dpaa2_eth_priv *priv, dma_addr_t key) } /* Size of the Rx flow classification key */ -int dpaa2_eth_cls_key_size(void) +int dpaa2_eth_cls_key_size(u64 fields) { int i, size = 0; - for (i = 0; i < ARRAY_SIZE(dist_fields); i++) + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + if (!(fields & dist_fields[i].id)) + continue; size += dist_fields[i].size; + } return size; } @@ -2703,6 +2721,24 @@ int dpaa2_eth_cls_fld_off(int prot, int field) return 0; } +/* Prune unused fields from the classification rule. + * Used when masking is not supported + */ +void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields) +{ + int off = 0, new_off = 0; + int i, size; + + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + size = dist_fields[i].size; + if (dist_fields[i].id & fields) { + memcpy(key_mem + new_off, key_mem + off, size); + new_off += size; + } + off += size; + } +} + /* Set Rx distribution (hash or flow classification) key * flags is a combination of RXH_ bits */ @@ -2724,14 +2760,13 @@ static int dpaa2_eth_set_dist_key(struct net_device *net_dev, struct dpkg_extract *key = &cls_cfg.extracts[cls_cfg.num_extracts]; - /* For Rx hashing key we set only the selected fields. - * For Rx flow classification key we set all supported fields + /* For both Rx hashing and classification keys + * we set only the selected fields. */ - if (type == DPAA2_ETH_RX_DIST_HASH) { - if (!(flags & dist_fields[i].rxnfc_field)) - continue; + if (!(flags & dist_fields[i].id)) + continue; + if (type == DPAA2_ETH_RX_DIST_HASH) rx_hash_fields |= dist_fields[i].rxnfc_field; - } if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) { dev_err(dev, "error adding key extraction rule, too many rules?\n"); @@ -2786,16 +2821,28 @@ free_key: int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u64 key = 0; + int i; if (!dpaa2_eth_hash_enabled(priv)) return -EOPNOTSUPP; - return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, flags); + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) + if (dist_fields[i].rxnfc_field & flags) + key |= dist_fields[i].id; + + return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, key); +} + +int dpaa2_eth_set_cls(struct net_device *net_dev, u64 flags) +{ + return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_CLS, flags); } -static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv) +static int dpaa2_eth_set_default_cls(struct dpaa2_eth_priv *priv) { struct device *dev = priv->net_dev->dev.parent; + int err; /* Check if we actually support Rx flow classification */ if (dpaa2_eth_has_legacy_dist(priv)) { @@ -2803,8 +2850,7 @@ static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv) return -EOPNOTSUPP; } - if (priv->dpni_attrs.options & DPNI_OPT_NO_FS || - !(priv->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING)) { + if (!dpaa2_eth_fs_enabled(priv)) { dev_dbg(dev, "Rx cls disabled in DPNI options\n"); return -EOPNOTSUPP; } @@ -2814,9 +2860,21 @@ static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv) return -EOPNOTSUPP; } + /* If there is no support for masking in the classification table, + * we don't set a default key, as it will depend on the rules + * added by the user at runtime. + */ + if (!dpaa2_eth_fs_mask_enabled(priv)) + goto out; + + err = dpaa2_eth_set_cls(priv->net_dev, DPAA2_ETH_DIST_ALL); + if (err) + return err; + +out: priv->rx_cls_enabled = 1; - return dpaa2_eth_set_dist_key(priv->net_dev, DPAA2_ETH_RX_DIST_CLS, 0); + return 0; } /* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs, @@ -2851,7 +2909,7 @@ static int bind_dpni(struct dpaa2_eth_priv *priv) /* Configure the flow classification key; it includes all * supported header fields and cannot be modified at runtime */ - err = dpaa2_eth_set_cls(priv); + err = dpaa2_eth_set_default_cls(priv); if (err && err != -EOPNOTSUPP) dev_err(dev, "Failed to configure Rx classification key\n"); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 7879622aa3e6..e180d5a68c98 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -334,6 +334,7 @@ struct dpaa2_eth_channel { struct dpaa2_eth_ch_stats stats; struct dpaa2_eth_ch_xdp xdp; struct xdp_rxq_info xdp_rxq; + struct list_head *rx_list; }; struct dpaa2_eth_dist_fields { @@ -341,6 +342,7 @@ struct dpaa2_eth_dist_fields { enum net_prot cls_prot; int cls_field; int size; + u64 id; }; struct dpaa2_eth_cls_rule { @@ -393,6 +395,7 @@ struct dpaa2_eth_priv { /* enabled ethtool hashing bits */ u64 rx_hash_fields; + u64 rx_cls_fields; struct dpaa2_eth_cls_rule *cls_rules; u8 rx_cls_enabled; struct bpf_prog *xdp_prog; @@ -436,6 +439,12 @@ static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv, (dpaa2_eth_cmp_dpni_ver((priv), DPNI_RX_DIST_KEY_VER_MAJOR, \ DPNI_RX_DIST_KEY_VER_MINOR) < 0) +#define dpaa2_eth_fs_enabled(priv) \ + (!((priv)->dpni_attrs.options & DPNI_OPT_NO_FS)) + +#define dpaa2_eth_fs_mask_enabled(priv) \ + ((priv)->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING) + #define dpaa2_eth_fs_count(priv) \ ((priv)->dpni_attrs.fs_entries) @@ -448,6 +457,18 @@ enum dpaa2_eth_rx_dist { DPAA2_ETH_RX_DIST_CLS }; +/* Unique IDs for the supported Rx classification header fields */ +#define DPAA2_ETH_DIST_ETHDST BIT(0) +#define DPAA2_ETH_DIST_ETHSRC BIT(1) +#define DPAA2_ETH_DIST_ETHTYPE BIT(2) +#define DPAA2_ETH_DIST_VLAN BIT(3) +#define DPAA2_ETH_DIST_IPSRC BIT(4) +#define DPAA2_ETH_DIST_IPDST BIT(5) +#define DPAA2_ETH_DIST_IPPROTO BIT(6) +#define DPAA2_ETH_DIST_L4SRC BIT(7) +#define DPAA2_ETH_DIST_L4DST BIT(8) +#define DPAA2_ETH_DIST_ALL (~0ULL) + static inline unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv, struct sk_buff *skb) @@ -482,7 +503,9 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv) } int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags); -int dpaa2_eth_cls_key_size(void); +int dpaa2_eth_set_cls(struct net_device *net_dev, u64 key); +int dpaa2_eth_cls_key_size(u64 key); int dpaa2_eth_cls_fld_off(int prot, int field); +void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields); #endif /* __DPAA2_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 591dfcf76adb..7b182f4b263c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -4,6 +4,7 @@ */ #include <linux/net_tstamp.h> +#include <linux/nospec.h> #include "dpni.h" /* DPNI_LINK_OPT_* */ #include "dpaa2-eth.h" @@ -264,7 +265,7 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, } static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask, - void *key, void *mask) + void *key, void *mask, u64 *fields) { int off; @@ -272,18 +273,21 @@ static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask, off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE); *(__be16 *)(key + off) = eth_value->h_proto; *(__be16 *)(mask + off) = eth_mask->h_proto; + *fields |= DPAA2_ETH_DIST_ETHTYPE; } if (!is_zero_ether_addr(eth_mask->h_source)) { off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_SA); ether_addr_copy(key + off, eth_value->h_source); ether_addr_copy(mask + off, eth_mask->h_source); + *fields |= DPAA2_ETH_DIST_ETHSRC; } if (!is_zero_ether_addr(eth_mask->h_dest)) { off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA); ether_addr_copy(key + off, eth_value->h_dest); ether_addr_copy(mask + off, eth_mask->h_dest); + *fields |= DPAA2_ETH_DIST_ETHDST; } return 0; @@ -291,7 +295,7 @@ static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask, static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value, struct ethtool_usrip4_spec *uip_mask, - void *key, void *mask) + void *key, void *mask, u64 *fields) { int off; u32 tmp_value, tmp_mask; @@ -303,18 +307,21 @@ static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value, off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC); *(__be32 *)(key + off) = uip_value->ip4src; *(__be32 *)(mask + off) = uip_mask->ip4src; + *fields |= DPAA2_ETH_DIST_IPSRC; } if (uip_mask->ip4dst) { off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST); *(__be32 *)(key + off) = uip_value->ip4dst; *(__be32 *)(mask + off) = uip_mask->ip4dst; + *fields |= DPAA2_ETH_DIST_IPDST; } if (uip_mask->proto) { off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO); *(u8 *)(key + off) = uip_value->proto; *(u8 *)(mask + off) = uip_mask->proto; + *fields |= DPAA2_ETH_DIST_IPPROTO; } if (uip_mask->l4_4_bytes) { @@ -324,23 +331,26 @@ static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value, off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); *(__be16 *)(key + off) = htons(tmp_value >> 16); *(__be16 *)(mask + off) = htons(tmp_mask >> 16); + *fields |= DPAA2_ETH_DIST_L4SRC; off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST); *(__be16 *)(key + off) = htons(tmp_value & 0xFFFF); *(__be16 *)(mask + off) = htons(tmp_mask & 0xFFFF); + *fields |= DPAA2_ETH_DIST_L4DST; } /* Only apply the rule for IPv4 frames */ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE); *(__be16 *)(key + off) = htons(ETH_P_IP); *(__be16 *)(mask + off) = htons(0xFFFF); + *fields |= DPAA2_ETH_DIST_ETHTYPE; return 0; } static int prep_l4_rule(struct ethtool_tcpip4_spec *l4_value, struct ethtool_tcpip4_spec *l4_mask, - void *key, void *mask, u8 l4_proto) + void *key, void *mask, u8 l4_proto, u64 *fields) { int off; @@ -351,41 +361,47 @@ static int prep_l4_rule(struct ethtool_tcpip4_spec *l4_value, off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC); *(__be32 *)(key + off) = l4_value->ip4src; *(__be32 *)(mask + off) = l4_mask->ip4src; + *fields |= DPAA2_ETH_DIST_IPSRC; } if (l4_mask->ip4dst) { off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST); *(__be32 *)(key + off) = l4_value->ip4dst; *(__be32 *)(mask + off) = l4_mask->ip4dst; + *fields |= DPAA2_ETH_DIST_IPDST; } if (l4_mask->psrc) { off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); *(__be16 *)(key + off) = l4_value->psrc; *(__be16 *)(mask + off) = l4_mask->psrc; + *fields |= DPAA2_ETH_DIST_L4SRC; } if (l4_mask->pdst) { off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST); *(__be16 *)(key + off) = l4_value->pdst; *(__be16 *)(mask + off) = l4_mask->pdst; + *fields |= DPAA2_ETH_DIST_L4DST; } /* Only apply the rule for IPv4 frames with the specified L4 proto */ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE); *(__be16 *)(key + off) = htons(ETH_P_IP); *(__be16 *)(mask + off) = htons(0xFFFF); + *fields |= DPAA2_ETH_DIST_ETHTYPE; off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO); *(u8 *)(key + off) = l4_proto; *(u8 *)(mask + off) = 0xFF; + *fields |= DPAA2_ETH_DIST_IPPROTO; return 0; } static int prep_ext_rule(struct ethtool_flow_ext *ext_value, struct ethtool_flow_ext *ext_mask, - void *key, void *mask) + void *key, void *mask, u64 *fields) { int off; @@ -396,6 +412,7 @@ static int prep_ext_rule(struct ethtool_flow_ext *ext_value, off = dpaa2_eth_cls_fld_off(NET_PROT_VLAN, NH_FLD_VLAN_TCI); *(__be16 *)(key + off) = ext_value->vlan_tci; *(__be16 *)(mask + off) = ext_mask->vlan_tci; + *fields |= DPAA2_ETH_DIST_VLAN; } return 0; @@ -403,7 +420,7 @@ static int prep_ext_rule(struct ethtool_flow_ext *ext_value, static int prep_mac_ext_rule(struct ethtool_flow_ext *ext_value, struct ethtool_flow_ext *ext_mask, - void *key, void *mask) + void *key, void *mask, u64 *fields) { int off; @@ -411,36 +428,38 @@ static int prep_mac_ext_rule(struct ethtool_flow_ext *ext_value, off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA); ether_addr_copy(key + off, ext_value->h_dest); ether_addr_copy(mask + off, ext_mask->h_dest); + *fields |= DPAA2_ETH_DIST_ETHDST; } return 0; } -static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask) +static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask, + u64 *fields) { int err; switch (fs->flow_type & 0xFF) { case ETHER_FLOW: err = prep_eth_rule(&fs->h_u.ether_spec, &fs->m_u.ether_spec, - key, mask); + key, mask, fields); break; case IP_USER_FLOW: err = prep_uip_rule(&fs->h_u.usr_ip4_spec, - &fs->m_u.usr_ip4_spec, key, mask); + &fs->m_u.usr_ip4_spec, key, mask, fields); break; case TCP_V4_FLOW: err = prep_l4_rule(&fs->h_u.tcp_ip4_spec, &fs->m_u.tcp_ip4_spec, - key, mask, IPPROTO_TCP); + key, mask, IPPROTO_TCP, fields); break; case UDP_V4_FLOW: err = prep_l4_rule(&fs->h_u.udp_ip4_spec, &fs->m_u.udp_ip4_spec, - key, mask, IPPROTO_UDP); + key, mask, IPPROTO_UDP, fields); break; case SCTP_V4_FLOW: err = prep_l4_rule(&fs->h_u.sctp_ip4_spec, &fs->m_u.sctp_ip4_spec, key, mask, - IPPROTO_SCTP); + IPPROTO_SCTP, fields); break; default: return -EOPNOTSUPP; @@ -450,13 +469,14 @@ static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask) return err; if (fs->flow_type & FLOW_EXT) { - err = prep_ext_rule(&fs->h_ext, &fs->m_ext, key, mask); + err = prep_ext_rule(&fs->h_ext, &fs->m_ext, key, mask, fields); if (err) return err; } if (fs->flow_type & FLOW_MAC_EXT) { - err = prep_mac_ext_rule(&fs->h_ext, &fs->m_ext, key, mask); + err = prep_mac_ext_rule(&fs->h_ext, &fs->m_ext, key, mask, + fields); if (err) return err; } @@ -473,6 +493,7 @@ static int do_cls_rule(struct net_device *net_dev, struct dpni_rule_cfg rule_cfg = { 0 }; struct dpni_fs_action_cfg fs_act = { 0 }; dma_addr_t key_iova; + u64 fields = 0; void *key_buf; int err; @@ -480,7 +501,7 @@ static int do_cls_rule(struct net_device *net_dev, fs->ring_cookie >= dpaa2_eth_queue_count(priv)) return -EINVAL; - rule_cfg.key_size = dpaa2_eth_cls_key_size(); + rule_cfg.key_size = dpaa2_eth_cls_key_size(DPAA2_ETH_DIST_ALL); /* allocate twice the key size, for the actual key and for mask */ key_buf = kzalloc(rule_cfg.key_size * 2, GFP_KERNEL); @@ -488,10 +509,36 @@ static int do_cls_rule(struct net_device *net_dev, return -ENOMEM; /* Fill the key and mask memory areas */ - err = prep_cls_rule(fs, key_buf, key_buf + rule_cfg.key_size); + err = prep_cls_rule(fs, key_buf, key_buf + rule_cfg.key_size, &fields); if (err) goto free_mem; + if (!dpaa2_eth_fs_mask_enabled(priv)) { + /* Masking allows us to configure a maximal key during init and + * use it for all flow steering rules. Without it, we include + * in the key only the fields actually used, so we need to + * extract the others from the final key buffer. + * + * Program the FS key if needed, or return error if previously + * set key can't be used for the current rule. User needs to + * delete existing rules in this case to allow for the new one. + */ + if (!priv->rx_cls_fields) { + err = dpaa2_eth_set_cls(net_dev, fields); + if (err) + goto free_mem; + + priv->rx_cls_fields = fields; + } else if (priv->rx_cls_fields != fields) { + netdev_err(net_dev, "No support for multiple FS keys, need to delete existing rules\n"); + err = -EOPNOTSUPP; + goto free_mem; + } + + dpaa2_eth_cls_trim_rule(key_buf, fields); + rule_cfg.key_size = dpaa2_eth_cls_key_size(fields); + } + key_iova = dma_map_single(dev, key_buf, rule_cfg.key_size * 2, DMA_TO_DEVICE); if (dma_mapping_error(dev, key_iova)) { @@ -500,7 +547,8 @@ static int do_cls_rule(struct net_device *net_dev, } rule_cfg.key_iova = key_iova; - rule_cfg.mask_iova = key_iova + rule_cfg.key_size; + if (dpaa2_eth_fs_mask_enabled(priv)) + rule_cfg.mask_iova = key_iova + rule_cfg.key_size; if (add) { if (fs->ring_cookie == RX_CLS_FLOW_DISC) @@ -522,6 +570,17 @@ free_mem: return err; } +static int num_rules(struct dpaa2_eth_priv *priv) +{ + int i, rules = 0; + + for (i = 0; i < dpaa2_eth_fs_count(priv); i++) + if (priv->cls_rules[i].in_use) + rules++; + + return rules; +} + static int update_cls_rule(struct net_device *net_dev, struct ethtool_rx_flow_spec *new_fs, int location) @@ -545,6 +604,9 @@ static int update_cls_rule(struct net_device *net_dev, return err; rule->in_use = 0; + + if (!dpaa2_eth_fs_mask_enabled(priv) && !num_rules(priv)) + priv->rx_cls_fields = 0; } /* If no new entry to add, return here */ @@ -581,14 +643,14 @@ static int dpaa2_eth_get_rxnfc(struct net_device *net_dev, break; case ETHTOOL_GRXCLSRLCNT: rxnfc->rule_cnt = 0; - for (i = 0; i < max_rules; i++) - if (priv->cls_rules[i].in_use) - rxnfc->rule_cnt++; + rxnfc->rule_cnt = num_rules(priv); rxnfc->data = max_rules; break; case ETHTOOL_GRXCLSRULE: if (rxnfc->fs.location >= max_rules) return -EINVAL; + rxnfc->fs.location = array_index_nospec(rxnfc->fs.location, + max_rules); if (!priv->cls_rules[rxnfc->fs.location].in_use) return -EINVAL; rxnfc->fs = priv->cls_rules[rxnfc->fs.location].fs; |