Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: mv88e6xxx: Improve multichip isolation of standalone ports

Given that standalone ports are now configured to bypass the ATU and
forward all frames towards the upstream port, extend the ATU bypass to
multichip systems.

Load VID 0 (standalone) into the VTU with the policy bit set. Since
VID 4095 (bridged) is already loaded, we now know that all VIDs in use
are always available in all VTUs. Therefore, we can safely enable
802.1Q on DSA ports.

Setting the DSA ports' VTU policy to TRAP means that all incoming
frames on VID 0 will be classified as MGMT - as a result, the ATU is
bypassed on all subsequent switches.

With this isolation in place, we are able to support configurations
that are simultaneously very quirky and very useful. Quirky because it
involves looping cables between local switchports like in this
example:

CPU
| .------.
.---0---. | .----0----.
| sw0 | | | sw1 |
'-1-2-3-' | '-1-2-3-4-'
$ @ '---' $ @ % %

We have three physically looped pairs ($, @, and %).

This is very useful because it allows us to run the kernel's
kselftests for the bridge on mv88e6xxx hardware.

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tobias Waldekranz and committed by
David S. Miller
d352b20f 585d42bb

+51 -19
+45 -19
drivers/net/dsa/mv88e6xxx/chip.c
··· 1630 1630 1631 1631 int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap) 1632 1632 { 1633 - int i, err; 1634 - u16 fid; 1635 - 1636 1633 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); 1637 1634 1638 - /* Set every FID bit used by the (un)bridged ports */ 1639 - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1640 - err = mv88e6xxx_port_get_fid(chip, i, &fid); 1641 - if (err) 1642 - return err; 1643 - 1644 - set_bit(fid, fid_bitmap); 1645 - } 1646 - 1647 - /* Set every FID bit used by the VLAN entries */ 1635 + /* Every FID has an associated VID, so walking the VTU 1636 + * will discover the full set of FIDs in use. 1637 + */ 1648 1638 return mv88e6xxx_vtu_walk(chip, mv88e6xxx_fid_map_vlan, fid_bitmap); 1649 1639 } 1650 1640 ··· 1647 1657 if (err) 1648 1658 return err; 1649 1659 1650 - /* The reset value 0x000 is used to indicate that multiple address 1651 - * databases are not needed. Return the next positive available. 1652 - */ 1653 - *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); 1660 + *fid = find_first_zero_bit(fid_bitmap, MV88E6XXX_N_FID); 1654 1661 if (unlikely(*fid >= mv88e6xxx_num_databases(chip))) 1655 1662 return -ENOSPC; 1656 1663 ··· 2138 2151 2139 2152 if (!vlan.valid) { 2140 2153 memset(&vlan, 0, sizeof(vlan)); 2154 + 2155 + if (vid == MV88E6XXX_VID_STANDALONE) 2156 + vlan.policy = true; 2141 2157 2142 2158 err = mv88e6xxx_atu_new(chip, &vlan.fid); 2143 2159 if (err) ··· 2939 2949 if (err) 2940 2950 return err; 2941 2951 2952 + /* On chips that support it, set all downstream DSA ports' 2953 + * VLAN policy to TRAP. In combination with loading 2954 + * MV88E6XXX_VID_STANDALONE as a policy entry in the VTU, this 2955 + * provides a better isolation barrier between standalone 2956 + * ports, as the ATU is bypassed on any intermediate switches 2957 + * between the incoming port and the CPU. 2958 + */ 2959 + if (dsa_is_downstream_port(ds, port) && 2960 + chip->info->ops->port_set_policy) { 2961 + err = chip->info->ops->port_set_policy(chip, port, 2962 + MV88E6XXX_POLICY_MAPPING_VTU, 2963 + MV88E6XXX_POLICY_ACTION_TRAP); 2964 + if (err) 2965 + return err; 2966 + } 2967 + 2968 + /* User ports start out in standalone mode and 802.1Q is 2969 + * therefore disabled. On DSA ports, all valid VIDs are always 2970 + * loaded in the VTU - therefore, enable 802.1Q in order to take 2971 + * advantage of VLAN policy on chips that supports it. 2972 + */ 2942 2973 err = mv88e6xxx_port_set_8021q_mode(chip, port, 2943 - MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED); 2974 + dsa_is_user_port(ds, port) ? 2975 + MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED : 2976 + MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE); 2977 + if (err) 2978 + return err; 2979 + 2980 + /* Bind MV88E6XXX_VID_STANDALONE to MV88E6XXX_FID_STANDALONE by 2981 + * virtue of the fact that mv88e6xxx_atu_new() will pick it as 2982 + * the first free FID. This will be used as the private PVID for 2983 + * unbridged ports. Shared (DSA and CPU) ports must also be 2984 + * members of this VID, in order to trap all frames assigned to 2985 + * it to the CPU. 2986 + */ 2987 + err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_STANDALONE, 2988 + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, 2989 + false); 2944 2990 if (err) 2945 2991 return err; 2946 2992 ··· 2989 2963 * relying on their port default FID. 2990 2964 */ 2991 2965 err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED, 2992 - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED, 2966 + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, 2993 2967 false); 2994 2968 if (err) 2995 2969 return err;
+6
include/net/dsa.h
··· 591 591 return port == dsa_upstream_port(ds, port); 592 592 } 593 593 594 + /* Return true if this is a DSA port leading away from the CPU */ 595 + static inline bool dsa_is_downstream_port(struct dsa_switch *ds, int port) 596 + { 597 + return dsa_is_dsa_port(ds, port) && !dsa_is_upstream_port(ds, port); 598 + } 599 + 594 600 /* Return the local port used to reach the CPU port */ 595 601 static inline unsigned int dsa_switch_upstream_port(struct dsa_switch *ds) 596 602 {