diff options
author | David S. Miller <davem@davemloft.net> | 2020-03-30 19:55:42 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-03-30 19:55:42 -0700 |
commit | 60d79ab33c4cc3c7b563cadea9e5bcf43e6e3951 (patch) | |
tree | 13af15cfc561b76a863e585f732fba756c29c236 /drivers | |
parent | ed52f2c608c9451fa2bad298b2ab927416105d65 (diff) | |
parent | 8b3abe304c5f1057b7bac70fd5576dfa67e3e2b3 (diff) | |
download | lwn-60d79ab33c4cc3c7b563cadea9e5bcf43e6e3951.tar.gz lwn-60d79ab33c4cc3c7b563cadea9e5bcf43e6e3951.zip |
Merge branch 'net-dsa-b53-and-bcm_sf2-updates-for-7278'
Florian Fainelli says:
====================
net: dsa: b53 & bcm_sf2 updates for 7278
This patch series contains some updates to the b53 and bcm_sf2 drivers
specifically for the 7278 Ethernet switch.
The first patch is technically a bug fix so it should ideally be
backported to -stable, provided that Dan also agress with my resolution
on this.
Patches #2 through #4 are minor changes to the core b53 driver to
restore VLAN configuration upon system resumption as well as deny
specific bridge/VLAN operations on port 7 with the 7278 which is special
and does not support VLANs.
Patches #5 through #9 add support for matching VLAN TCI keys/masks to
the CFP code.
Changes in v2:
- fixed some code comments and arrange some code for easier reading
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/dsa/b53/b53_common.c | 29 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 10 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_cfp.c | 139 |
3 files changed, 136 insertions, 42 deletions
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 39ae4ed87d1d..68e2381694b9 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -681,7 +681,9 @@ int b53_configure_vlan(struct dsa_switch *ds) { struct b53_device *dev = ds->priv; struct b53_vlan vl = { 0 }; + struct b53_vlan *v; int i, def_vid; + u16 vid; def_vid = b53_default_pvid(dev); @@ -699,6 +701,19 @@ int b53_configure_vlan(struct dsa_switch *ds) b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(i), def_vid); + /* Upon initial call we have not set-up any VLANs, but upon + * system resume, we need to restore all VLAN entries. + */ + for (vid = def_vid; vid < dev->num_vlans; vid++) { + v = &dev->vlans[vid]; + + if (!v->members) + continue; + + b53_set_vlan_entry(dev, vid, v); + b53_fast_age_vlan(dev, vid); + } + return 0; } EXPORT_SYMBOL(b53_configure_vlan); @@ -1340,6 +1355,14 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port, if ((is5325(dev) || is5365(dev)) && vlan->vid_begin == 0) return -EOPNOTSUPP; + /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of + * receiving VLAN tagged frames at all, we can still allow the port to + * be configured for egress untagged. + */ + if (dev->chip_id == BCM7278_DEVICE_ID && port == 7 && + !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) + return -EINVAL; + if (vlan->vid_end > dev->num_vlans) return -ERANGE; @@ -1705,6 +1728,12 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) u16 pvlan, reg; unsigned int i; + /* On 7278, port 7 which connects to the ASP should only receive + * traffic from matching CFP rules. + */ + if (dev->chip_id == BCM7278_DEVICE_ID && port == 7) + return -EINVAL; + /* Make this port leave the all VLANs join since we will have proper * VLAN entries from now on */ diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 368ead87e07a..affa5c6e135c 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -178,9 +178,17 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, core_writel(priv, reg, CORE_DIS_LEARN); /* Enable Broadcom tags for that port if requested */ - if (priv->brcm_tag_mask & BIT(port)) + if (priv->brcm_tag_mask & BIT(port)) { b53_brcm_hdr_setup(ds, port); + /* Disable learning on ASP port */ + if (port == 7) { + reg = core_readl(priv, CORE_DIS_LEARN); + reg |= BIT(port); + core_writel(priv, reg, CORE_DIS_LEARN); + } + } + /* Configure Traffic Class to QoS mapping, allow each priority to map * to a different queue number */ diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 1962c8330daa..f707edc641cf 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -13,6 +13,8 @@ #include <net/dsa.h> #include <linux/bitmap.h> #include <net/flow_offload.h> +#include <net/switchdev.h> +#include <uapi/linux/if_bridge.h> #include "bcm_sf2.h" #include "bcm_sf2_regs.h" @@ -261,16 +263,27 @@ static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv, static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv, struct flow_dissector_key_ipv4_addrs *addrs, struct flow_dissector_key_ports *ports, - unsigned int slice_num, + const __be16 vlan_tci, + unsigned int slice_num, u8 num_udf, bool mask) { u32 reg, offset; + /* UDF_Valid[7:0] [31:24] + * S-Tag [23:8] + * C-Tag [7:0] + */ + reg = udf_lower_bits(num_udf) << 24 | be16_to_cpu(vlan_tci) >> 8; + if (mask) + core_writel(priv, reg, CORE_CFP_MASK_PORT(5)); + else + core_writel(priv, reg, CORE_CFP_DATA_PORT(5)); + /* C-Tag [31:24] * UDF_n_A8 [23:8] * UDF_n_A7 [7:0] */ - reg = 0; + reg = (u32)(be16_to_cpu(vlan_tci) & 0xff) << 24; if (mask) offset = CORE_CFP_MASK_PORT(4); else @@ -336,6 +349,7 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, struct ethtool_rx_flow_spec *fs) { struct ethtool_rx_flow_spec_input input = {}; + __be16 vlan_tci = 0 , vlan_m_tci = 0xffff; const struct cfp_udf_layout *layout; unsigned int slice_num, rule_index; struct ethtool_rx_flow_rule *flow; @@ -360,6 +374,12 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1); + /* Extract VLAN TCI */ + if (fs->flow_type & FLOW_EXT) { + vlan_tci = fs->h_ext.vlan_tci; + vlan_m_tci = fs->m_ext.vlan_tci; + } + /* Locate the first rule available */ if (fs->location == RX_CLS_LOC_ANY) rule_index = find_first_zero_bit(priv->cfp.used, @@ -421,18 +441,11 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, core_writel(priv, layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf), CORE_CFP_MASK_PORT(6)); - /* UDF_Valid[7:0] [31:24] - * S-Tag [23:8] - * C-Tag [7:0] - */ - core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); - - /* Mask all but valid UDFs */ - core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); - /* Program the match and the mask */ - bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, slice_num, false); - bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, SLICE_NUM_MASK, true); + bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, vlan_tci, + slice_num, num_udf, false); + bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, vlan_m_tci, + SLICE_NUM_MASK, num_udf, true); /* Insert into TCAM now */ bcm_sf2_cfp_rule_addr_set(priv, rule_index); @@ -468,17 +481,29 @@ out_err_flow_rule: static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv, const __be32 *ip6_addr, const __be16 port, - unsigned int slice_num, + const __be16 vlan_tci, + unsigned int slice_num, u32 udf_bits, bool mask) { u32 reg, tmp, val, offset; + /* UDF_Valid[7:0] [31:24] + * S-Tag [23:8] + * C-Tag [7:0] + */ + reg = udf_bits << 24 | be16_to_cpu(vlan_tci) >> 8; + if (mask) + core_writel(priv, reg, CORE_CFP_MASK_PORT(5)); + else + core_writel(priv, reg, CORE_CFP_DATA_PORT(5)); + /* C-Tag [31:24] * UDF_n_B8 [23:8] (port) * UDF_n_B7 (upper) [7:0] (addr[15:8]) */ reg = be32_to_cpu(ip6_addr[3]); val = (u32)be16_to_cpu(port) << 8 | ((reg >> 8) & 0xff); + val |= (u32)(be16_to_cpu(vlan_tci) & 0xff) << 24; if (mask) offset = CORE_CFP_MASK_PORT(4); else @@ -587,6 +612,11 @@ static int bcm_sf2_cfp_rule_cmp(struct bcm_sf2_priv *priv, int port, ret = memcmp(&rule->fs.h_u, &fs->h_u, fs_size); ret |= memcmp(&rule->fs.m_u, &fs->m_u, fs_size); + /* Compare VLAN TCI values as well */ + if (rule->fs.flow_type & FLOW_EXT) { + ret |= rule->fs.h_ext.vlan_tci != fs->h_ext.vlan_tci; + ret |= rule->fs.m_ext.vlan_tci != fs->m_ext.vlan_tci; + } if (ret == 0) break; } @@ -600,6 +630,7 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, struct ethtool_rx_flow_spec *fs) { struct ethtool_rx_flow_spec_input input = {}; + __be16 vlan_tci = 0, vlan_m_tci = 0xffff; unsigned int slice_num, rule_index[2]; const struct cfp_udf_layout *layout; struct ethtool_rx_flow_rule *flow; @@ -623,6 +654,12 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1); + /* Extract VLAN TCI */ + if (fs->flow_type & FLOW_EXT) { + vlan_tci = fs->h_ext.vlan_tci; + vlan_m_tci = fs->m_ext.vlan_tci; + } + layout = &udf_tcpip6_layout; slice_num = bcm_sf2_get_slice_number(layout, 0); if (slice_num == UDF_NUM_SLICES) @@ -704,20 +741,13 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, reg = layout->udfs[slice_num].mask_value | udf_upper_bits(num_udf); core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); - /* UDF_Valid[7:0] [31:24] - * S-Tag [23:8] - * C-Tag [7:0] - */ - core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_DATA_PORT(5)); - - /* Mask all but valid UDFs */ - core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5)); - /* Slice the IPv6 source address and port */ bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->src.in6_u.u6_addr32, - ports.key->src, slice_num, false); + ports.key->src, vlan_tci, slice_num, + udf_lower_bits(num_udf), false); bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->src.in6_u.u6_addr32, - ports.mask->src, SLICE_NUM_MASK, true); + ports.mask->src, vlan_m_tci, SLICE_NUM_MASK, + udf_lower_bits(num_udf), true); /* Insert into TCAM now because we need to insert a second rule */ bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]); @@ -768,16 +798,12 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, udf_lower_bits(num_udf) << 8; core_writel(priv, reg, CORE_CFP_MASK_PORT(6)); - /* Don't care */ - core_writel(priv, 0, CORE_CFP_DATA_PORT(5)); - - /* Mask all */ - core_writel(priv, 0, CORE_CFP_MASK_PORT(5)); - bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->dst.in6_u.u6_addr32, - ports.key->dst, slice_num, false); + ports.key->dst, 0, slice_num, + 0, false); bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->dst.in6_u.u6_addr32, - ports.key->dst, SLICE_NUM_MASK, true); + ports.key->dst, 0, SLICE_NUM_MASK, + 0, true); /* Insert into TCAM now */ bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]); @@ -823,7 +849,9 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port, struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; __u64 ring_cookie = fs->ring_cookie; + struct switchdev_obj_port_vlan vlan; unsigned int queue_num, port_num; + u16 vid; int ret; /* This rule is a Wake-on-LAN filter and we must specifically @@ -843,6 +871,34 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port, dsa_is_cpu_port(ds, port_num)) || port_num >= priv->hw_params.num_ports) return -EINVAL; + + /* If the rule is matching a particular VLAN, make sure that we honor + * the matching and have it tagged or untagged on the destination port, + * we do this on egress with a VLAN entry. The egress tagging attribute + * is expected to be provided in h_ext.data[1] bit 0. A 1 means untagged, + * a 0 means tagged. + */ + if (fs->flow_type & FLOW_EXT) { + /* We cannot support matching multiple VLAN IDs yet */ + if ((be16_to_cpu(fs->m_ext.vlan_tci) & VLAN_VID_MASK) != + VLAN_VID_MASK) + return -EINVAL; + + vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK; + vlan.vid_begin = vid; + vlan.vid_end = vid; + if (cpu_to_be32(fs->h_ext.data[1]) & 1) + vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED; + else + vlan.flags = 0; + + ret = ds->ops->port_vlan_prepare(ds, port_num, &vlan); + if (ret) + return ret; + + ds->ops->port_vlan_add(ds, port_num, &vlan); + } + /* * We have a small oddity where Port 6 just does not have a * valid bit here (so we substract by one). @@ -878,21 +934,22 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, int ret = -EINVAL; /* Check for unsupported extensions */ - if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype || - fs->m_ext.data[1])) + if (fs->flow_type & FLOW_MAC_EXT) return -EINVAL; - if (fs->location != RX_CLS_LOC_ANY && fs->location >= CFP_NUM_RULES) + if (fs->location != RX_CLS_LOC_ANY && + fs->location > bcm_sf2_cfp_rule_size(priv)) return -EINVAL; + if ((fs->flow_type & FLOW_EXT) && + !(ds->ops->port_vlan_prepare || ds->ops->port_vlan_add || + ds->ops->port_vlan_del)) + return -EOPNOTSUPP; + if (fs->location != RX_CLS_LOC_ANY && test_bit(fs->location, priv->cfp.used)) return -EBUSY; - if (fs->location != RX_CLS_LOC_ANY && - fs->location > bcm_sf2_cfp_rule_size(priv)) - return -EINVAL; - ret = bcm_sf2_cfp_rule_cmp(priv, port, fs); if (ret == 0) return -EEXIST; @@ -973,7 +1030,7 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, u32 loc) struct cfp_rule *rule; int ret; - if (loc >= CFP_NUM_RULES) + if (loc > bcm_sf2_cfp_rule_size(priv)) return -EINVAL; /* Refuse deleting unused rules, and those that are not unique since |